SpringMVC技术总结
1、SpringMVC执行流程
-
浏览器提交请求到中央调度器
-
中央调度器直接将请求转给处理器映射器。
-
处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
-
中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
-
处理器适配器调用执行处理器。
-
处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
-
处理器适配器直接将结果返回给中央调度器。
-
中央调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象。
-
视图解析器将封装了的视图对象返回给中央调度器
-
中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
-
中央调度器响应浏览器。
2、依赖,需要添加servlet和spring-webmvc依赖
<dependency> <groupId>javax.servletgroupId> <artifactId>javax.servlet-apiartifactId> <version>3.1.0version> <scope>providedscope> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webmvcartifactId> <version>5.2.5.RELEASEversion> dependency>
3、web.xml配置文件
(1) 注册中央调度器
<servlet> <servlet-name>dispatcherServletservlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class> <init-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:spring-mvc.xmlparam-value> </init-param> <load-on-startup>1load-on-startup> servlet> <servlet-mapping> <servlet-name>dispatcherServletservlet-name> <url-pattern>*.htmlurl-pattern> <url-pattern>*.jsonurl-pattern> servlet-mapping>
-
全限定性类名
该中央调度器为一个 Servlet,名称为 DispatcherServlet。 中央调度器的全限定性类名在导入的 Jar 文件 spring-webmvc-5.2.5.RELEASE.jar 的第一个包 org.springframework.web.servlet下可找到。
-
<load-on-startup/>
在<servlet/>中添加<load-on-startup/>的作用是,标记是否在Web服务器(这里是Tomcat)启动时会创建这个 Servlet 实例,即是否在 Web 服务器启动时调用执行该 Servlet 的 init()方
法,而不是在真正访问时才创建。它的值必须是一个整数。
-
当值大于等于 0 时,表示容器在启动时就加载并初始化这个 servlet,数值越小,该 Servlet的优先级就越高, 其被创建的也就越早;
-
当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
-
当值相同时,容器会自己选择创建顺序。
-
配置文件位置与名称
默认要从项目根下的 WEB-INF 目录下找名称为 "Servlet 名称-servlet.xml "的配置文件。这里的“Servlet 名称”指的是注册中央调度器<servlet-name/>标签中指定的 Servlet 的 name 值。本例配置文件名为dispatcherServlet-servlet.xml。我们通过<param-value>classpath:spring-mvc.xml</param-value>指定配置文件地址。
-
<url-pattern/>
(2) 配置字符集过滤器
<!--配置字符集过滤器--> <filter> <filter-name>characterEncodingFilter</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> <!--强制请求设置字符集--> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <!--强制相应设置字符集--> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4、SpringMVC配置文件详解
<!--配置自动扫描的包--> <context:component-scan base-package="com.atguigu.crowd.mvc"/> <!--配置SpringMVC注解驱动--> <mvc:annotation-driven/> <!--配置视图解析器,为controller层函数返回视图添加前后缀--> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <!--配置基于XML的异常映射--> <!--已使用注解方式--> <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!--配置异常的类型和具体视图页面的对应关系--> <property name="exceptionMappings"> <props> <!--key属性指定异常全类名,标签体中写对应的视图名(这个值要拼前后缀得到具体路径)--> <prop key="java.lang.Exception">system-error</prop> <prop key="com.atguigu.crowd.exception.AccessForbiddenException">admin-login</prop> </props> </property> </bean> <!--配置view-controller,直接把请求地址和视图名称关联起来,不必写handler方法了 @RequestMapping("/admmin/to/login/page.html") public String toLoginPage(){ return "admin-admin"; } --> <mvc:view-controller path="/admin/to/login/page.html" view-name="admin-login"/> <!--注册拦截器: 使用 SpringSecurity 后当前自定义的登录拦截器不再使用了--> <!--注册拦截器--> <mvc:interceptors> <mvc:interceptor> <!--mvc:mapping配置要拦截的资源--> <!-- /* 对应一层路径,比如:/aaa --> <!-- /** 对应多层路径,比如:/aaa/bbb 或 /aaa/bbb/ccc --> <mvc:mapping path="/**"/> <!-- mvc:exclude-mapping 配置不拦截的资源 --> <mvc:exclude-mapping path="/admin/to/login/page.html"/> <mvc:exclude-mapping path="/admin/do/login.html"/> <mvc:exclude-mapping path="/admin/do.logout.html"/> <bean class="com.atguigu.crowd.mvc.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
(1) 视图解析器
SpringMVC 框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器InternalResouceViewResolver 中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接。
(2)注解驱动
将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由来完成。
5、处理器简介
(1) @Controller:表示当前类为处理器
(2) @RequestMapping:表示当前方法为处理器方法。
-
属性value:对 value 属性所指定的 URI进行处理与响应。若有多个请求路径均可匹配该处理器方法的执行,value 属性中可以写成一个数组。
-
属性method: 可以设置为get或post提交。不设置则get和post都可
(3) 实现处理器的老方式:1.处理器类实现Controller接口;2.配置name对应请求路径,class对应处理请求的类
6、处理器方法的参数
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
-
HttpServletRequest
-
HttpServletResponse
-
HttpSession
-
请求中所携带的请求参数
-
逐个接收:保证请求参数名与该请求处理方法的参数名相同即可。
-
@RequestParam:指定请求 URL 所携带参数的名称。
-
对象参数: 将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。
-
@PathVariable:获取请求路径中的变量
(1) @RequestParam:
请求URL所携带的参数名称与处理方法中指定的参数名不相同时,在参数前添加一个注解@RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。
用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容。提交方式为get或post。(Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-orm-urlencoded类型)
RequestParam实质是将Request.getParameter() 中的Key-Value参数Map利用Spring的转化机制ConversionService配置,转化成参数接收对象或字段。
-
属性value:请求参数名
-
属性required:true表示请求中必须有此参数,false表示可以没有此参数
(2) @RequestBody:
可以接收json格式的数据,并将其转换成对应的数据类型(对象,map)。将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。
处理HttpEntity传递过来的数据,一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据(但是也可以处理)。application/json、application/xml等格式的数据,必须使用@RequestBody来处理。
GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。
这里涉及到使用@RequestBody接收不同的对象
1. 创建一个新的entity,将两个entity都进去。这是最简单的,但是不够“优雅”。
2. 用Map<String, Object>接受request body,自己反序列化到各个entity中。
3. 类似方法2,不过更为generic,实现自己的HandlerMethodArgumentResolver。参考https://sadique.io/blog/2016/01/30/using-custom-arguments-in-spring-mvc-controllers/
(3) @ModelAttribute:将参数绑定到Model对象
使用@ModelAttribute注解的实体类接收前端发来的数据格式需要为"x-www-form-urlencoded"。若是使用@ModelAttribute接收application/json格式,虽然不会报错,但是值并不会自动填入。
当前台界面使用GET或POST方式提交数据时,数据编码格式由请求头的ContentType指定。分为以下几种情况:
-
application/x-www-form-urlencoded,这种情况的数据@RequestParam、@ModelAttribute可以处理,@RequestBody也可以处理。
-
multipart/form-data,@RequestBody不能处理这种格式的数据。(form表单里面有文件上传时,必须要指定enctype属性值为multipart/form-data,以二进制流的形式传输文件。)
-
application/json、application/xml等格式的数据,必须使用@RequestBody来处理。
TODO:1.研究Content-Type: application/x-www-form-urlencoded等编码格式;2. 消息转换器 HttpMessageConverter
(4) RestFul风格
@PathVariable获取请求路径变量,也可以加载类头的@RequestMapping()参数里(RestFul风格)
可使用组合注解:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
@RequestMapping("/pets/{petId}")
public void findPet(PathVariable String petId, Model model) {
// implementation omitted
}
7、注解驱动
将Object数据转化为JSON数据,需要由消息转换器HttpMessageConverter完成。而转换器的开启,需要由来完成。
当 Spring 容器进行初始化过程中,在处创建注解驱动时,默认创建了七个 HttpMessageConverter 对象。
HttpMessageConverter 接口 : HttpMessageConverter是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息
HttpMessageConverter接口定义的方法: boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为clazz类型对象,指定支持MIME类型(text/html,applaiction/json 等) boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在 MediaType 中定义。 List getSupportMediaTypes():该转换器支持的媒体类型。 T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。 void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将 T 类型的对象写到响应流中,同时指定相应的媒体类型为 contentType
主要关注接口实现类MappingJackson2HttpMessageConverter:负责读取和写入json格式的数据。 利用Jackson的ObjectMapper读写json数据,操作Object 类型数据,可读取application/json,响应媒体类型为 application/json
使用场景:
-
Ajax请求时
-
解决静态资源访问的时候
-
异常处理使用注解方式的时候
9、处理器方法的返回值
-
ModelAndView:处理器方法处理完后,需要跳转到其它资源,又要传递数据, 选择返回ModelAndView 比较好
-
String:返回内部资源逻辑视图名,与视图解析器结合使用
-
无返回值void:
-
返回自定义类型对象Object:
-
这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
-
返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。
-
一般都是将数据转化为了 JSON 对象后传递给浏览器页面的。需要导入Jackson 的相关Jar包。
-
需要声明注解驱动
-
返回中文字符串,需要在@RequestMapping 的 produces 属性指定字符集。如:produces="text/plain;charset=utf-8"
注意:
-
Model、ModelMap(参数传入)和ModelAndView(自己创建)都可以用于返回数据
-
@Responsebody 表示该方法的返回结果直接写入 HTTP response body 中。一般在异步获取数据时使用,在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。比如异步获取json数据,加上 @Responsebody 后,会直接返回json数据。
总结:
-
普通请求
-
需要返回数据:ModelAndView、String+ModelMap
-
不需返回数据:String、ModelAndView(不传数据)
-
Ajax请求:加@ResponseBody注解,返回Object
9、静态资源的访问
(1) *.do方式
SpringMVC 的中央调度器 DispatcherServlet 的常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。图片会正常显示。
(2) / 方式
DispatcherServlet 向静态资源的获取请求(.css、 .js、 .jpg、 .png)当作是一个普通的 Controller 请求。中央调度器调用处理器映射器为其查找相应的处理器。找不到会报 404 错误。图片无法显示。
(3) 无法访问静态资源的解决方法
-
表示使用 DefaultServletHttpRequestHandler 处理器对象。
-
对进入 DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的Servlet 处理。
-
一般的服务器都有默认的 Servlet。而该处理器调用了 Tomcat 的 DefaultServlet 来处理静态资源的访问请求。
-
使用:在Spring3.0版本后, Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。需要在springmvc 配置文件中添加如下配置:
<mvc:resources location="/images/" mapping="/images/**" /> location:表示静态资源所在目录。 当然, 目录不要使用/WEB-INF/及其子目录。 mapping:表示对该资源的请求(以/images/开始的请求,如/image/beauty.jpg,/images/car.png等)。注意后面是两个星号**。
-
解决动态资源和静态资源冲突的问题,还需要在 springmvc 配置文件声明注解驱动
10、请求重定向和转发
-
对于请求转发的页面,可以是WEB-INF中页面;
-
重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问WEB-INF中资源的。
-
转发和重定向都不需要视图解析器
(1) 请求转发
-
处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:
-
处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径。
(2) 重定向
-
在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转
11、异常处理
SpringMVC提供了基于XML和基于注解两种异常映射机制。这两种异常映射不能够只使用一个,他们需要一起使用。因为有些异常是基于注解异常映射捕获不到的。
-
请求是由处理的,抛出了异常就得用基于xml的异常映射来捕获处理这个异常。
-
请求是由@RequestMapping这个注解来处理的,发生了异常就得使用基于注解的异常映射来捕获并处理这个异常。没找到基于注解的异常映射就会找xml的异常映射
(1) 基于xml的异常映射
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">system-errorprop>
props>
property>
bean>
-
exceptionMappings是SimpleMappingExceptionResolver的一个成员属性。
-
prop中的system-error会经过视图解析器处理,所以如果发生java.lang.Exception异常就会跳转到system-error.jsp页面
(2) 基于注解的异常映射
-
使用注解@ExceptionHandler指定异常处理方法。该注解只有一个可选属性value,为一个Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
-
使用注解@ControllerAdvice(控制器增强)定义全局异常处理类。使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler。
-
当使用@RequestMapping注解修饰的方法抛出异常时,会执行@ControllerAdvice修饰的类中的异常处理方法。
-
@ControllerAdvice 是使用@Component 注解修饰的,可以扫描到@ControllerAdvice 所在的类路径(包名), 创建对象。
-
若@ExceptionHandler使用在@RequestMapping注解的方法上,异常处理方法去xml异常映射中找
12、拦截器
(1) 定义拦截器
需要实现 HandlerInterceptor 接口。而该接口中含有三个方法:
- preHandle(request,response, Object handler):该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。
- postHandle(request,response, Object handler,modelAndView):该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。
- afterCompletion(request,response, Object handler, Exception ex):当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。 即该方法是在中央调度器渲染(数据填充) 了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。afterCompletion 最后执行的方法,清除资源,例如在 Controller 方法中加入数据
(2) 注册拦截器
<!--注册拦截器--> <mvc:interceptors> <mvc:interceptor> <!--mvc:mapping配置要拦截的资源--> <!-- /* 对应一层路径,比如:/aaa --> <!-- /** 对应多层路径,比如:/aaa/bbb 或 /aaa/bbb/ccc --> <mvc:mapping path="/**"/> <!-- mvc:exclude-mapping 配置不拦截的资源 --> <mvc:exclude-mapping path="/admin/to/login/page.html"/> <mvc:exclude-mapping path="/admin/do/login.html"/> <mvc:exclude-mapping path="/admin/do.logout.html"/> <bean class="com.atguigu.crowd.mvc.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
(3) 多拦截器执行顺序
13、SpringMVC配置文件总结
-
组件扫描器:<context:component-scan base-package>
-
视图解析器:<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
-
声明注解驱动:<mvc:annotation-driven />
-
静态资源访问
-
<mvc:default-servlet-handler/>:创建DefaultServletHttpRequestHandler处理器对象
-
<mvc:resources/>:加入静态资源
-
<mvc:annotation-driven />声明注解驱动
-
异常处理
-
xml方式:<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
-
注解方式:
-
组件扫描器扫描@ControllerAdvice注解所在的包
-
声明注解驱动
-
注册拦截器:<mvc:interceptors>
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--声明组件扫描器--> <context:component-scan base-package="com.luca.security.config.controller"/> <!--声明 springmvc框架中的视图解析器,帮助开发人员设置视图文件的路径--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀:视图文件的路径--> <property name="prefix" value="/WEB-INF/view/"/> <!--后缀:视图文件的扩展名--> <property name="suffix" value=".jsp"/> </bean> <!--声明注解驱动--> <mvc:annotation-driven/> <!--静态资源访问--> <!--方式一--> <mvc:default-servlet-handler/> <!--方式二:使用一个配置语句,指定多种静态资源的访问--> <mvc:resources mapping="/static/**" location="/static/"/> <!--异常处理--> <!--配置基于xml的异常映射--> <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.Exception">system-error</prop> </props> </property> </bean> <!--基于注解的异常映射--> <context:component-scan base-package="com.luca.controller"/> <!--注册拦截器--> <mvc:interceptors> <mvc:interceptor> <!--mvc:mapping配置要拦截的资源--> <!-- /* 对应一层路径,比如:/aaa --> <!-- /** 对应多层路径,比如:/aaa/bbb 或 /aaa/bbb/ccc --> <mvc:mapping path="/**"/> <!-- mvc:exclude-mapping 配置不拦截的资源 --> <mvc:exclude-mapping path="/admin/to/login/page.html"/> <mvc:exclude-mapping path="/admin/do/login.html"/> <mvc:exclude-mapping path="/admin/do.logout.html"/> <bean class="com.atguigu.crowd.mvc.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors> </beans>
浙公网安备 33010602011771号