Spring MVC-04循序渐进之基于注解的控制器(上)

简介: Spring MVC-04循序渐进之基于注解的控制器

概述


Spring MVC-03循序渐进之Spring MVC中我们介绍了传统的开发方式,其弊端Controller接口实现类只能处理一个单一动作,本篇博文我们来介绍下基于注解的控制器。


Spring MVC注解类型


基于注解的控制器优点如下:


一个控制器可以处理多个请求动作,而一个实现了Controller接口的控制器只能处理一个动作

基于注解的控制器的请求映射不需要存储在配置文件中,使用RequestMapping注解类型,可以对一个方法进行请求处理。


Controller和RequestMapping注解类型是SpringMVC API最重要的两个注释类型,当然了我们这里也会介绍其他一些注解类型


Controller注解类型


org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器。


下面是一个带有@Controller注解的例子

import org.springframework.stereotype.Controller;
@Controller
public class ArtisanController {
}

Spring使用注解扫描的方式来找到应用中所有基于注解的控制器类,为了确保Spring能扫描到你的控制器,需要完成两件事情

  1. 在Spring MVC配置文件中声明spring-context及指定schema
  2. 然后配置component-scan扫描路径
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
     <context:component-scan base-package="com.artisan.springmvc.controller"/>
   <!--......省略其他配置项--->
</beans>


确保所有控制器类都在基本包下,并且不要指定一个太宽泛的基本包,这样会使Spring扫描了无关的包。


RequestMapping注解类型


现在我们需要在控制器类内部为每一个动作开发相应的处理方法,要让Spring知道哪一种方法来处理它的动作,需要使用org.springframework.web.bind.annotation.RequestMapping注解类型映射的URL与方法。


RequestMapping注释类型的作用:映射一个请求和一种方法,可以使用@RequestMapping注释一种方法或者一个类


一个采用了@RequestMapping注解的方法将成为一个请求处理方法,并由调度程序在接收到对应的URL请求时调用


下面是一个@RequestMapping注解方法的控制器类

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ArtisanController {
    @RequestMapping(value="/doSomething")
    public String doSomething() {
        // do some bussiness logic here
        return "custoomer";
    }
}


使用RequestMapping注解的value属性将URI映射到方法。在上面的例子中,我们将doSomething映射到doSomething方法,这样,就可以使用如下URL访问doSomething方法

http://domain/context/doSomething


value属性

由于value属性是RequestMapping注解的默认属性,如果只有唯一的属性,则可以省略属性名称。换句话说

    @RequestMapping(value="/doSomething")
    @RequestMapping("/doSomething")


效果是等同的,但是如果超过一个属性时,就必须要输入value属性名称。

请求映射的值可以是一个空字符,此时该方法被映射到如下网址 http://domain/context


其他属性


RequestMapping除了具有value属性,还有其他属性。比如method属性用来指示改方法仅处理哪些HTTP方法. 当method为多个值时,后面写为数组{method1, method2}

例如只有在HTTP POST或者PUT方法时才能访问到下面的方法

@Controller
public class ArtisanController {
    @RequestMapping(value="/doSomething",method={RequestMethod.POST,RequestMethod.PUT})
    public String doSomething() {
        // do some bussiness logic here
        return "customer";
    }
}


如果method属性仅有一个HTTP方法值,则不需要花括号

@RequestMapping(value="/doSomething",method=RequestMethod.POST)


如果没有指定method属性值,则请求处理方法可以处理任意HTTP方法。


此外RequestMapping注释类型也可以用来注释一个控制器类

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/artisan")
public class ArtisanController {
}


这种情况下,所有的方法都将映射为相对于类级别的请求,比如下面的deleteArtisan方法

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/artisan")
public class ArtisanController {
    @RequestMapping(value="/delete",method={RequestMethod.POST,RequestMethod.PUT})
    public String deleteArtisan() {
        // do delete opertaion
        return "artisanList";
    }
}


