Spring MVC(处理流程+XML配置+注解配置)
参考/摘录书籍:Spring+Spring MVC+MyBatis整合开发 著○陈学明
参考图片:来自网络/自己提供
说明:本人相关博客仅供学习参考!
一、MVC回顾
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分;
M:Model,模型,指工程中的JavaBean,作用是处理数据
V:View,视图,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制器,指工程中的Servlet,作用是接收请求和响应浏览器
JavaBean分为两类:
一类称为实体类Bean
:专门存储业务数据的,如 Student、User 等。这种Bean也叫数据模型
。
一类称为业务处理 Bean
:指Dao或Service类,专门用于处理数据访问和业务逻辑。这种Bean也叫业务处理模型
。
MVC的工作流程: 用户通过视图发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器。
二、Spring MVC
2.1 介绍
Spring MVC 是一个优秀的 Web 层框架!
Spring 使用 Servlet技术提供了一个中央控制器DispatcherServlet
,用于同一接收客户端请求,DispatcherServlet根据配置的映射规则将请求分派到不同业务处理的控制器中,各业务控制器调用响应的业务逻辑模型处理后返回模型数据,中央处理器将模型数据交由视图解析器获得最终的视图进行请求的响应。
2.2 Spring MVC框架请求处理流程
- 中央控制器(DispatcherServlet)同一接收前端发来的请求,并分发前端请求,也被称为
前端控制器
。 - DispatcherServlet 解析接收到的请求地址或参数,调用处理器映射器(HandlerMapping)处理。
- 处理器映射器根据URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)并一并返回给 DispatcherServlet。
- DispatcherServlet 通过处理器适配器(HandlerAdapter)调用处理器。
- 处理器适配器调用执行处理器(Handler),处理器对应的也就是具体的Controller类,也被称为
后端控制器
。 - Controller 执行完成返回模型视图对象(ModelAndView)。模型视图对象包含了
逻辑数据
和视图的名称
。 - HandlerAdapter 将Controller执行的结果(ModelAndView)返回给DispatcherServlet。
- DispatcherServlet 将 ModelAndView 传给ViewReslover 视图解析器,通过视图名称获取对应的视图对象。
- ViewResolver 解析后返回具体视图对象(View)。(可以是html文件,jsp文件)
- DispatcherServlet 对视图对象进行渲染(
即将数据模型填充到视图中
)后返回。
2.3 第一个Spring MVC程序
2.3.1 创建Maven工程,引入依赖
<!-- 打包方式 -->
<packaging>war</packaging>
<!-- springmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- 日志依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- Servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
2.3.2 创建后端控制器(HelloController.java)
/**
* 后端控制器Handler(即Controller)
* 【Handler返回ModelAndView对象】
*
* 该Handler(即Controller)类的缺点
* 1)每个Controller类都需要实现Controller接口
* 2)每个类(Controller)只能处理一个用户请求(或者只能处理一个业务逻辑)
* 3)每个类(Controller)都要在配置文件里,进行配置
* 【可以使用注解的方式解决】
*/
public class HelloController implements Controller {
/**
* handleRequest()有点类似于Servlet中的service(),用来处理请求
* @param httpServletRequest
* @param httpServletResponse
* @return
* @throws Exception
*/
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//初始化模型视图
ModelAndView mv = new ModelAndView();
//设置视图名称
mv.setViewName("show");
User user = new User(1,"许褚",22);
//往模型视图中添加数据【等价于request.setAttribute()】
mv.addObject("user",user);
return mv;
}
}
ModelAndView 可以设置页面和添加返回的模型对象。在ModelAndView中添加的模型对象可以在相应的view中获取。
2.3.3 springmvc.xml 配置文件
在Spring配置文件(springmvc.xml)中配置控制器Bean
、请求处理映射
、视图处理的映射
。
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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">
<!-- 控制器 -->
<bean id="helloController" class="com.java.controller.HelloController" />
<!-- 请求处理映射 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings"> <!--路径映射-->
<props>
<prop key="/hello1">helloController</prop> <!--跟控制器Bean的id一致-->
<prop key="/hello2">helloController</prop>
</props>
</property>
</bean>
<!-- 视图解析器
prefix+viewName+suffix,假设我们视图是hello.jsp,这个页面要放在/WEB-INF/views/hello.jsp
WEB-INF是受保护的目录,它里面的资源只能通过服务器内部转发方式来访问,不能直接访问
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" /><!-- 视图前缀 -->
<property name="suffix" value=".jsp" /><!-- 视图后缀 -->
</bean>
</beans>
通过设置
mappings属性
配置不同的URL请求地址和不同的处理器的映射。(多个请求地址可以对应一个处理器)
2.3.4 视图文件(hello.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>第一个Springmvc案例</title>
</head>
<body>
Hello:<br/>
用户ID: ${user.id} <br/> <!--获取模型数据的user对象的id属性值-->
用户名: ${user.name} <br/> <!--获取模型数据的user对象的name属性值-->
年龄: ${user.age} <br/> <!--获取模型数据的user对象的age属性值-->
</body>
</html>
使用
${user.name}
这样的变量获取模型中的数据,视图在渲染时会根据模型数据替换成实际的值。
2.3.5 web.xml 中配置 DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- DispatcherServlet中央处理器配置
1、Web容器/服务器读取web.xml配置进行Servlet的注册和初始化。
2、DispatcherServlet是Spring MVC提供的用于前端请求分发的Servlet,所有的前端请求首先由其进行处理。
3、通过设置contextConfigLocation参数指定Spring MVC的配置文件。
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- init-param指定配置的servlet的参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 让tomcat启动时就去加载DispatcherServlet并初始化Spring容器 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 配置核心控制器能拦截的请求路径,
/*:拦截所有请求(包括jsp,png,css等)(避免使用),/:拦截除jsp以外的请求
-->
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- DispatcherServlet最好在容器启动时一并启动,这样Spring配置的Bean可以在容器启动时一并实例化。(
<load-on-startup>1</load-on-startup>
)contextConfigLocation参数
用于指定Spring MVC的配置 XML 文件。
2.3.6 运行效果图
三、Spring MVC 配置细节
3.1 DispatcherServlet——中央处理器
<!-- DispatcherServlet中央处理器配置
1、Web容器/服务器读取web.xml配置进行Servlet的注册和初始化。
2、DispatcherServlet是Spring MVC提供的用于前端请求分发的Servlet,所有的前端请求首先由其进行处理。
3、通过设置contextConfigLocation参数指定Spring MVC的配置文件。
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- init-param指定配置的servlet的参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 让tomcat启动时就去加载DispatcherServlet并初始化Spring容器 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 配置核心控制器能拦截的请求路径,
/*:拦截所有请求(包括jsp,png,css等)(避免使用),/:拦截除jsp以外的请求
-->
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
servlet-name
用于指定 Servlet 的名字,这个名字会在 servlet-mapping 时使用。此外,默认情况下,DispatcherServlet 也会使用这个名字去找对应的Spring配置文件。
3.1.1 Spring 配置文件(自定义配置文件名称)
用户发送请求到web容器,并被DispatchServlet拦截之后进入springmvc容器,springmvc该怎么处理,这就需要springmvc的配置文件。
springmvc默认读取/WEB-INF/{servlet-name}-servlet.xml
这个配置文件,因为我们在web.xml中的servlet-name配置的是springmvc,所以在WEB-INF目录下创建springmvc-servlet.xml
文件。
注意:也可以通过contextConfigLocation 参数
指定Spring配置文件的路径和文件名。(如上面代码)
- 如果只指定文件名,则默认从WEB-INF路径查找配置文件。
- 配置文件如果放在类文件的路径下,可以使用
classpath:
的方式,如classpath:springmvc.xml
,包之间使用斜线 “/
” 进行分隔。多个配置文件之间使用逗号 “,
” 分隔。- 【注意】
classpath:
等同于/WEB-INF/classes
。
3.1.2 Servlet 加载配置(load-on-startup)
<load-on-startup>1</load-on-startup>
用于标记是否在容器启动时就实例化和加载这个Servlet。其配置是一个整数。
- 当 load-on-startup 的值 <0 或者没有配置时,表示该Servlet被请求时容器才去加载它。
- 当 load-on-startup 的值 =0 或 >0 时,表示容器启动时就会加载它。值的大小表示Servlet被载入的顺序,值越小,优先级越高,越容易被容器加载。
3.1.3 Servlet 路径拦截匹配(< servlet-mapping >)
Servlet 容器在接收到浏览器发起的一个URL请求后,会把应用上下文去除后,以剩余的字符串来映射响应的 Servlet。例如,URL请求地址是 http://localhost/ssmt/index.html,其应用上下文是ssmt(也是项目名),容器会将 http://localhost/ssmt 去掉,用 /index.html 拿来做Servlet的映射匹配。
【注意1】:当一个URL与多个Servlet的匹配规则可以匹配时,按照精确路径 ---> 最长路径 ---> 扩展名
的优先级匹配Servlet,当匹配一个Servlet后就不用管剩下的Servlet了。
<servlet-mapping>
中的每个<url-pattern>
元素代表一个匹配规则,从Servlet 2.5 开始,同一个Servlet 可以使用多个url-pattern 的规则。(注意:路径匹配和扩展名匹配不能同时设置。)
【注意2】:因为DispatcherServlet负责前端所有请求的拦截和分发,一般是配置一个 < url-pattern > 用于拦截所有的请求进行转发。常见的路径规则如下:
- 传统方式(
扩展名形式
):*.do
或*.action
【优点】:不会导致静态文件(js、css、png等)被拦截。 - 流行方式(
REST风格
):/
【缺点】:这种配置会覆盖Tomcat默认的拦截请求,也就是会拦截(*.js、*.png等)静态文件。(可以通过web.xml或Spring的配置放行这些资源)
【扩展】
REST 风格的 URL
类似 /user/add,在基于注解映射的开发方式中,在 Controller类上使用类似“/user”的路径,在该Controller类的不同处理方法使用类似“/add”的映射。- 在DispatcherServlet中应避免使用这种映射规则–>
/*
。
3.1.4 静态资源放行(注解方式)
在springmvc.xml配置文件中配置下面两个标签:
<!-- 配置默认的Servlet处理器,它对静态资源放行【应配合注解驱动标签使用,否则无法访问处理器】 -->
<mvc:default-servlet-handler/>
<!-- 注解驱动(会自动注册映射处理器和处理器适配器和参数转换注解) -->
<mvc:annotation-driven></mvc:annotation-driven>
3.2 HandlerMapping——处理器映射器
Spring 内置了3中类型的处理器映射器:BeanNameUrlHandlerMapping(组件名URL处理器映射器【默认】)
、SimpleUrlHandlerMapping(简单的URL处理器映射器)
、RequestMappingHandlerMapping(请求映射处理器映射)
。通过查看源码可知HandlerMapping接口下有个实现BeanNameUrlHandlerMapping(在DispatcherServlet.properties配置文件中已经默认配置了),所以只需要在 springmvc.xml 配置文件中配置待name属性的控制器即可。
3.2.1 BeanNameUrlHandlerMapping(类层级映射)
BeanNameUrlHandlerMapping是默认的映射器,Spring 默认会创建一个BeanNameUrlHandlerMapping的实例,所以不需要进行 BeanNameUrlHandlerMapping 的Bean配置也可以进行映射。
【注意下面代码中的注解】
<!-- 如果进行了SimpleURLHandlerMapping处理器映射,则默认的BeanNameURLHandlerMapping处理器映射器失效。
【解决方法】显式声明BeanNameURLHandlerMapping处理器映射器即可。-->
<bean id="controllerC" name="/contC" class="com.ccbx.controller.ControllerC" />
【注意】Bean的
name 属性
需要是一个路径地址,也就是要以 “/
” 开头,如果不加斜线的话就无法访问。
3.2.2 SimpleUrlHandlerMapping(类层级映射)
简单的URL处理器映射器配置URL和Controller处理器的对应,通过配置SimpleUrlHandlerMapping类的 < bean > 来实现,使用属性 mappings 进行请求的URL路径和处理器的显式映射。
<!-- 控制器 -->
<bean id="helloController" class="com.ccbx.controller.HelloController" />
<!-- 请求处理映射 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings"><!-- 路径映射 -->
<props>
<!--bean标签id=helloController的处理器控制器映射路径“/hello1”和“/hello2”-->
<prop key="/hello1">helloController</prop>
<prop key="/hello2">helloController</prop>
<prop key="/*.do">controllerA</prop>
<prop key="/*">controllerB</prop>
</props>
</property>
</bean>
【扩展】多个映射的URL可以对应同一个处理器控制器!
3.2.3 RequestMappingHandlerMapping(方法层级映射)
实际开发中最常使用的就是该方法层级的映射。(也就是在 @Controller注解类的方法上使用 @RequestMapping、@GetMapping、@PostMapping等注解进行请求的映射。)
以上注解映射处理器
和处理器适配器
在使用<mvc:annotation-driven />
配置元素后会自动初始化和注册。再配合<context:component-scan>
开启组件注解或依赖自动装载等注解功能。
案例如下
后端控制器
@Controller //控制器注解
@RequestMapping("/annotationController") //控制器类上的请求映射注解
public class ControllerD{
@RequestMapping("/method01") //方法层级的请求注解
public ModelAndView method01() { //映射方法
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("messageView");
modelAndView.addObject("msg","注解配置的控制器:controller-D");
return modelAndView;
}
}
springmvc.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd ">
<!-- 扫描类路径以查找将自动注册为的带注释的组件 -->
<context:component-scan base-package="com.ccbx.controller"/>
<!-- 注解驱动【开启请求注解映射注解(注解类型的处理器和适配器)和参数转换注解】 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 视图解析器
prefix+viewName+suffix,假设我们视图是hello.jsp,这个页面要放在/WEB-INF/views/hello.jsp
WEB-INF是受保护的目录,它里面的资源只能通过服务器内部转发方式来访问,不能直接访问
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" /><!-- 视图前缀 -->
<property name="suffix" value=".jsp" /><!-- 视图后缀 -->
</bean>
</beans>
运行效果
除了使用 annotation-driven 配置元素外,也可以分别定义映射处理器和处理器适配器的 < bean > 配置达到相同的效果。(不推荐 )
<!-- 映射处理器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<!-- 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
3.3 HandlerAdapter——处理器适配器
适配器模式(Adapter)是设计模式的一种,就是把一个类的接口编程客户端所期待的另一种接口。适配器模式主要是为了结局接口不兼容的问题。
3.3.1 SimpleControllerHandlerAdapter(简单的控制器处理适配器)
SimpleControllerHandlerAdapter 是默认的处理器适配器,用于适配实现 org.springframework.web.servlet.mvc.Controller
接口的处理器,最终调用处理器类的 handlerRequest(request,response) 方法
。【该适配器不会处理请求参数的转换】
<bean class="org.springframework.web.servlet.mvc.Controller" />
3.3.2 HttpRequestHandlerAdapter(HTTP请求处理器适配器)
适配的处理器的类是继承自 HttpRequesthandler 接口
,主要处理普通的 HTTP 请求的处理器。也可以自定义 HttpRequestHandler 接口实现类,实现自定义的请求处理。
【注意】HttpRequestHandler和Controller接口斗殴 handlerRequest() 处理方法,但是Controller接口中的方法返回值为 ModelAndView对象,Controller接口的handlerRequest()方法有@Nullable注解修饰,也就是可以返回空的返回值;而HttpRequesthandler中的方法无返回值(void)。
3.3.3 SimpleServletHandlerAdapter(Servlet处理器适配器)
该处理器适配器支持的是继承自 Servlet 接口
的类,也就是适配 Servlet,Servlet 使用 service(request,response) 进行请求的处理。源码如下:
public class SimpleServletHandlerAdapter implements HandlerAdapter {
@Override
@Nullable //该注解表示:方法返回值可以为空(null)
//适配器的处理方法
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//调用 Servlet 的service方法来处理请求。
((Servlet) handler).service(request, response);
return null;
}
}
3.3.4 RequestMappingHandlerAdapter(请求映射处理器适配器)
【最常用】该适配器支持的是 HandlerMethod 类型的处理器,也就是适配 @RequestMapping 等请求映射注解的方法。该适配器有如下几个功能:
- 获取当前 Spring 容器的 Bean 类中标注了
@ModelAttribute 注解
但是没有标注 @RequestMapping 注解的方法,在真正调用具体的处理器方法之前会将这些方法依次调用。 - 获取当前 Spring 容器的 Bean 类中标注了
@InitBinder 注解
的方法,调用这些方法对一些用户自定义的参数进行转换并且绑定。 - 根据当前 Handler 的方法参数标注的注解类型(如@RequestParam,@ModelAttribute等),获取其对应的
参数处理器(ArgumentResolve)
,并将请求对象中的参数转换为当前方法中对应注解的类型。 - 通过反射调用具体的处理器方法。
- 通过 ReturnValueHandler 对返回值进行适配。
处理器映射器、处理器适配器和处理器之间的对应关系
处理器映射器 | 处理器适配器 | 处理器 |
---|---|---|
BeanNameUrlHandlerMapping SimpleUrlHandlerMapping | SimpleControllerHandlerAdapter | 实现Controller的接口类 |
BeanNameUrlHandlerMapping SimpleUrlHandlerMapping | HttpRequestHandlerAdapter | 实现HttpRequestHandler的接口类 |
BeanNameUrlHandlerMapping SimpleUrlHandlerMapping | SimpleServletHandlerAdapter | Servlet |
RequestMappingHandlerMapping | RequestMappingHandlerAdapter | HandlerMethod使用@Controller注解类中,@RequestMapping等注解的方法 |
3.4 视图与视图解析器
Controller 中的请求处理方法执行完成后会返回一个 ModelAndView 类型的对象(这个对象包含一个逻辑视图名和模型数据),接着执行 DispatcherServlet 的 processDispatcherResult 方法,结合视图和模型进行请求的响应。响应步骤如下:
- 使用容器中注册的视图解析器,根据视图的逻辑名称,实例化一个 View 子类对象,并返回给 DispatcherServlet。
- DispatcherServlet 调用视图的 render()方法,结合模型数据对视图进行渲染后,把视图的内容通过响应流,响应到客户端。
【注意】对于返回String、View或ModelMap等类型的映射方法,String MVC也会在内部将他们装配成一个 ModelAndView 对象。
ViewResolver接口下有实现类InternalResourceViewResolver 内部资源视图解析器
,它是最常用的视图解析器,用于JSP视图的解析。(也可以自定义视图及解析器)
3.4.1 视图
视图(View)的作用是渲染模型数据,将模型数据里的数据以某种形式呈现给客户端,视图的类型可以是JSP,也可以是JSON或Excel等其他类型。以JSP视图来说,可以理解为将JSP文件中的 ${}占位符
替换成模型的实际值
,通过 HTTPServletResponse输出流的方式响应到客户端 。
Spring 提供了同一的视图接口 View,改接口定义的常见方法:
方法 | 描述 |
---|---|
getContentType() | 得到内容的类型,也就是响应到客户端的类型(MIME类型) |
render() | 结合模型数据渲染视图 【注】该方法会实际调用 HTTPServletResponse 的数据流输出方法 out.write(),或者使用RequestDispatcher进行请求的派发 |
Spring 默认内置了继承 View 接口的不同类型视图:
视图名称 | 描述 |
---|---|
InternalResourceView | 内部资源视图,对应JSP文件 |
JstlView | 继承自InternalResourceView,是包含JSP标准标签的JSP页面 |
FreeMarkerView | FreeMarker 模板引擎视图 |
VelocityView | Velocity 模板引擎视图 |
MappingJackson2JsonView | JSON 数据格式的视图 |
MarshallingView | XML 内容类型的视图 |
RedirectView | 重定向视图 |
3.4.2 视图解析器
视图解析器(ViewResolver)是把一个逻辑上的视图名称解析为一个真正的视图。Spring提供了视图解析器的同一几口 ViewResolver,该接口只有一个方法resolveViewName(String viewName,Locale locale)
,即根据不同的视图名称找到具体的视图实现类。
Spring 也实现了不同的视图处理器,常见的视图处理器如下:
视图解析器 | 描述 |
---|---|
AbstractCachingViewResolver | 带缓存功能的ViewResolver接口的基础实现抽象类,将解析过的视图进行缓存,下次再次绩溪的时候就会在缓存中直接寻找该视图。 |
InternalResourceViewResolver | 根据视图名字返回 InternalResourceView 视图的视图。 【注】继承自AbstractCachingViewResolver。 |
FreeMarkerViewResolver | 返回 FreeMarkerView |
BeanNameViewResolver | 根据路基视图名称去匹配定义好的视图 Bean 对象。 【注】该视图解析器不会进行视图缓存; |
【扩展】在一个Spring MVC项目中可以同时使用多个视图计息期组成的一个
视图解析器链
。(在springmvc.xml中配置位置越靠前越先被遍历)
四、Spring MVC 注解配置
一些相关的注解整理:
spring三个配置标签的区别:
4.0 Spring MVC常用的注解汇总表
类别 | 注解 | 应用目标 | 功能 |
---|---|---|---|
组件 | @Controller | 类 | 后端控制器 该注解中的value属性可以设置注入容器时组件的名称,如: @Controller(value=“aaa”) |
↑ | @RestController | 类 | 后端控制器 该注解是 @ResponseBody 和@Controller 的组合注解。 |
↑ | @Service | 类 | 服务类组件 |
请求映射 | @RequestMapping | 类或方法 | 通用请求映射 |
↑ | @GetMapping | 类或方法 | GET请求映射,获取数据 |
↑ | @PostMapping | 类或方法 | POST请求映射,创建数据 |
↑ | @PutMapping | 类或方法 | PUT请求映射,修改数据 |
↑ | @PatchMapping | 类或方法 | PATCH请求映射,修改部分数据 |
↑ | @DeleteMapping | 类或方法 | DELETE请求映射,删除数据 |
参数匹配 | @RequestParam | 方法参数 | 请求参数,例如: /springmvcTest/method06?id=1001&name=张良 |
↑ | @PathVariable | 方法参数 | 路径变量,例如: /springmvcTest/method07/1001/张良/18/男 |
↑ | @MatrixVariable | 方法参数 | 矩阵变量,例如: /matrixParam/addStu;id=1001,name=张良 |
↑ | @ModelAttribute | 方法或方法参数 | 模型属性 |
参数绑定 | @RequestBody | 方法参数 | 指示方法参数的注释应该绑定到 Web 请求的正文。 也就是将请求体中的数据赋值为参数对象的属性上。 |
↑ | @ResponseBody | 类或方法 | 指示方法返回值的注释应该绑定到 Web 响应正文。 |
4.1 组件与依赖注解
与Spring核心容器一直,配置<context:componebt-scan>
就可以自动扫描和注册对应的包下的组件和依赖注入的注解。相比 @Component 的通用组件注解,Spring MVC项目中可以使用对应不同层级更为精确的组件注解。(如上)
4.2 请求映射与参数注解
请求映射注解可以使用在类和方法上。特定的请求注解只接受特定类型的 HTTP 请求方法,而 @RequestMapping 可以通用。
4.2.1 @RequestMapping——请求映射注解
@RequestMapping 可以使用在@Controller 注解类的类和方法上。应用在类中作为请求路径的前缀,结合方法中的路径组成完整的请求路径。
@Controller //控制器组件注解
@RequestMapping("/annotationController") //应用在@Controller修饰的类上的请求映射注解
public class ControllerD{
@RequestMapping("/method01") //应用在@Controller注解类中的方法上的请求映射注解
public ModelAndView method01() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("messageView");
modelAndView.addObject("msg","注解配置的控制器:controller-D");
return modelAndView;
}
}
【注意以下几种情况】
(1)、不同请求类型的映射
@RequestMapping 后面可以直接指定路径,也可以使用 value 属性来指定路径。method 属性用于指定匹配不同类型的请求。(有多个属性的时候value不能省略不写)
@RequestMapping(value="/method01",method=RequestMethod.GET)
//等同于
@GetMapping(value="/method01")
//多种请求参数映射可写成
@RequestMapping(value="/method01",method={RequestMethod.GET,RequestMethod.POST})
(2)、请求内容的类型配置
@RequestMapping请求映射注解的属性:
属性名 | 解释 |
---|---|
vlaue | path 的别名,用来映射路径 |
method | 映射的HTTP请求方法 |
consumes | 处理请求的提交内容类型(Content-Type),例如 application/json、text/html |
produces | 返回的内容类型 |
params | 匹配传递的参数 |
//还可以通过charset设置字符集
@RequestMapping(value = "/outText",method = RequestMethod.GET,
consumes = "text/html",produces = "text/html;charset=UTF-8")
@ResponseBody //返回html类型数据
public String method02(){
return "Spring MVC的控制器响应字符串到浏览器上";
}
(3)、请求参数限制
@RequestMapping(value="",params="")
params="userId":请求参数中必须带有userId
params="!userId":请求参数中不能包含userId
params="userId=1":请求参数中userId必须为1
params="userId!=1":请求参数中userId必须不为1,参数中可以不包含userId
params={"userId", "name"}:请求参数中必须有userId,name参数
4.2.2 @RequestParam——请求参数匹配注解
@RequestParam 使用在映射方法的形参中,默认情况下,请求参数与映射方法参数名需要同名,如果在请求参数或请求体中不存在同名的参数,则会报错。【默认@RequestParam注解的参数必须传值,否则抛异常;但可以通过设置required="false"取消这个限制;还可以通过value属性设置映射到的前端请求的参数名(此时形参名可以任意取)】
如果方法参数是一个对象类型的话,容器还会降请求中的参数自动装箱,也就是创建该类型的对象,并将前端传递过来的参数自动匹配到对象的属性中。【调用对象的setXxx方法进行属性赋值】【请求传递过来的参数名要和对象的属性名一致!!!】
<h1>学生注册</h1>
<form action="reciveAndBind/stuRegist" method="post">
学号:<input name="id" placeholder="请输入学号..."/><br>
姓名:<input name="sname" placeholder="请输入姓名..."/><br>
性别:<input type="radio" name="sex" value="男" checked/>男
<input type="radio" name="sex" value="女" />女 <br>
爱好:
<input type="checkbox" name="hobby" value="篮球">篮球
<input type="checkbox" name="hobby" value="游泳">游泳
<input type="checkbox" name="hobby" value="足球">足球
<input type="checkbox" name="hobby" value="唱歌">唱歌 <br>
是否在籍:
<input type="radio" name="isOn" value="在籍" checked>在籍
<input type="radio" name="isOn" value="毕业">毕业
<input type="radio" name="isOn" value="辍学">辍学 <br>
学费:<input type="text" name="tuition" placeholder="请输入学费..."> <br/>
<input type="submit" value="注册">
</form>
public class Student {
private String id;
private String sname;
private String sex;
private String[] hobby;
private Integer isOn;
private Double tuition;
//属性的get和set方法
}
//如出现POST请求中的中文乱码问题请参考 --> 4.5.3 常见问题
@PostMapping("stuRegist") //请求中的参数会自动封装到Student对象的属性上
public String addStu(Student stu, Model model) {
model.addAttribute("stu", stu);
return "stu2";
}
【注意】
- 映射方法参数的 @RequestParam 也可以不用,容器会根据参数名称自动进行匹配。
4.2.3 @PathVariable——路径变量注解
【@PathVariable 常用在 REST 风格请求中】该注解使用在映射方法参数中,用来绑定映射注解中的URL占位符的值,并赋给该参数,占位符使用{}
格式。【路径占位符匹配】
@PathVariable 和 @RequestParam 都是用来传递参数的。
@RequestParam常用在标准URL请求中,@PathVariable 常用在 REST 风格请求中。
4.2.4 @MatrixVariable——矩阵变量注解
【通常配合 @PathVariable 路径变量匹配注解使用】
首先在Spring MVC中开启 @MatrixVariable 注解:
<!--默认装配方案,开启@MatrixVariable注解功能,默认在SpringMVC中是不启动的-->
<mvc:annotation-driven enable-matrix-variables="true"></mvc:annotation-driven>
@MatrixVariable相关属性:
属性名 | 说明 |
---|---|
name | 指定参数的键的名字 |
pathVar | 指定路径变量的名字 |
(1)简单案例:
(2)复杂案例:
【说明】
- 路径和参数以分号
;
分隔,参数的键和值以等号=
分隔;
例如:请求路径 “/matrixParam/addStu;id=1001
”,这里addStu是路径,id是键值对的参数。
4.2.5 标准URL 映射和 REST风格映射
几种映射:
- 标准URL映射 (
@RequestParam 注解
接收参数) - REST风格映射:含占位符:{xxx} (
@PathVariable 注解
接收URL中占位符参数) - 限定请求方法的映射:get post put delete
- 限定请求参数的映射:@RequestMapping(value=" ",params=“请求参数”)
(1)、标准URL映射
//省略形式
@RequestMapping("xxx")
//完整形式
@RequestMapping(value="xxx") 或 @RequestMapping(path="xxx")
【请求映射的规则】
- 类上的@RequestMapping的value+方法上的@RequestMapping的value。
- 类上的@RequestMapping可省略,这时请求路径就是方法上的@RequestMapping的value 。【如果多个Controller中都有同名的方法,则此时要在类上面的@RequestMapping()映射一个路径,以便于精准访问】
(2)、REST 风格映射(含占位符)
//controller类中的映射为
@RequestMapping(value="/user/{userId}/{name}")
//此时请求的URL为
请求URL:http://localhost:8080/user/1001/zhangsan
通过@PathVariable("userId") Long id, @PathVariable("name") String name
获取对应的参数。
【注意】
- @PathVariable(“key”)中的key必须和对应的占位符中的参数名一致,而方法形参的参数名可任意取。
- 如果@PathVariable的value值和形参名称相同,此时value属性可以省略。
/**
* Rest风格的映射【含占位符】
* @return
*/
@RequestMapping("restStyleMappingTest/{bookId}/{bookName}")
public ModelAndView test2(@PathVariable("bookId") Integer bid,@PathVariable("bookName") String bname){
ModelAndView mav = new ModelAndView();
mav.setViewName("restM");
mav.addObject("msg","Rest风格映射<br/>占位符中的值为:"+bid+","+bname);
return mav;
}
/**
* Rest风格的映射【含占位符】【简写】
* 要求:@PathVariable的value值和形参名称相同,此时value属性可以省略
* @return
*/
@RequestMapping("restStyleMappingTest2/{bookId}/{bookName}")
public ModelAndView test3(@PathVariable Integer bookId,@PathVariable String bookName){
ModelAndView mav = new ModelAndView();
mav.setViewName("restM");
mav.addObject("msg","Rest风格映射<br/>占位符中的值为:"+bookId+","+bookName);
return mav;
}
4.3 @ModelAttribute——模型属性注解
@ModelAttribute 注解将数据添加到模型中,用于视图展示。该注解一般用在控制器中,控制器可以包含任意数量的 @ModelAttribute 注解方法,这些方法会在统一控制器中的@RequestMapping注解方法之前被调用。
@ModelAttribute 注解方法可以通过 @ControllerAdvice
在控制器之间共享。
4.3.1 使用在控制器普通方法中
这里的普通方法是没有使用 @RequestMapping 等请求映射注解标注的方法。@ModelAttribute 注解的普通方法会在每个映射请求方法之前执行,可以使用@RequestParam
获取请求参数,通过Model类型的参数设置模型的属性值。
@Controller
public class ModelAttributeController {
//将方法参数或方法返回值绑定到命名模型属性的注释,暴露给 Web 视图
@ModelAttribute
public void method01(@RequestParam String name, Model model){
model.addAttribute("name",name);
}
@ModelAttribute //设置模型数据
public void method02(@RequestParam Integer id, Model model){
model.addAttribute("id",id); //设置模型数据中的属性
}
//当返回对象时,【默认】框架会将返回的对象的类名首字母小写作为键值加入模型对象中
//也可以使用该注解的 value或name属性指定属性名
@ModelAttribute(name = "user")
public User method04(@RequestParam String name,@RequestParam Integer id){
return new User(name,id); //返回的对象直接加入到模型视图中,可以不用加Model参数
}
@RequestMapping("/method03") //请求映射注解
public String method03(){
return "messageView"; //返回视图名
}
}
4.3.2 使用在映射注解的方法中
@ModelAttribute 和 @RequestMapping 可以同时注解同一个方法,如果该方法返回的是一个字符串类型,这个字符串代表的就不是一个视图的名字,而是模型的属性的值。
@RequestMapping("/method05")
@ModelAttribute("modAttr")
public String test05(){
return "此返回的字符串作为@ModelAttribute注解中 属性 modAttr的值!!";
}
【
说明
】该例子中,会把@ModelAttribute注解中的 modAttr 作为键,方法的返回值作为该键的值写入模型数据。返回的视图名称则通过转换映射的请求地址得来,也就是会去找与 /method05路径对应的同名的jsp文件method05.jsp。
4.3.3 使用在方法参数中
在方法的形参前使用 @ModelAttribute 注解,则会从隐含的模型对象中获取键是参数名的属性值,并将这个值赋给注解的参数。
@Controller
public class ModelAttributeController {
//当返回对象时,【默认】框架会将返回的对象的类名首字母小写作为键值加入模型对象中
//也可以使用该注解的 value或name属性指定属性名
@ModelAttribute(name = "user")
public User method04(@RequestParam String name,@RequestParam Integer id){
return new User(name,id); //返回的对象直接加入到模型视图中,可以不用加Model参数
}
//从隐含的模型对象中获取 "键为user" 的属性【method04方法已经封装了部分属性】,并将其值赋给方法形参user
@RequestMapping("method06")
public String test06(@ModelAttribute User user){
user.setAge(18);
return "messageView";
}
}
4.4 常见问题
4.4.1 方法返回值写入响应体(乱码问题)
【注意】:如果要让返回值直接写入到响应体(直接输出给浏览器),还需要加@ResponseBody
【否则会出现下面这样的情况】
正确写法:(但此时会出现中文乱码问题)
@Controller
@RequestMapping("/annotationController")
public class ControllerD{
@RequestMapping("/outText")
@ResponseBody //将方法的返回值绑定到Web响应正文上
public String method02(){
return "Spring MVC的控制器响应字符串到浏览器上";
}
}
解决办法1【不推荐】:给@RequestMapping注解添加 produces 属性值
;【缺点】只能解决单个映射方法的返回值编码问题,不能全局解决响应乱码问题。
@RequestMapping(value="/outText",produces = "text/html;charset=UTF-8")
解决办法2【推荐】:在注解驱动标签<mvc:annotation-driven>
中配置消息转换器。
<!-- 配置默认的Servlet处理器,它对静态资源放行【应配合注解驱动使用,否则无法访问处理器】 -->
<mvc:default-servlet-handler/>
<!-- 注解驱动(会自动注册映射处理器和处理器适配器和参数转换注解) -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 配置消息转换器处理Web响应中的中文乱码问题 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<!-- 设置默认字符集 -->
<property name="defaultCharset" value="UTF-8" />
<!-- 设置此转换器支持的MediaType对象列表 -->
<!-- 该属性调用 setSupportedMediaTypes(List<MediaType> supportedMediaTypes) -->
<property name="supportedMediaTypes">
<!-- (在List集合中设置要转换的MIME类型即可) -->
<list>
<!-- html页面 -->
<value>text/html</value>
<!-- JSON格式的数据类型 -->
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
MIME
(Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。
4.4.2 @ResponseBody注解
使用GET请求,参数是在?后面追加,请求参数名默认和形参名一致。
//限定GET请求,获取传入的参数并写给页面
@RequestMapping(value = "reMethod",method = RequestMethod.GET)
@ResponseBody //将方法的返回值写入页面,而不是跳转到【返回值.jsp】页面。
public String test6(String name){
return "GET,请求中的name:"+name;
}
4.4.3 解决POST请求的中文乱码
解决:在web.xml中配置一个springmvc的字符编码过滤器。
<!-- 配置一个springmvc的字符编码过滤器【解决post请求中文乱码问题】
【springmvc.xml中配置的处理直接回显给浏览器时中文乱码的配置不能删】 -->
<filter>
<filter-name>charterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>charterEncoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
4.4.4 POJO对象绑定
SpringMVC会将请求参数名和POJO实体中的属性名(setXxx方法)进行自动匹配,如果名称一致,将把值填充到对象属性中,并且支持级联(例如:user.dept.id)。比如:name=”sex”->把sex的值填充到对象的sex属性上,调用的是setSex()。【要求:请求参数名和Pojo对象的属性名要一致】
<form action="stu/add2" method="post">
学号:<input name="id" /><br>
姓名:<input name="sname" /><br>
年龄:<input name="age" /><br>
性别:<input type="radio" name="sex" value="男" checked/>男
<input type="radio" name="sex" value="女" />女
@PostMapping("add2")
public String add2(Student stu, Model model) {
model.addAttribute("stu", stu);
return "stu2";
}
4.4.5 集合的绑定
需求:将表单中多个学生的信息封装到List集合中
注意:不能使用List集合直接接收多个学生的记录。
解决方案:
1)把List集合封装到一个类中,作为该类的属性
public class StudentVo {
private List<Student> stuList;
public List<Student> getStuList() {
return stuList;
}
public void setStuList(List<Student> stuList) {
this.stuList = stuList;
}
}
2)页面上的表单中Input元素的name要写成集合中元素的属性名,这样就能自动匹配上元素的属性,即把请求参数值自动封装到对象的属性上【根据集合中的索引来匹配属性名,并调用属性名的setXxx方法】
例如:学号:<input name="stuList[${i}].id" />
3)Controller中用Vo类封装
@PostMapping("add3")
@ResponseStatus(value = HttpStatus.OK)
public void add3(StudentVo studentVo, Model modell){
List<Student> stuList = studentVo.getStuList();
System.out.println("集合长度:"+ stuList.size());
for (Student student : stuList) {
System.out.println(student);
}
}
【注意】如果希望Controller中方法不跳转页面,则方法上要有
@ResponseStatus 注解
,否则还是会跳转到prefix+方法url+suffix页面。
五、Spring MVC 代码配置
5.1 Java代码代替 web.xml 配置文件
参考:Spring——Java代码配置 web.xml 和 spring.xml