• spring MVC处理请求过程及配置详解


    本文主要梳理下Spring MVC处理http请求的过程,以及配置servlet及业务application需要的常用标签,及其包含的意义。

    spring MVC处理请求过程

    首先看一个整体图
    结构图

    简单说下各步骤:

    handlerMapping

    handlerMapping将请求映射到处理器,即图中的HandlerExecutionChain。依据是请求中的信息:请求URL(value),请求参数(params),请求方法(method),请求头(headers)。处理器方法从中获取参数,相关的标注有PathVariable、RequestParam、RequestHeader、CookieValue等。

    • 请求方法包括GET、POST、DELETE、HEAD、OPTIONS、PUT、TRACE等。大多数浏览器只支持GET和POST,解决方法:客户端post提交数据,添加“_method”参数来指定特定的方法;服务端配置HiddenHttpMethodFilter。spring会根据_method的值模拟特定的HTTP方法,从而被controller正确获取。

    DataBinder

    DataBinder处理servletRequest中的消息,对其进行数据类型转换(conversionService接口)和数据格式化(Formatter接口)操作,然后填充到入参对象中。再调用validator组件,做数据校验。把conversion和validator的结果放在BindingResult中。即BindingResult存储入参对象和校验错误对象,可直接作为controller处理方法的参数。

    看一下ConfigurableWebBindingInitializer(封装了WebDataBinder,继承自DataBinder)的代码:

    	//处理BindingResult
    	private BindingErrorProcessor bindingErrorProcessor;
    	//数据校验
    	private Validator validator;
    	//数据类型转换
    	private ConversionService conversionService;
    
    • 这里的convert做什么工作呢?比如controller处理方法的参数为User,包含用户名、密码、昵称等,而用户传入的参数是userName:password:nick这样的特定格式,这时就需要一个converter处理String2User。

    HandlerExecutionChain

    HandlerExecutionChain,执行链。包含一个处理器Handler(controller中的处理方法)及若干拦截器HandlerInterceptor。处理过程如下:

    HandlerExecutionChain结构

    • handlerInterceptor如果处理出错,就会直接返回结果,而不会到达handler。
    • 在进入handler之前,执行handlerInterceptor的preHandler方法;handler处理之后,执行handlerInterceptor的postHandler方法;相应被渲染后,执行handlerInterceptor的afterCompletion方法。

    viewResolve

    视图对象是一个Bean,视图对象由视图解析器负责实例化(感觉有点像handler和handlerAdapter)。可装配多个视图解析器,配置优先级。

    常用标签理解

    <mvc:annotation-driven/>

    • 默认创建并注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。如果配置自定义的,则替换默认的。
    • 默认注册FormattingConversionServiceFactoryBean(一个默认的ConversionService)。自定义conversionService使用属性conversion-service来组装。
    • 默认装配LocalValidatorFactoryBean,支持controller方法的入参标注@Valid。

    <mvc:default-servlet-handler />

    • 默认配置DefaultServletHttpRequestHandler,检查URL,若是静态资源,则将请求转由web应用服务器默认的servlet处理。否则,由DispatcherServlet处理。
    • 如果web应用服务器默认servlet的名字不是“default”,则需要配置 <mvc:default-servlet-handler default-servlet-name="yourServerDefaultServletName"/>

    <mvc:resources/>

    • 由Spring MVC框架自己处理静态资源,并添加一些有用的附加功能。可将多个路径映射为一个逻辑路径;按照配置路径顺序查找,只要查找到,即返回。

    其他

    ExceptionHandler

    标注在方法上,指定处理特定异常的方法。作用域:类。创建一个BaseController,里面指定各种异常的处理方法;其他controller继承BaseController。

    定义全局处理方法:配置SimpleMappingExceptionResolver 。

    示例:

    //处理一种异常@ExceptionHandler(RuntimeException.class)
    //处理多种异常
     @ExceptionHandler({BindException.class,RuntimeException.class})
     @ResponseBody
     public Map<String, Object> bindExceptionHandler(BindException e, HttpServletResponse response, HttpServletRequest request) {
         HashMap body = new HashMap();
         body.put("status", Integer.valueOf(1));
         response.setStatus(200);
         return body;
     }
    

    RequestBody

    标注RequestBody,将参数按照属性名匹配的方式,填充入POJO。支持级联的属性名。如下,为类结构图,则传递user参数时,应该写
    userName=tom&dept.deptId=1&dept.address.tel=102

    类结构图

    @RequestBody/ResponseBody

    @RequestBody/ResponseBody是开发中常用的注解。

    • 数据类型转换主要通过converter来实现,接口:HttpMessageConverter<T>,作用:将请求信息转换为一个对象,将对象输出为响应信息。相应接口:canRead,read,canWrite,write。相关标注:RequestBody,ResponseBody。
    • 当controller处理方法使用到@RequestBody/ResponseBody或者HttpEntity/ResponseEntity时,才使用HttpMessageConverter对请求/响应消息进行处理。
    • 处理表单数据的FormHttpMessageConverter,处理的数据类型为MultiValueMap,所以如果要模拟post请求,需要将参数封装成MultiValueMap,看这个示例

    关于HttpMessageConverter的装配和使用,看下源代码:
    RequestMappingHandlerAdapter 的属性包含如下几个:

    	private HandlerMethodArgumentResolverComposite argumentResolvers;
    	//参数解析器
    	private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
    	//请求报文和对象之间的转换
    	private List<HttpMessageConverter<?>> messageConverters;
    	//封装DataBinder,用于数据类型转换、数据格式化、数据校验
    	private WebBindingInitializer webBindingInitializer;
    

    其中HandlerMethodArgumentResolverComposite 是包含了一个HandlerMethodArgumentResolver的List,用于解析参数。 WebBindingInitializer接口 封装了WebDataBinder(继承自DataBinder),前面ConfigurableWebBindingInitializer是它的一个具体实现。 HttpMessageConverter就是我们配置的数据转换器。

    RequestMappingHandlerAdapter构造方法中会添加默认的几个HttpMessageConverter:

    	public RequestMappingHandlerAdapter() {
    		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    		stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316
    
    		this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
    		this.messageConverters.add(new ByteArrayHttpMessageConverter());
    		this.messageConverters.add(stringHttpMessageConverter);
    		this.messageConverters.add(new SourceHttpMessageConverter<Source>());
    		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    	}
    

    设置之后,将messageConverters添加到参数解析其中:

    	@Override
    	public void afterPropertiesSet() {
    		if (this.argumentResolvers == null) {
    			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
    			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    		}
        ......
    	}
    
    
    	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
    		// Annotation-based argument resolution
    		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    		......
    		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
    		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
    		......
    		return resolvers;
    	}
    

    可以看到,将messageConverters放入RequestResponseBodyMethodProcessor中。然后再在RequestResponseBodyMethodProcessor中使用具体的messageConverters转换报文和对象,同时对数据进行校验。

    	@Override
    	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    		//使用MessageConverters读取报文,并转为对象
    		Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
    		//创建WebDataBinder,获取对象参数
    		String name = Conventions.getVariableNameForParameter(parameter);
    		WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
    		//参数校验,结果存入 binder.getBindingResult()
    		if (argument != null) {
    			validate(binder, parameter);
    		}
            //将参数处理结果 binder.getBindingResult()放入mavContainer
    		mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
    
    		return argument;
    	}
    

    这里,把HttpMessageConverter和WebDataBinder的顺序搞清楚了。其实,既然HttpMessageConverter是用来解析报文为对象的,肯定是放在参数处理第一步的。

  • 相关阅读:
    Android按返回键退出程序但不销毁,程序后台运行,同QQ退出处理方式
    android 下动态获取控件的id
    Android大图片裁剪终极解决方案 原理分析
    如何使用Android MediaStore裁剪大图片
    最新的Android Sdk 使用Ant多渠道批量打包
    nodejs学习(1)
    C#——企业微信一般操作之一
    html(1)——转圈等待效果+鼠标移动悬浮显示相关信息
    SQL注入小结
    Java实现二叉树地遍历、求深度和叶子结点的个数
  • 原文地址:https://www.cnblogs.com/shoren/p/spring-flow.html
Copyright © 2020-2023  润新知