由于控制器类的映射使用了“/artisan” ,而deleteArtisan方法映射为/delete,则如下的URL将映射到该方法上

htpp://domain/context/artisan/delete


编写请求处理方法


每个请求处理方法可以有多个不同类型的参数,以及一个多钟类型的返回结果。


比如在请求处理方法中需要访问HttpSession对象,则可以添加HttpSession作为参数,Spring会将对象正确传递给方法

    @RequestMapping("/uri")
    public String method(HttpSession session){
        // do something
        session.setAttribute(key, value);
        return ...;
    }


或者,如果需要访问客户端环境和HttpServletRequest,则可以在方法签名上包括这样的参数

@RequestMapping("/uri")
    public String method(HttpServletRequest request,Locale locale){
        // access HttpServletRequest or Locale here 
        return ...;
    }



每个请求处理方法可以有多个不同类型的参数,下面时可以在请求处理方法中出现的参数类型:


javax.servlet.ServletRequest 或 javax.servlet.HttpServletRequest

javax.servlet.ServletResponse

或 javax.servlet.httpHttpServletResponse

javax.servlet.http.HttpSession

org.springframework.web.context.request.WebRequest

或 org.springframework.web.context.request.nativeWebRequest

java.util.Locale

java.io.InputStream 或 java.io.Reader

java.io.OutputStream 或 java.io.Writer

java.security.Principal

HttpEntity<?>

java.util.Map 或 org.springframework.ui.Model

org.springframework.ui.ModelMap

org.springframework.web.servlet.mvc.support.RedirectAttributes

org.springframework.validation.Errors

orgspringframework.validation.BindingResult

命令或表单对象

org.springframework.web.util.UriCompontsBuilder

org.springframework.web.util.UriComponentsBuilder

带@PathVariable, @MatrixVariable注释的对象

@RequestParam, @RequestHeader, @RequestBody 或 @RequestPart


特别重要的是org.springframework.ui.Model类型不是一个Servlet API类型,而是一个包涵Map的Spring MVC类型。每次调用请求处理方法时,Spring MVC都创建Model对象将其Map注入到各种对象。


请求处理方法可以返回如下类型的对象:


ModelAndView

Model

Map包含模型的属性

View

代表逻辑视图名的String

void

提供对Servlet的访问。以相应HTTP头部和内容HttpEntity或ResponseEntity对象

Callable

DeferredResult

其他任意类型,Spring将其视作输出给View的对象模型


应用基于注解的控制器


该处的示例是对前面几篇博文的重写,区别于前几篇博文中的示例在于

  • 控制器类中增加了@Controller注解
  • Spring配置文件增加了部分元素,下面详解


目录结构


20180120204514700.jpg


maven工程结构如上,在这里,只有一个控制器类,而不是之前示例中的两个。

同时增加了一个名为index.html的静态文件,以便Spring MVC Servlet的URL模式设置为”/”时,依然可以访问静态资源


配置文件


两个配置文件,第一个为部署描述符(web.xml文件)中注册Spring MVC的DispatcherServlet ,第二个Spring MVC的配置文件 springmvc-config.xml


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/springmvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>    
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>



在部署描述符中servlet-mapping元素中url-pattern设置为 / ,而不是之前实例中的action。 实际上映射动作不必一定是要用某种URL扩展。


当然,当URL设置为/,意味着所有的请求( 包括那些静态资源)都被映射到DispatcherServlet, 为了正确的处理静态资源,就必须要在Spring MVC的配置文件中添加一些 resouce元素。


springmvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd     
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.artisan.springmvc.controller"/>
    <mvc:annotation-driven/>
    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/*.html" location="/"/>
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>


Spring MVC配置文件中最重要的是context:component-scan元素,这是要告诉SpringMVC扫描目标包中的类。


接下来是一个mvc:annotation-driven和两个mvc:resources。


mvc:annotation-driven元素做的事情内包括注册用于支持基于注解的控制器的请求处理方法的bean对象


mvc:resources元素用于指示Spring MVC 哪些静态资源需要单独处理,即不通过Dispatcher Servlet


在这个示例中,第一个resources元素确保/css目录下的所有文件可见

第二个允许显示所有的.html文件


注意:如果没有annotation-driven,resources元素会阻止任意控制器被调用,如果不需要使用resources,则不需要annotation-driven元素


Controller类


使用Controller注释类型的一个优点在于:一个控制器类可以包含多个请求处理方法

package com.artisan.springmvc.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.artisan.springmvc.domain.Product;
import com.artisan.springmvc.form.ProductForm;
@Controller
public class ProductController {
    private static final Log logger = LogFactory.getLog(ProductController.class);
    @RequestMapping(value="/product_input")
    public String inputProduct() {
        logger.info("inputProduct called");
        return "ProductForm";
    }
    @RequestMapping(value="/product_save")
    public String saveProduct(ProductForm productForm, Model model) {
        logger.info("saveProduct called");
        // no need to create and instantiate a ProductForm
        // create Product
        Product product = new Product();
        product.setName(productForm.getName());
        product.setDescription(productForm.getDescription());
        try {
            product.setPrice(Float.parseFloat(
                    productForm.getPrice()));
        } catch (NumberFormatException e) {
        }
        // add product
        model.addAttribute("product", product);
        return "ProductDetails";
    }
}


其中,ProductController#saveProduct()方法的第二个入参

public String saveProduct(ProductForm productForm, Model model)


无论是否会使用,SpringMVC都会在每一个请求处理方法被调用时创建一个Model实例,用于增加需要显示在视图中的属性,例如通过调用model.addAttribute("product", product);来添加Product实例。这样Product实例就可以被添加到HttpServletRequestt中那样访问了。


View

ProductForm.jsp

<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global">
<form action="product_save" method="post">
    <fieldset>
        <legend>Add a product</legend>
        <p>
            <label for="name">Product Name: </label>
            <input type="text" id="name" name="name" 
                tabindex="1">
        </p>
        <p>
            <label for="description">Description: </label>
            <input type="text" id="description" 
                name="description" tabindex="2">
        </p>
        <p>
            <label for="price">Price: </label>
            <input type="text" id="price" name="price" 
                tabindex="3">
        </p>
        <p id="buttons">
            <input id="reset" type="reset" tabindex="4">
            <input id="submit" type="submit" tabindex="5" 
                value="Add Product">
        </p>
    </fieldset>
</form>
</div>
</body>
</html>


ProductDetails.jsp

<!DOCTYPE HTML>
<html>
<head>
<title>Save Product</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global">
    <h4>The product has been saved.</h4>
    <p>
        <h5>Details:</h5>
        Product Name: ${product.name}<br/>
        Description: ${product.description}<br/>
        Price: $${product.price}
    </p>
</div>
</body>
</html>



测试应用


http://localhost:8080/chapter04a/product_input

输入对应的表格


20180120211133731.jpg


提交后

http://localhost:8080/chapter04a/product_save

调用saveProduct方法


20180120211245415.jpg


相关文章
|
8天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
132 73
|
3天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
35 21
|
8天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
8天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
2月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
137 2
|
2月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
58 2
|
8月前
|
设计模式 前端开发 JavaScript
Spring MVC(一)【什么是Spring MVC】
Spring MVC(一)【什么是Spring MVC】
|
7月前
|
设计模式 前端开发 Java
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
114 1
|
7月前
|
前端开发 Java 应用服务中间件
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
|
8月前
|
前端开发 Java 关系型数据库
基于ssm框架旅游网旅游社交平台前后台管理系统(spring+springmvc+mybatis+maven+tomcat+html)
基于ssm框架旅游网旅游社交平台前后台管理系统(spring+springmvc+mybatis+maven+tomcat+html)