SpringMVC学习笔记
SpringMVC配置逻辑
HandlerMapping & HandlerAdapter
DispatcherServlet维护着web组件,其中某种组件有多个不同类型。比如映射器、适配器、异常处理器、视图解析器。它们的配置方式都差不多,主要分析映射器和适配器。
映射器在上一篇文章已经简要说明。在不进行手动配置的情况下,默认提供的有三种BeanNameUrlHandlerMapping,RequestMappingHandlerMapping,RouterFunctionMapping。同样的HandlerAdapter也有默认的实现类。
适配器接口提供了两个留给子类实现的抽象方法,supports,handle方法,分别用来检查适配器是否能处理handler和执行handler。
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
分析最常用的RequestMappingHandlerAdapter。适配器要执行要用到参数解析器和返回值处理器等。它们聚合成HandlerMethodArgumentResolverComposite、HandlerMethodReturnValueHandlerComposite保存在适配器中。RequestMappingHandlerAdapter在invokeHandlerMethod方法中执行handler。然后又定位到ServletInvocableHandlerMethod的invokeAndHandle方法。再到invokeForRequest方法,这一步会调用传入的参数解析器集合,从中取出所有的参数解析器进行匹配,匹配成功则进行解析。然后执行doInvoke,执行完毕后回到invokeAndHandle调用返回值处理器。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
/**
* Get the method argument values for the current request, checking the provided
* argument values and falling back to the configured argument resolvers.
* <p>The resulting array will be passed into {@link #doInvoke}.
* @since 5.1.2
*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
EnableWebMVC
配置进化过程:xml配置大量bean来支撑mvc功能,@EnableWebMvc—>自动装配相关组件。
SpringBoot的maven依赖的自动装配。
DispatcherServlet要使用容器中存在的的组件,在initStrategies中会获取到它所需要的组件。现在我们要配置自定义组件就要想办法在initStrategies方法之前往容器自定义修改组件,好让initStrategies方法能够拿到。
在Servlet配置类上使用@EnableWebMvc注解的效果等同于继承DelegatingWebMvcConfiguration和WebMvcConfigurationSupport。通过这两个类的方法我们可以自定义组件。在WebMvcConfigurationSupport类中我们可以找到很多有Bean注解的返回组件的方法,这些Bean就是我们使用@EnableWebMvc后容器中提供的组件。容器在创建时会加载到配置类,容器refresh时会调用到这些Bean注解的方法创建对象,因此组件存在于容器中了。通过编写符合要求的能被容器扫描的配置类,再去继承DelegatingWebMvcConfiguration类可以帮我们用少量代码自定义想要的组件。例如自定义RequestMappingHandlerMapping,
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
例如自定义RequestMappingHandlerMapping,在原来功能的基础上增加一个由"/hello2"路径到hello方法的映射。
@ComponentScan(
value = "com.tg",
includeFilters = {
@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = {Controller.class, RestController.class})
},useDefaultFilters = false
)
//@Configuration
//@EnableWebMvc 开启EnableWebMvc和继承的效果一样
public class MyServletConfig extends DelegatingWebMvcConfiguration {
@Override
@Bean
@SneakyThrows
public RequestMappingHandlerMapping requestMappingHandlerMapping(
ContentNegotiationManager contentNegotiationManager,
FormattingConversionService conversionService,
ResourceUrlProvider resourceUrlProvider) {
//父类WebMvcConfigurationSupport中的RequestMappingHandlerMapping
RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping
(contentNegotiationManager, conversionService, resourceUrlProvider);
//进行功能加强,增加一个路径匹配
RequestMappingInfo build = RequestMappingInfo.paths("/hello2").build();
OrderController bean = getApplicationContext().getBean(OrderController.class);
requestMappingHandlerMapping.registerMapping(build,bean,OrderController.class.getMethod("hello"));
return requestMappingHandlerMapping;
}
}
自定义请求处理
在配置类中编写如下的三个Bean
@Bean("/my-http")
public MyServletRequestHandler myServletRequestHandler(){
return new MyServletRequestHandler();
}
@Bean("/user-action")
public UserAction userAction(){
return new UserAction();
}
@Bean
public ActionHandlerAdapter actionHandlerAdapter(){
return new ActionHandlerAdapter();
}
MyServletRequestHandler继承了HttpRequestHandler,WebMvcConfigurationSupport中提供有HttpRequestHandler的适配器。@Bean(“/my-http”)实现了url到handler的映射。这样就能实现一个简单的请求处理。
public class MyServletRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("this is myhttprequesthandler");
}
}
@Bean(“/user-action”)实现了url到UserAction的映射。可以将UserAction看作一个handler,但是UserAction没有继承HttpRequestHandler,因此容器中没有对应的适配器。于是用ActionHandlerAdapter继承HandlerAdapter,向容器中注册一个适配UserAction的适配器。实现请求处理。
要在SpringMVC上自定义请求处理,需要了解SpringMVC的映射器,适配器等等组件的设计思想,对接这些组件才能实现自定义请求处理。最常用的自定义请求处理是在配置类中配置包扫描,在能扫到的包中的@Controller注解类下,用@RequestMapping注解标注在方法上,实现请求处理。这个过程中我们等于新增了handler,映射器和适配器都没有改变。编写上面两种自定义请求处理主要是理解SpringMVC的运行方式,也体现了SpringMVC的兼容性。顺便回顾一下没有SpringMVC如何自定义请求处理,继承Servlet,重写service方法即可。
基于WebMvcConfigurer的自定义组件
自定义一个拦截器的方法,在配置类中编写方法返回WebMvcConfigurer的实现类返回。
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("开始处理请求");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("处理请求结束");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("处理请求完成");
}
});
}
};
}
这些WebMvcConfigurer会被自动注入到DelegatingWebMvcConfiguration的configurers
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
需要拦截器的组件会调用到getInterceptors方法,在此方法中配置类去找到由用户自定义的拦截器将它们注册,再添加两个默认的拦截器并返回。
组件设置拦截器
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
WebMvcConfigurerComposite的addInterceptors方法会遍历所有被注入的WebMvcConfigurer并调用它们的添加拦截器方法,实现拦截器的添加。
@Override
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
ebMvcConfigurerComposite的addInterceptors方法会遍历所有被注入的WebMvcConfigurer并调用它们的添加拦截器方法,实现拦截器的添加。
@Override
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}