SpringMvc学习笔记----SpringMVC配置逻辑

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);
		}
	}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值