基于servlet
springmvc的核心类DispatcherServlet
,基于javaweb传统开发模式,顶层继承自HttpServlet,在web.xml中配置该Servlet匹配的路径为/,即所有请求都会经过DispatcherServlet
进行处理。实现servlet的doGet、doPost等方法,最终都经过doDispatch
方法处理,在该方法进行参数解析、路径匹配处理、响应处理、异常处理等等。
基于listener
springmvc的ContextLoaderListener
通过实现监听器,初始化springmvc的全局上下文WebApplicationContext
。
以下流程都在doDispatch
中处理。
DefaultMultipartHttpServletRequest
.HandlerMapping
根据request得到请求路径
寻找匹配的类和方法,获取到匹配的handlerMethod
.(通过@requetMapping配置的类和方法,每个方法会被封装成一个handlerMethod
,该类包含了配对到的类的类型和方法信息method类)。HandlerInterceptor
,将所有匹配的拦截器对象加入interceptor
集合,并与第二步获取的handlerMethod
一块封装成执行链HandlerExecutionChain
对象。interceptor
的preHandler
方法,在请求到达对应的handlerMethod
前进行拦截处理。handlerMethod
获取对应的HandlerAdapter
准备开始处理请求,这时会获取到RequestMappingHandlerAdapter
。在该类中通过实现InitializingBean
初始化,设置已有的参数解析器集合HandlerMethodArgumentResolver
,然后循环调用匹配的参数解析器进行处理,默认参数解析器会借助DataBind
类进行请求数据和对象的绑定转换。(一个参数只会被一个参数解析器匹配并处理)。handlmethod
通过method类进行反射调用匹配的方法RequestMappingHandlerAdapter
封装的返回值处理HandlerMethodReturnValueHandler
集合,获取匹配的处理器,进行处理。处理后封装modelAndView
对象.(比如常用的@RequestBody
注解,就是通过返回值处理器RequestResponseBodyMethodProcessor
处理)。interceptor
的postHandler
,即在handlerMethod完成处理后,还没渲染视图前处理。以上整个过程的处理都是通过try catch包裹的,如果报错会被catch,然后放到Exception对象引用
,如果Exception对象不为空,则会通过HandlerExceptionResolver
异常处理器处理,同样只要有一个异常匹配并处理了就不会被其他处理器处理,处理后获取异常封装的modelAndView。此时的modelAndView可能是正常请求结束后封装的,也可能是出异常后封装的
)中的视图名字,通过ViewResolver
集合渲染视图。interceptor
的afterCompletion
,进行视图渲染后的操作(这一步被另外try catch,且只会打印错误信息,不会影响请求响应数据)。注意
:1-8步如果那部出现异常,都会提前跳到第9步。
简单总结:HandlerMapping
路径匹配->interceptor
执行拦截器preHandler->HandlerMethodArgumentResolver
参数解析->invoke执行匹配的方法->HandlerMethodReturnValueHandler
返回值解析->封装modelAndView->interceptor
执行拦截器postHandler
->HandlerExceptionResolver
异常处理->渲染视图->interceptor
执行拦截器afterCompletion.
用于数据绑定,将请求的数据转成对应的实体类,并且可支持校验参数,且支持类型转换。如果是基本数据类型
BeanWrapper
该类是spring用于方便的修改和获取bean中的数据,内部依赖BeanUtils对bean做反射操作。DataBinder内部依赖
BeanWrapper对bean进行设值和获取值。
WebDataBinder
继承DataBinderd类,对DataBinder做了增强处理,且支持将MultipartFile绑定到javaBean属性上。
通过@ControllerAdvice
修饰类,@InitBinder
注解方法,方法可以传入WebDataBinder参数,再创建一个类继承PropertyEditorSupport
,可以自定义针对某种类型进行自定义的转化。比如自定义data
类型的转换,将data转String,还可以对请求参数携带某些前缀的参数进行匹配,然后
原理:RequestMappingHandlerAdapter
在初始化数据绑定起时,会通过ApplicationContext
获取@ControllerAdvice
注解修饰的类,找到类中被@InitBinder
注解修饰的方法,封装Method存储起来。在生成绑定工厂时会获取这些Method创建。
实现例子
@ControllerAdvice
public class ControllerAdvices {
/**
* 把date类型空字符串转为null
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new StringTrimmerEditor(true));
}
}
另外一种参数转换器,initBind使用String->其他类型和其他类型->String,Converter支持类型转类型,initBind的优先级比较高。
例子:
public class QueryTypeConverter implements Converter<String, QueryType> {
@Override
public QueryType convert(String source) {
String value = source.trim();
if ("".equals(value)) {
return null;
}
return QueryType.getByType(Integer.valueOf(value));
}
}
@Configuration
public class MyMvcConfigurer implements WebMvcConfigurer{
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new QueryTypeConverter());
}
}
1.实现参数解析器,只要实现HandlerMethodArgumentResolver
接口,然后实现WebMvcConfigurer
类进行配置,通过addArgumentResolvers
将解析器加入到解析器集合中。通常可以用于实现自定义的参数解析器,需要注意有执行顺序的问题,排在前面的解析器先匹配成功可能会被前面的处理了。可以用来实现自定义注解解析参数。
2.也可以通过实现RequestBodyAdvice
接口,同时通过@ControllerAdvice
注解修饰类。原理是RequestResponseBodyMethodProcessor
实现了HandlerMethodArgumentResolver
(同时也实现返回值处理器),处理参数解析时会获取实现了ResponseBodyAdvice
接口的类,通过循环遍历匹配处理。而RequestMappingHandlerAdapter
在获取参数解析器时,有一步就是把RequestResponseBodyMethodProcessor
解析器加入集合中。
1.实现返回值处理器,只要实现HandlerMethodReturnValueHandler
接口,通过通过WebMvcConfigurer
类进行配置。可以用来实现自定义注解,对返回做一些处理,比如过滤某些字段、过滤空值等。
2.也可以通过实现ResponseBodyAdvice
接口,同时通过@ControllerAdvice
注解修饰类。原理是RequestResponseBodyMethodProcessor
实现了HandlerMethodReturnValueHandler
,处理参数解析时会获取实现了ResponseBodyAdvice
接口,通过循环遍历匹配处理。而RequestMappingHandlerAdapter
在获取默认返回值处理器时,有一步就是把RequestResponseBodyMethodProcessor
解析器加入集合中。
实现参数解析器RequestBodyAdvice和实现返回值处理器ResponseBodyAdvice都是通过获取实现了接口的类的对象列表,那么这个对象列表是从哪获取的,什么时候注入的?
RequestMappingHandlerAdapter
实现了InitializingBean
接口,在afterPropertiesSet
中,通过ApplicationContext
获取所有包含@ControllerAdvice
注解的类,得到一个类的集合,然后循环判断是否为RequestBodyAdvice
类的子类或者ResponseBodyAdvice
子类,如果是则加入到本类的成员变量requestResponseBodyAdvice
类集合中。然后经过一层封装,封装到前面提到的RequestResponseBodyMethodProcessor
类,再根据返回值处理器和参数解析器封装到各自的集合,放到成员变量中。
实现拦截器只要实现HandlerInterceptor
接口,通过WebMvcConfigurer
配置即可。可以做一些请求拦截,日志打印等处理。
实现异常处理器只要实现HandlerExceptionResolver
接口,通过WebMvcConfigurer配置即可。可以对一些异常进行捕捉处理,返回自定义的ModelAndView.
注解方式
通过@ControllerAdvice注释类 + @ExceptionHandler注释处理异常的方法 + @ResponseStatus + @ResponseBody
结合使用处理全局异常。
原理:是spring通过ExceptionHandlerExceptionResolver
类来处理,该类是spring标准异常处理器之一,会被加到处理器集合中,不用通过webMvcConfigurer
配置。该类实现了HandlerExceptionResolver
,在被创建时,通过实现InitializingBean
在afterPropertiesSet
方法中,会对@ControllerAdvice
封装的类进行获取并解析,解析该类中中被@ExceptionHandler
注释的方法,最终封装成一个ExceptionHandlerMethodResolver
类,设置到ExceptionHandlerExceptionResolver
集合全局变量。该类实现了异常处理器接口,所以有异常时,会遍历到此类进行处理,该类通过遍历ExceptionHandlerExceptionResolver
集合进行类型匹配。
1.以上的处理器都可以通过WebMvcConfigurer
配置,即写一个类实现该类,并用@Configuration
注解修饰类实现。原理是通过
DelegatingWebMvcConfiguration
类,该类依赖注入一个WebMvcConfigurer
集合,将所有实现该类的对象注入进来,然后该类又继承了webMvcConfigurationSupport
类并配置了@Configuration
加入到容器,而RequestMappingHandlerAdapter
获取参数解析器、返回值处理器其中有一步就是从这里获取。
2.其他配置方式则是基于spring默认的一些实现了对应处理器接口的类实现,这些默认类通过获取ApplicationContext
类获取使用某些注解的类或者实现了某些接口的类,将这些类解析到标准处理类中处理。为用户提供了方便的注解方式扩展。
校验签名可以通过拦截器实现
加密解密可以通过参数解析器和返回值解析器实现, spring mvc web xml配置
使用springmvc需要如下配置。使用springboot则不用配。
<servlet>
<servlet-name>springservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springservlet-name>
<url-pattern>/*url-pattern>
servlet-mapping>
<listener>
<listenerclass>
org.springframework.web.context.ContextLoaderListener
listener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:config/applicationContext.xmlparam-value>
context-param>