我们在之前的博客中己经分析了 Spring MVC 的大体结构,以及 Spring MVC 的 Controller是如何封装成 Handler以及根据 URL 获取如何查找 Handler,今天,我们继续来分析 Spring MVC的另一个细节,Controller 方法中的请求参数是如何注入的。那我们先来看一个例子。
1. 创建controller
//处理登录请求的后端控制器 //注意:@RequestParam注解中的required注解对表单提交中的属性是没有用的,就算不填它也会默认为空字符串,它只对GET请求中 //在url后加的key-value的属性有限制作用 @Controller @RequestMapping(value = {"/test"}) public class UserController { private static final String CURRENT_USER = "Now_user"; //如果是GET方法请求的话,就直接给用户返回登录的页面,此页面表单请求的方法为POST @RequestMapping(value = {"/login"},method = {RequestMethod.GET}) public ModelAndView LoginGet(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("index"); return modelAndView; } @RequestMapping(value = {"/register"},method = {RequestMethod.GET}) public String register(User user){ System.out.println(JSON.toJSONString(user)); return "registersuccess"; } @RequestMapping(value = {"/query"},method = {RequestMethod.GET}) public String query(String username,User user){ System.out.println(username); System.out.println(JSON.toJSONString(user)); return "registersuccess"; } @RequestMapping(value = {"/detail"}) public String detail(@RequestBody User user){ System.out.println(JSON.toJSONString(user)); return "registersuccess"; } @RequestMapping(value = {"/detailInfo"}) public String detailInfo(User user){ System.out.println(JSON.toJSONString(user)); return "registersuccess"; } // http://localhost:8080/test/login.htm?userName=zhangsan&password=lizi @RequestMapping(value = {"login"},method = {RequestMethod.POST}) //让请求的url后面必须跟上一个叫做userName的属性,是用户的用户名 public ModelAndView LoginPost(@RequestParam(value = "userName") String userName, //请求的url后必须跟上password属性,为用户当前的密码 @RequestParam(value = "password") String password,//Spring MVC框架集成了Servlet请求响应等一系列参数,可以在有需要的时候使用 HttpServletRequest request, HttpServletResponse response, HttpSession session, RedirectAttributes redirectAttributes) { //这里是和后端交互的代码,如果是用户登录的话就在数据库中查找对应的用户信息 if (userName.isEmpty() || password.isEmpty()) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("error", "用户名或密码为空"); modelAndView.setViewName("index"); return modelAndView; } //到了这里就说明用户登录成功了 System.out.println("===========================:" + userName + "密码是:" + password); //使用session进行会话跟踪 session.setAttribute(CURRENT_USER, userName); //创建模型与视图类,返回给前端控制器 ModelAndView modelAndView = new ModelAndView(); //重定向的时候,因为是客户端重新的请求,所以参数是不会被传到重定向页面的 //所以使用此方法,可以把属性放到一个叫做FlashMap的Map中 redirectAttributes.addFlashAttribute("userName", userName); redirectAttributes.addFlashAttribute("password", password); redirectAttributes.addAttribute("uname", userName); redirectAttributes.addAttribute("pwd", password); //使用重定向的时候不能写jsp的名字,要写url映射的路径 modelAndView.setViewName("redirect:/test/main"); return modelAndView; } }
@Controller @RequestMapping(value = {"/test"}) public class MainController { @RequestMapping(value = {"/main"}, method = {RequestMethod.GET}) public ModelAndView GetMain( HttpServletRequest request, HttpSession session) { ModelAndView modelAndView = new ModelAndView(); //对FlashMap中的参数进行提取,有两种方法 //第一种,使用RequestContextUtils(请求工具包),因为在重定向后FlashMap会把表单中的属性 //放在重定向新的请求中,所以可以获得请求中的FlashMap Map<String, ?> map = RequestContextUtils.getInputFlashMap(request); //把FlashMap直接放入模型,传给前端控制器 modelAndView.addAllObjects(map); //视图名传入 modelAndView.setViewName("test/main"); System.out.println("==========================get "); return modelAndView; } //第二种:使用@ModelAttribute注解 //因为FlashMap是处理这个url的初始化数据模型,所以可以通过这个注解拿到FlashMap的属性 @RequestMapping(value = {"/main"}, method = {RequestMethod.POST}) public String PostMain( @ModelAttribute(value = "userName") String userName, @ModelAttribute(value = "password") String password) { System.out.println("userName:" + userName + ",password:" + password); return "test/main"; } }
2.创建实体
@Data public class UserInfo implements Serializable { private int age ; private String desc; }
@Data public class User { private String username; private String password; private UserInfo userInfo; }
3.创建 web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee" xmlns:web="http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"> <display-name>spring_tiny</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring_101_200/config_181_190/spring185.xml</param-value> </context-param> <servlet> <servlet-name>spring_tiny</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring_tiny</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 自己配置描述文件,需要多少个描述文件就配置多少 --> <jsp-config> <!-- 配置c描述文件-对应c标签,这里的taglib-uri对应jsp中引入的uri --> <taglib> <taglib-uri>http://www.codecoord.com</taglib-uri> <taglib-location>/WEB-INF/c.tld</taglib-location> </taglib> </jsp-config> </web-app>
4. 创建 Spring xml
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <mvc:annotation-driven /> <context:component-scan base-package="com.spring_101_200.test_181_190.test_185_spring_mvc"></context:component-scan> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
5. 测试
请求 url
http://localhost:8080/test/register.htm?username=zhangsan&password=123456
AnnotationDrivenBeanDefinitionParser.java
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); //设置内容协商(Content Negotiation),也就是控制器返回的相同数据的多个表示(或视图) RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext); //注册RequestMappingHandlerMapping RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //Handler匹配url 的优先级,order越小,越优先匹配 handlerMappingDef.getPropertyValues().add("order", 0); handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef); //配置将键值对写到路径中映射作为controller中的方法参数 if (element.hasAttribute("enable-matrix-variables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } else if (element.hasAttribute("enableMatrixVariables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } //配置url路径匹配器 configurePathMatchingProperties(handlerMappingDef, element, parserContext); //跨域设置 RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source); handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef); //定义自定义请求参数转换器 RuntimeBeanReference conversionService = getConversionService(element, source, parserContext); //属性验证器 RuntimeBeanReference validator = getValidator(element, source, parserContext); //错误或者异常消息处理器 RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element); //注册ConfigurableWebBindingInitializer RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class); bindingDef.setSource(source); bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); bindingDef.getPropertyValues().add("conversionService", conversionService); bindingDef.getPropertyValues().add("validator", validator); bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver); //允许注册实现了HttpMessageConverter接口的bean,来对requestbody 或responsebody中的数据进行解析 ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext); //允许注册实现了WebArgumentResolver接口的bean,来对handlerMethod中的用户自定义的参数或annotation进行解析 ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext); //允许注册实现了HandlerMethodReturnValueHandler接口的bean,来对handler method的特定的返回类型做处理 ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext); //设置异步超时时间 String asyncTimeout = getAsyncTimeout(element); //获取异步执行器 RuntimeBeanReference asyncExecutor = getAsyncExecutor(element); //获取实现了CallableProcessingInterceptor异步执行拦截器 ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext); //获取实现了DeferredResultProcessingInterceptor异步执行拦截器 ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext); //设置RequestMappingHandlerAdapter适配器 RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class); handlerAdapterDef.setSource(source); handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef); handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters); //添加 requestBody增强 addRequestBodyAdvice(handlerAdapterDef); //添加 responseBody增强 addResponseBodyAdvice(handlerAdapterDef); //重定向后,如何去掉url后面的地址设置 if (element.hasAttribute("ignore-default-model-on-redirect")) { Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect")); handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel); } else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) { // "ignoreDefaultModelOnRedirect" spelling is deprecated Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect")); handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel); } if (argumentResolvers != null) { handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers); } if (returnValueHandlers != null) { handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers); } if (asyncTimeout != null) { handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout); } if (asyncExecutor != null) { handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor); } handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors); handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors); String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef); String uriCompContribName = "mvcUriComponentsContributor"; //注册CompositeUriComponentsContributorFactoryBean RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class); uriCompContribDef.setSource(source); uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef); uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService); parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef); //注册ConversionServiceExposingInterceptor拦截器 RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class); csInterceptorDef.setSource(source); csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService); //注册MappedInterceptor拦截器 RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class); mappedCsInterceptorDef.setSource(source); mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null); mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef); String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef); //注册ExceptionHandlerExceptionResolver解析器 RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class); exceptionHandlerExceptionResolver.setSource(source); exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters); exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0); addResponseBodyAdvice(exceptionHandlerExceptionResolver); String methodExceptionResolverName = parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver); //ResponseStatusExceptionResolver异常解析器 RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class); responseStatusExceptionResolver.setSource(source); responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); responseStatusExceptionResolver.getPropertyValues().add("order", 1); String responseStatusExceptionResolverName = parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver); //DefaultHandlerExceptionResolver异常解析器 RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class); defaultExceptionResolver.setSource(source); defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); defaultExceptionResolver.getPropertyValues().add("order", 2); String defaultExceptionResolverName = parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver); parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName)); parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName)); parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName)); parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName)); parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName)); parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName)); parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName)); // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off" MvcNamespaceUtils.registerDefaultComponents(parserContext, source); parserContext.popAndRegisterContainingComponent(); return null; } }
private RuntimeBeanReference getContentNegotiationManager(Element element, Object source, ParserContext parserContext) { RuntimeBeanReference contentNegotiationManagerRef; if (element.hasAttribute("content-negotiation-manager")) { contentNegotiationManagerRef = new RuntimeBeanReference(element.getAttribute("content-negotiation-manager")); } else { RootBeanDefinition factoryBeanDef = new RootBeanDefinition(ContentNegotiationManagerFactoryBean.class); factoryBeanDef.setSource(source); factoryBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); factoryBeanDef.getPropertyValues().add("mediaTypes", getDefaultMediaTypes()); String beanName = CONTENT_NEGOTIATION_MANAGER_BEAN_NAME; parserContext.getReaderContext().getRegistry().registerBeanDefinition(beanName , factoryBeanDef); parserContext.registerComponent(new BeanComponentDefinition(factoryBeanDef, beanName)); contentNegotiationManagerRef = new RuntimeBeanReference(beanName); } return contentNegotiationManagerRef; }
private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element, ParserContext parserContext) { Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching"); if (pathMatchingElement != null) { Object source = parserContext.extractSource(element); if (pathMatchingElement.hasAttribute("suffix-pattern")) { Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern")); handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch); } if (pathMatchingElement.hasAttribute("trailing-slash")) { Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash")); handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch); } //url后缀是否唯一 if (pathMatchingElement.hasAttribute("registered-suffixes-only")) { Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only")); handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch); } RuntimeBeanReference pathHelperRef = null; if (pathMatchingElement.hasAttribute("path-helper")) { pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper")); } pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source); handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef); RuntimeBeanReference pathMatcherRef = null; if (pathMatchingElement.hasAttribute("path-matcher")) { pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher")); } pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source); handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef); } }
public static RuntimeBeanReference registerCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations, ParserContext parserContext, Object source) { if (!parserContext.getRegistry().containsBeanDefinition("mvcCorsConfigurations")) { RootBeanDefinition corsConfigurationsDef = new RootBeanDefinition(LinkedHashMap.class); corsConfigurationsDef.setSource(source); corsConfigurationsDef.setRole(2); if (corsConfigurations != null) { corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations); } parserContext.getReaderContext().getRegistry().registerBeanDefinition("mvcCorsConfigurations", corsConfigurationsDef); parserContext.registerComponent(new BeanComponentDefinition(corsConfigurationsDef, "mvcCorsConfigurations")); } else if (corsConfigurations != null) { BeanDefinition corsConfigurationsDef = parserContext.getRegistry().getBeanDefinition("mvcCorsConfigurations"); corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations); } return new RuntimeBeanReference("mvcCorsConfigurations"); }
private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) { RuntimeBeanReference conversionServiceRef; if (element.hasAttribute("conversion-service")) { conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service")); } else { RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class); conversionDef.setSource(source); conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef); parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName)); conversionServiceRef = new RuntimeBeanReference(conversionName); } return conversionServiceRef; }
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) { if (element.hasAttribute("validator")) { return new RuntimeBeanReference(element.getAttribute("validator")); } else if (javaxValidationPresent) { RootBeanDefinition validatorDef = new RootBeanDefinition( "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean"); validatorDef.setSource(source); validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef); parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName)); return new RuntimeBeanReference(validatorName); } else { return null; } }
private RuntimeBeanReference getMessageCodesResolver(Element element) { if (element.hasAttribute("message-codes-resolver")) { return new RuntimeBeanReference(element.getAttribute("message-codes-resolver")); } else { return null; } }
private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) { Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters"); ManagedList<? super Object> messageConverters = new ManagedList<Object>(); if (convertersElement != null) { //注册自定义messageConverter messageConverters.setSource(source); for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) { Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null); messageConverters.add(object); } } //注册默认messageConverter if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) { messageConverters.setSource(source); messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source)); RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source); stringConverterDef.getPropertyValues().add("writeAcceptCharset", false); messageConverters.add(stringConverterDef); messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source)); messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source)); messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source)); if (romePresent) { messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source)); messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source)); } if (jackson2XmlPresent) { RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source); GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source); jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true); jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef); messageConverters.add(jacksonConverterDef); } else if (jaxb2Present) { messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source)); } if (jackson2Present) { RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source); GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source); jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef); messageConverters.add(jacksonConverterDef); } else if (gsonPresent) { messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source)); } } return messageConverters; }
private String getAsyncTimeout(Element element) { Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support"); return (asyncElement != null) ? asyncElement.getAttribute("default-timeout") : null; } private RuntimeBeanReference getAsyncExecutor(Element element) { Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support"); if (asyncElement != null) { if (asyncElement.hasAttribute("task-executor")) { return new RuntimeBeanReference(asyncElement.getAttribute("task-executor")); } } return null; }
private ManagedList<?> getCallableInterceptors(Element element, Object source, ParserContext parserContext) { ManagedList<? super Object> interceptors = new ManagedList<Object>(); Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support"); if (asyncElement != null) { Element interceptorsElement = DomUtils.getChildElementByTagName(asyncElement, "callable-interceptors"); if (interceptorsElement != null) { interceptors.setSource(source); for (Element converter : DomUtils.getChildElementsByTagName(interceptorsElement, "bean")) { BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter); beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef); interceptors.add(beanDef); } } } return interceptors; }
private ManagedList<?> getDeferredResultInterceptors(Element element, Object source, ParserContext parserContext) { ManagedList<? super Object> interceptors = new ManagedList<Object>(); Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support"); if (asyncElement != null) { Element interceptorsElement = DomUtils.getChildElementByTagName(asyncElement, "deferred-result-interceptors"); if (interceptorsElement != null) { interceptors.setSource(source); for (Element converter : DomUtils.getChildElementsByTagName(interceptorsElement, "bean")) { BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter); beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef); interceptors.add(beanDef); } } } return interceptors; }
protected void addRequestBodyAdvice(RootBeanDefinition beanDef) { if (jackson2Present) { beanDef.getPropertyValues().add("requestBodyAdvice", new RootBeanDefinition(JsonViewRequestBodyAdvice.class)); } } protected void addResponseBodyAdvice(RootBeanDefinition beanDef) { if (jackson2Present) { beanDef.getPropertyValues().add("responseBodyAdvice", new RootBeanDefinition(JsonViewResponseBodyAdvice.class)); } }
public static void registerDefaultComponents(ParserContext parserContext, Object source) { //注册BeanNameUrlHandlerMapping registerBeanNameUrlHandlerMapping(parserContext, source); //注册HttpRequestHandlerAdapter registerHttpRequestHandlerAdapter(parserContext, source); //注册SimpleControllerHandlerAdapter registerSimpleControllerHandlerAdapter(parserContext, source); }
private static void registerBeanNameUrlHandlerMapping(ParserContext parserContext, Object source) { if (!parserContext.getRegistry().containsBeanDefinition(BeanNameUrlHandlerMapping.class.getName())){ RootBeanDefinition beanNameMappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class); beanNameMappingDef.setSource(source); beanNameMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //Handler匹配url 的优先级,order越小,越优先匹配 beanNameMappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source); beanNameMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef); parserContext.getRegistry().registerBeanDefinition(BeanNameUrlHandlerMapping.class.getName(), beanNameMappingDef); parserContext.registerComponent(new BeanComponentDefinition(beanNameMappingDef, BeanNameUrlHandlerMapping.class.getName())); } }
private static void registerHttpRequestHandlerAdapter(ParserContext parserContext, Object source) { if (!parserContext.getRegistry().containsBeanDefinition(HttpRequestHandlerAdapter.class.getName())) { RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class); handlerAdapterDef.setSource(source); handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); parserContext.getRegistry().registerBeanDefinition(HttpRequestHandlerAdapter.class.getName(), handlerAdapterDef); parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HttpRequestHandlerAdapter.class.getName())); } }
private static void registerSimpleControllerHandlerAdapter(ParserContext parserContext, Object source) { if (!parserContext.getRegistry().containsBeanDefinition(SimpleControllerHandlerAdapter.class.getName())) { RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class); handlerAdapterDef.setSource(source); handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); parserContext.getRegistry().registerBeanDefinition(SimpleControllerHandlerAdapter.class.getName(), handlerAdapterDef); parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, SimpleControllerHandlerAdapter.class.getName())); } }
那么 User 对象是如何被注入值的呢?那我们来看看源码是如何实现的。
DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //如果当前的 handler 支持 last-modified 头处理 String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } ... //真正的激活handler 并返回视图 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ... }
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
这个时候,我们可能会产生疑问。handlerAdapters是从哪里来的呢?我们在Spring源码深度解析(郝佳)-学习-源码解析-Spring MVC(一)这篇博客己经分析过,在容器初始化时,会调用initHandlerAdapters方法,初始化所有的
private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; //detectAllHandlerAdapters 默认为 true if (this.detectAllHandlerAdapters) { Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { try { //如果自定义了handlerAdapter HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { } } if (this.handlerAdapters == null) { //使用默认的handlerAdapter, //org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ // org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ // org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); } } }
上述代码中,获取所有实现HandlerAdapter的Adapter,并且根据 Order 排序。那 Spring 注册了哪些实现了HandlerAdapter的 bean 呢?我们来看看<mvc:annotation-driven ></mvc:annotation-driven>标签解析RequestMappingHandlerAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,再来看看三个HandlerAdapter的类结构。
从上图中得知,RequestMappingHandlerAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter都实现了HandlerAdapter接口,但是只有RequestMappingHandlerAdapter实现了 Ordered接口,但是遗憾的是,RequestMappingHandlerAdapter也没有设置 ordered 的值,因此在上述 sort 方法中,以 Ordered 来排序,没有起到作用,所以handlerAdapters的顺序,就是以bean加入到容的先后中的顺序。从<mvc:annotation-driven ></mvc:annotation-driven>标签解析的过程中,发现RequestMappingHandlerAdapter是最先被加入到容器中的,因此在getHandlerAdapter方法中,遍历handlerAdapters时,先后顺序分别是RequestMappingHandlerAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter。接下来,我们继续看RequestMappingHandlerAdapter的supports方法。
public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); } protected boolean supportsInternal(HandlerMethod handlerMethod) { return true; }
在Spring源码深度解析(郝佳)-学习-源码解析-Spring MVC(三)-Controller 解析这篇博客分析得知,返回的handler肯定是HandlerMethod,并且supportsInternal方法默认返回true,因此getHandlerAdapter 方法最终返回的 HandlerAdapter是RequestMappingHandlerAdapter。理清了这里,我们继续来看handle方法实现。
AbstractHandlerMethodAdapter.java
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }
RequestMappingHandlerAdapter.java
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //检测 request checkRequest(request); //判断是否配置了SessionAttributes注解,并且注解中配置了 names 或 types 属性值 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } //如果需要,在同步块中执行invokeHandlerMethod if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return invokeHandlerMethod(request, response, handlerMethod); } } } return invokeHandlerMethod(request, response, handlerMethod); }
protected final void checkRequest(HttpServletRequest request) throws ServletException { String method = request.getMethod(); //如果方法不支持,抛出异常 if (this.supportedMethods != null && !this.supportedMethods.contains(method)) { throw new HttpRequestMethodNotSupportedException( method, StringUtils.toStringArray(this.supportedMethods)); } //如果Session 不存在,但是又配置了必需 Session,抛出异常 if (this.requireSession && request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } }
首先,我们来看看,在什么情况下会设置supportedMethods呢?
而WebContentGenerator构造方法只有在AbstractController中才会被调用。
public WebContentGenerator() { this(true); }
因此,只有继承了AbstractController类,supportedMethods才会被设置成GET,HEAD,POST 方法,反过来说,继承了AbstractController的 Controller只支持GET,HEAD,POST方法,例子如下:
public class UserController extends AbstractController { @Override protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception { ... return new ModelAndView("userlist", "users", userList); } }
RequestMappingHandlerAdapter.java
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<Class<?>, SessionAttributesHandler>(64); private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) { Class<?> handlerType = handlerMethod.getBeanType(); SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { synchronized (this.sessionAttributesHandlerCache) { sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore); this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler); } } } return sessionAttrHandler; }
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) { Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null."); this.sessionAttributeStore = sessionAttributeStore; SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class); if (annotation != null) { this.attributeNames.addAll(Arrays.asList(annotation.names())); this.attributeTypes.addAll(Arrays.asList(annotation.types())); } for (String attributeName : this.attributeNames) { this.knownAttributeNames.add(attributeName); } }
getSessionAttributesHandler方法主要是看 HandlerType 中是否有SessionAttributes注解,如果有,则将names和types属性值保存到attributeNames和attributeTypes属性中。接下来,我们继续看hasSessionAttributes的实现逻辑
public boolean hasSessionAttributes() { return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0)); }
protected final void prepareResponse(HttpServletResponse response) { if (this.cacheControl != null) { //设置response的Cache-Control,清空Pragma applyCacheControl(response, this.cacheControl); } else { applyCacheSeconds(response, this.cacheSeconds); } }
protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds) { if (this.useExpiresHeader || !this.useCacheControlHeader) { if (cacheSeconds > 0) { //设置response的Expires,Cache-Control,清空Pragma cacheForSeconds(response, cacheSeconds); } else if (cacheSeconds == 0) { //设置response的Pragma为no-cache //Expires为1秒 //Cache-Control为no-cache或no-store preventCaching(response); } } else { CacheControl cControl; if (cacheSeconds > 0) { cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); //如果始终必需重新验证 if (this.alwaysMustRevalidate) { //必需重新验证 cControl = cControl.mustRevalidate(); } } else if (cacheSeconds == 0) { //设置noStore或noCache为true cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); } else { cControl = CacheControl.empty(); } applyCacheControl(response, cControl); } }
protected final void applyCacheControl(HttpServletResponse response, CacheControl cacheControl) { String ccValue = cacheControl.getHeaderValue(); if (ccValue != null) { //设置 response 的Cache-Control response.setHeader("Cache-Control", ccValue); //清空response的Pragma if (response.containsHeader("Pragma")) { response.setHeader("Pragma", ""); } } }
对于applyCacheSeconds和prepareResponse方法的分析得知,这两个方法无非是对 response头设置,下面我们继续来看invokeHandlerMethod方法的实现。
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //构建ServletWebRequest对象 ServletWebRequest webRequest = new ServletWebRequest(request, response); //获取数据绑定工厂ServletRequestDataBinderFactory WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); //创建ModelFactory ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); //创建ServletInvocableHandlerMethod对象,并初始化ResponseStatus ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); //设置方法参数解析器 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); //设置返回值处理器 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); //设置参数名发现器 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); //构建 ModelAndView 容器 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //将request 中的 FlashMap 属性加入到 ModelAndView中 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); //初始化 Model modelFactory.initModel(webRequest, mavContainer, invocableMethod); //使用 redirect 时不带参数,但是解决不了 //拦截器 MyInterceptor 的 public void postHandle //方法里面加了一个modelAndView.addObject("siteName", "我的站"); 那这个 redirect 后,会吧这个参数带上的问题 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); } //调用 controller中的方法 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } //返回视图 return getModelAndView(mavContainer, modelFactory, webRequest); }
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) || hasResponseStatus() || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(this.responseReason)) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); try { //返回值处理器处理返回值 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } }
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() { @Override public boolean matches(Method method) { return AnnotationUtils.findAnnotation(method, InitBinder.class) != null; } }; public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() { @Override public boolean matches(Method method) { return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) && (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null)); } }; private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception { Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.initBinderCache.get(handlerType); if (methods == null) { //找到Controller中所有配置了InitBinder注解的方法 methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); } List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>(); for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache .entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { //将配置了InitBinder注解的全局方法加入到initBinderMethods中 initBinderMethods.add(createInitBinderMethod(bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); //将 Controller中配置了InitBinder注解的方法加入到initBinderMethods中 initBinderMethods.add(createInitBinderMethod(bean, method)); } //创建数据绑定工厂 return createDataBinderFactory(initBinderMethods); }
对于Controller层的InitBinder注解方法实现非常简单,示例如下:
@InitBinder("user") public void initBinderUser(WebDataBinder binder) { binder.setFieldDefaultPrefix("user."); } @RequestMapping("/bind") @ResponseBody public Map<String, Object> bind(HttpServletRequest request, @ModelAttribute("user") User user) { Map<String, Object> map = new HashMap<String, Object>(); map.put("user", user); System.out.println(user.getUsername()); System.out.println(user.getPassword()); return map; }
但是如何配置全局的InitBinder方法呢?请看如下示例:
@ControllerAdvice public class MyControllerAdvice { @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Double.class, new DoubleEditor()); } @ExceptionHandler(value = Exception.class) public void exceptionHandler(Exception e, HttpServletResponse response) throws IOException { e.printStackTrace(); StringBuffer msg = new StringBuffer(); response.setContentType("text/html; charset=utf-8"); PrintWriter out = response.getWriter(); System.out.println("=================" + msg.toString()); out.write(msg.toString()); out.flush(); out.close(); } }
那我们来看全局initBinderAdviceCache属性是在哪被初始化?我们先来看Bean的生命周期,
在RequestMappingHandlerAdapter的bean生命周期中会调用一个非常重要的方法afterPropertiesSet,我们进入这个方法看看
public void afterPropertiesSet() { initControllerAdviceCache(); if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); //将默认及自定义参数解析器加入到argumentResolvers中 this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.initBinderArgumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); //将默认及自定义绑定参数解析器加入到argumentResolvers中 this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); //将默认及自定义的返回值处理器加入到returnValueHandlers中 this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }
看到initControllerAdviceCache这个方法,感觉看到了希望,至于其他方法,我们后面再来分析,继续跟进。
private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } if (logger.isInfoEnabled()) { logger.info("Looking for @ControllerAdvice: " + getApplicationContext()); } //从容器中获取所有配置了ControllerAdviceBean注解的 bean List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); //以Order进行排序 AnnotationAwareOrderComparator.sort(beans); List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>(); //遍历所有的 bean for (ControllerAdviceBean bean : beans) { //获取所有配置了ModelAttribute注解,但是没有配置RequestMapping注解的bean Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(bean, attrMethods); logger.info("Detected @ModelAttribute methods in " + bean); } //获取所有配置了InitBinder注解的 bean Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS); if (!binderMethods.isEmpty()) { this.initBinderAdviceCache.put(bean, binderMethods); logger.info("Detected @InitBinder methods in " + bean); } //如果 bean所在类配置了RequestBodyAdvice注解 if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) { requestResponseBodyAdviceBeans.add(bean); logger.info("Detected RequestBodyAdvice bean in " + bean); } //如果bean 所在类配置了ResponseBodyAdvice注解 if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) { requestResponseBodyAdviceBeans.add(bean); logger.info("Detected ResponseBodyAdvice bean in " + bean); } } if (!requestResponseBodyAdviceBeans.isEmpty()) { this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); } }
通过上述代码的分析,我们知道了initBinderAdviceCache是在RequestMappingHandlerAdapter的生命周期中调用afterPropertiesSet方法进行初始化的,初始化的条件就是方法所在类配置了ControllerAdvice注解,并且方法本身也配置了InitBinder注解。
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) { InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method); binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers); //从<mvc:annotation-driven ></mvc:annotation-driven>标签解析时,知道webBindingInitializer是ConfigurableWebBindingInitializer binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer)); binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); return binderMethod; }
上述方法中,关于initBinderArgumentResolvers和parameterNameDiscoverer是从哪里来的呢?我们回到RequestMappingHandlerAdapter的afterPropertiesSet方法,在方法的内部有一段这个的代码
if (this.initBinderArgumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); }
先来看看getDefaultInitBinderArgumentResolvers方法的内部实现。
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); //基于注释的参数解析 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); //基于类型的参数解析 resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); //自定义参数解析 if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } //包罗万象的参数解析器 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); return resolvers; }
通过上述代码分析,我们知道了initBinderArgumentResolvers属性是在RequestMappingHandlerAdapter的afterPropertiesSet方法内赋值的,先是加载系统默认的注解参数解析器,再加载用户自定义的参数解析器,接下来,我们继续分析parameterNameDiscoverer属性是在哪里赋值的。
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
发现是在RequestMappingHandlerAdapter类中直接创建的,就不做深究了。
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods) throws Exception { return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer()); } public WebBindingInitializer getWebBindingInitializer() { return this.webBindingInitializer; }
webBindingInitializer是从哪里来的呢?细心的读者会发现,是在<mvc:annotation-driven ></mvc:annotation-driven>标签解析的时候注入ConfigurableWebBindingInitializer属性的,我们继续分析代码
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.modelAttributeCache.get(handlerType); if (methods == null) { //获取所有配置了ModelAttribute注解,但是没有配置RequestMapping注解的方法 methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS); this.modelAttributeCache.put(handlerType, methods); } List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>(); //获取全局配置了ModelAttribute注解,但是没有配置RequestMapping的方法,其实现原理和InitBinder注解一样 for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); }
public ModelFactory(List<InvocableHandlerMethod> invocableMethods, WebDataBinderFactory dataBinderFactory, SessionAttributesHandler sessionAttributesHandler) { if (invocableMethods != null) { for (InvocableHandlerMethod method : invocableMethods) { this.modelMethods.add(new ModelMethod(method)); } } this.dataBinderFactory = dataBinderFactory; this.sessionAttributesHandler = sessionAttributesHandler; }
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) { InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method); attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); attrMethod.setDataBinderFactory(factory); return attrMethod; }
同样,我们好奇argumentResolvers属性是哪里赋值的?老办法,看RequestMappingHandlerAdapter的afterPropertiesSet方法,其中有一段代码
if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); }
同样的方式,我们来看getDefaultArgumentResolvers方法的内部实现。
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); //基于注解的参数解析 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); //基于类型的参数解析 resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); //自定义参数解析 if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } //包罗万象的参数解析 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
初始化 ModelAndView 属性
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) throws Exception { //如果方法配置了@SessionAttributes注解,则从 session中取出注解属性 names对应值 Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); //将值合并到 Model中,如果 model中己经存在该值,不覆盖 mavContainer.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, mavContainer); for (String name : findSessionAttributeArguments(handlerMethod)) { if (!mavContainer.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'"); } mavContainer.addAttribute(name, value); } } }
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { while (!this.modelMethods.isEmpty()) { InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod(); String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value(); if (mavContainer.containsAttribute(modelName)) { continue; } Object returnValue = attrMethod.invokeForRequest(request, mavContainer); if (!attrMethod.isVoid()){ String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType()); if (!mavContainer.containsAttribute(returnValueName)) { mavContainer.addAttribute(returnValueName, returnValue); } } } }
private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) { for (ModelMethod modelMethod : this.modelMethods) { //全局ModelAttribute方法的方法参数是否配置了ModelAttribute注解 //如果方法参数配置了ModelAttribute注解,并且mavContainer Model并没有该注解属性值所对应的属性 //则略过当前modelMethod if (modelMethod.checkDependencies(mavContainer)) { if (logger.isTraceEnabled()) { logger.trace("Selected @ModelAttribute method " + modelMethod); } this.modelMethods.remove(modelMethod); return modelMethod; } } ModelMethod modelMethod = this.modelMethods.get(0); if (logger.isTraceEnabled()) { logger.trace("Selected @ModelAttribute method (not present: " + modelMethod.getUnresolvedDependencies(mavContainer)+ ") " + modelMethod); } this.modelMethods.remove(modelMethod); return modelMethod; }
private static class ModelMethod { private final Set<String> dependencies = new HashSet<String>(); private ModelMethod(InvocableHandlerMethod handlerMethod) { this.handlerMethod = handlerMethod; for (MethodParameter parameter : handlerMethod.getMethodParameters()) { //如果HandlerMethod方法参数中有ModelAttribute注解, //则将参数名称加入到ModelMethod的dependencies属性中 if (parameter.hasParameterAnnotation(ModelAttribute.class)) { this.dependencies.add(getNameForParameter(parameter)); } } } ... public boolean checkDependencies(ModelAndViewContainer mavContainer) { for (String name : this.dependencies) { if (!mavContainer.containsAttribute(name)) { return false; } } return true; } ... }
我相信读者看到这里,不知所以,不知道getNextModelMethod方法的意思是什么,那我们来看一个例子吧。
@ControllerAdvice public class MyControllerAdvice { @ModelAttribute("xxx") protected String modelXXX(String ccc ) { if("cccc".equals(ccc)){ return "return cccc"; }else{ return "return default"; } } @ModelAttribute("yyy") protected String modelYYY(@ModelAttribute("ddd") String ddd ) { if("dddd".equals(ddd)){ return "return dddd"; }else{ return "return default"; } } }
在上例中,我们配置了两个全局的ModelAttribute方法modelXXX和modelYYY,modelYYY方法中有一个配置了ModelAttribute注解的方法参数ddd,在初始化ModelMethod方法时,会将 ddd名称加入到dependencies属性中,在getNextModelMethod方法中遍历所有的ModelMethod方法,如先找到modelYYY方法,检测Model(mavContainer)中是否有modelYYY对应的ModelMethod对象的dependencies属性存在,有,则略过modelYYY对应的 ModelMethod,也就是modelYYY的invokeForRequest方法总比modelYYY先执行,结合后面的这段代码来看
if (!attrMethod.isVoid()){ String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType()); if (!mavContainer.containsAttribute(returnValueName)) { mavContainer.addAttribute(returnValueName, returnValue); } }
ModelMethod先执行invokeForRequest方法,那么就先设置mavContainer容器中的属性,后执行不可覆盖己经存在mavContainer中的属性,那么是什么情况呢,看下例子:
@ModelAttribute("xxx") protected String modelXXX(String ccc ) { if("cccc".equals(ccc)){ return "return cccc"; }else{ return "return default cccc"; } } @ModelAttribute("xxx") protected String modelYYY(@ModelAttribute("ddd") String ddd ) { if("dddd".equals(ddd)){ return "return dddd"; }else{ return "return default dddd"; } }
测试例子:
@Controller @RequestMapping(value = {"/test"}) public class MainController { @RequestMapping(value = {"/main2"}) public String PostMain( String userName, String password, @ModelAttribute("xxx") String xxx) { System.out.println("userName:" + userName + ",password:" + password + ",xxx:" + xxx); return "test/main"; } }
请求 url:
http://localhost:8080/test/main2.htm?userName=zhangsan&password=123&ccc=cccc&ddd=dddd
因为先执行modelXXX方法,返回return cccc,因此modelYYY在这里没有启到作用,分析完了getNextModelMethod方法,我们继续来分析ModelMethod的invokeForRequest方法。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { StringBuilder sb = new StringBuilder("Invoking ["); sb.append(getBeanType().getSimpleName()).append("."); sb.append(getMethod().getName()).append("] method with arguments "); sb.append(Arrays.asList(args)); logger.trace(sb.toString()); } //通过反射调用ModelAttribute方法 Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]"); } return returnValue; }
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; //parameterNameDiscoverer默认为DefaultParameterNameDiscoverer parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); //解析方法参数类型 GenericTypeResolver.resolveParameterType(parameter, getBean().getClass()); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } //找到参数类型解析器,保存到缓存中 if (this.argumentResolvers.supportsParameter(parameter)) { try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); } throw ex; } } if (args[i] == null) { String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); throw new IllegalStateException(msg); } } return args; }
public MethodParameter[] getMethodParameters() { return this.parameters; }
很遗憾,只看到从 get 方法中返回了parameters,但是parameters是在哪里初始化的呢?我们先来看HandlerMethod的构造方法。
public HandlerMethod(Object bean, Method method) { Assert.notNull(bean, "Bean is required"); Assert.notNull(method, "Method is required"); this.bean = bean; this.beanFactory = null; this.beanType = ClassUtils.getUserClass(bean); this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); this.resolvedFromHandlerMethod = null; }
终于看到了方法参数的初始化是调用了initMethodParameters方法。
HandlerMethod.java
private MethodParameter[] initMethodParameters() { int count = this.bridgedMethod.getParameterTypes().length; MethodParameter[] result = new MethodParameter[count]; for (int i = 0; i < count; i++) { result[i] = new HandlerMethodParameter(i); } return result; }
protected class HandlerMethodParameter extends SynthesizingMethodParameter { public HandlerMethodParameter(int index) { super(HandlerMethod.this.bridgedMethod, index); } @Override public Class<?> getContainingClass() { return HandlerMethod.this.getBeanType(); } @Override public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) { return HandlerMethod.this.getMethodAnnotation(annotationType); } }
先来看一下HandlerMethodParameter结构
SynthesizingMethodParameter.java
public SynthesizingMethodParameter(Method method, int parameterIndex) { super(method, parameterIndex); }
MethodParameter.java
public MethodParameter(Method method, int parameterIndex) { this(method, parameterIndex, 1); } public MethodParameter(Method method, int parameterIndex, int nestingLevel) { Assert.notNull(method, "Method must not be null"); //设置方法,及参数索引 this.method = method; this.parameterIndex = parameterIndex; this.nestingLevel = nestingLevel; //默认值是1 this.constructor = null; }
通过上述代码分析,getMethodParameters方法得到的只是一个空壳MethodParameter对象,其中记录了方法及方法参数索引位置。
GenericTypeResolver.java
public static Class<?> resolveParameterType(MethodParameter methodParam, Class<?> clazz) { Assert.notNull(methodParam, "MethodParameter must not be null"); Assert.notNull(clazz, "Class must not be null"); //设置方法所在类 methodParam.setContainingClass(clazz); //设置methodParam索引处的参数类型, //比如 method(String a ,User user ) //第0个位置的类型为 String 类型,第一个位置的类型为 User 类型 methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam).resolve()); return methodParam.getParameterType(); }
ResolvableType.java
public static ResolvableType forMethodParameter(MethodParameter methodParameter) { return forMethodParameter(methodParameter, (Type) null); }
public static ResolvableType forMethodParameter(MethodParameter methodParameter, Type targetType) { Assert.notNull(methodParameter, "MethodParameter must not be null"); //获取到方法所在类的ResolvableType类型 ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()); return forType(targetType, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()). getNested(methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel); }
static class MethodParameterTypeProvider implements TypeProvider { private final String methodName; private final Class<?>[] parameterTypes; private final Class<?> declaringClass; private final int parameterIndex; private transient MethodParameter methodParameter; public MethodParameterTypeProvider(MethodParameter methodParameter) { if (methodParameter.getMethod() != null) { //设置方法名称 this.methodName = methodParameter.getMethod().getName(); //设置参数类型列表 this.parameterTypes = methodParameter.getMethod().getParameterTypes(); } else { this.methodName = null; this.parameterTypes = methodParameter.getConstructor().getParameterTypes(); } //设置方法所在类类型 this.declaringClass = methodParameter.getDeclaringClass(); //设置方法参数索引 this.parameterIndex = methodParameter.getParameterIndex(); this.methodParameter = methodParameter; } }
static ResolvableType forType(Type type, TypeProvider typeProvider, VariableResolver variableResolver) { //因为方法参数传入的为 null if (type == null && typeProvider != null) { type = SerializableTypeWrapper.forTypeProvider(typeProvider); } if (type == null) { return NONE; } if (type instanceof Class) { return new ResolvableType(type, typeProvider, variableResolver, null); } cache.purgeUnreferencedEntries(); ResolvableType key = new ResolvableType(type, typeProvider, variableResolver); ResolvableType resolvableType = cache.get(key); if (resolvableType == null) { resolvableType = new ResolvableType(type, typeProvider, variableResolver, null); cache.put(resolvableType, resolvableType); } return resolvableType; }
static Type forTypeProvider(final TypeProvider provider) { Assert.notNull(provider, "Provider must not be null"); if (provider.getType() instanceof Serializable || provider.getType() == null) { return provider.getType(); } Type cached = cache.get(provider.getType()); if (cached != null) { return cached; } for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) { if (type.isAssignableFrom(provider.getType().getClass())) { ClassLoader classLoader = provider.getClass().getClassLoader(); Class<?>[] interfaces = new Class<?>[] { type, SerializableTypeProxy.class, Serializable.class }; InvocationHandler handler = new TypeProxyInvocationHandler(provider); cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler); cache.put(provider.getType(), cached); return cached; } } throw new IllegalArgumentException("Unsupported Type class " + provider.getType().getClass().getName()); }
public Type getType() { return this.methodParameter.getGenericParameterType(); }
MethodParameter.java
public Type getGenericParameterType() { if (this.genericParameterType == null) { if (this.parameterIndex < 0) { //如果方法参数索引小于0,以方法返回值作为参数类型返回 this.genericParameterType = (this.method != null ? this.method.getGenericReturnType() : null); } else { //如果方法参数索引大于等于0,如果方法不为空,则从方法中获取方法索引处的参数类型, //如果方法为空,则从构造函数中获取方法索引处的参数类型 //可能在有点绕,如 method(String a ,User usr ) //如果parameterIndex为0,则genericParameterType为 String 类型 //如果parameterIndex为1,则genericParameterType为 User 类型 this.genericParameterType = (this.method != null ? this.method.getGenericParameterTypes()[this.parameterIndex] : this.constructor.getGenericParameterTypes()[this.parameterIndex]); } } return this.genericParameterType; }
代码写了这么多,层层封装,其实最顶用的还是 this.method.getGenericParameterTypes()[this.parameterIndex]这句代码,直接通过反射从方法中获取索引位置的参数类型。也正是层层抽象,才让代码的封装性好,接下来,我们继续分析获取方法参数解析器。
HandlerMethodArgumentResolverComposite.java
public boolean supportsParameter(MethodParameter parameter) { return (getArgumentResolver(parameter) != null); }
HandlerMethodArgumentResolverComposite.java
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { //遍历所有的方法参数解析器,参数解析器的初始化在RequestMappingHandlerAdapter的afterPropertiesSet方法中 for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (logger.isTraceEnabled()) { logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]"); } if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; //如果参数类型解析器找到了,保存到缓存中 this.argumentResolverCache.put(parameter, result); break; } } } return result; }
我们来看解析器有很多,同时supportsParameter方法的实现也有很多。我们就随便挑几个解析器的supportsParameter方法来看看吧。
- RequestParamMethodArgumentResolver解析器的supportsParameter方法
public boolean supportsParameter(MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); if (parameter.hasParameterAnnotation(RequestParam.class)) { if (Map.class.isAssignableFrom(paramType)) { String paramName = parameter.getParameterAnnotation(RequestParam.class).name(); return StringUtils.hasText(paramName); } else { return true; } } else { if (parameter.hasParameterAnnotation(RequestPart.class)) { return false; } else if (MultipartFile.class == paramType || "javax.servlet.http.Part".equals(paramType.getName())) { return true; } else if (this.useDefaultResolution) { return BeanUtils.isSimpleProperty(paramType); } else { return false; } } }
public static boolean isSimpleProperty(Class<?> clazz) { Assert.notNull(clazz, "Class must not be null"); return isSimpleValueType(clazz) || (clazz.isArray() && isSimpleValueType(clazz.getComponentType())); } public static boolean isSimpleValueType(Class<?> clazz) { return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() || //对CharSequence类型及子类型支持,如 String CharSequence.class.isAssignableFrom(clazz) || //对Number类型及子类型支持 Number.class.isAssignableFrom(clazz) || //对 Date 类型及子类型支持 Date.class.isAssignableFrom(clazz) || //对URI及 URL 支持 URI.class == clazz || URL.class == clazz || //对 Locale 及 Class类型支持 Locale.class == clazz || Class.class == clazz); }
public static boolean isPrimitiveOrWrapper(Class<?> clazz) { Assert.notNull(clazz, "Class must not be null"); return (clazz.isPrimitive() || isPrimitiveWrapper(clazz)); }
public static boolean isPrimitiveWrapper(Class<?> clazz) { Assert.notNull(clazz, "Class must not be null"); return primitiveWrapperTypeMap.containsKey(clazz); }
从何而来呢?
static { primitiveWrapperTypeMap.put(Boolean.class, boolean.class); primitiveWrapperTypeMap.put(Byte.class, byte.class); primitiveWrapperTypeMap.put(Character.class, char.class); primitiveWrapperTypeMap.put(Double.class, double.class); primitiveWrapperTypeMap.put(Float.class, float.class); primitiveWrapperTypeMap.put(Integer.class, int.class); primitiveWrapperTypeMap.put(Long.class, long.class); primitiveWrapperTypeMap.put(Short.class, short.class); }
综合上述,发现RequestParamMethodArgumentResolver参数解析器对上述为绿色类型及其数组类型全部支持,值得注意的是,对 Class 类型支持,也就是说,我们任意定义一个对象作为方法的参数,也是支持的。
我们写一下上述supportsParameter的伪代码吧
if(方法参数有RequestParam注解){ if(参数类型是 Map 类型){ if(如果RequestParam有 name 属性值并且不为空){ return true ; }else { return fasle; } }else{ if(如果方法参数中有RequestPart注解){ return false; } if(如果方法参数类型是MultipartFile 或 方法参数名是javax.servlet.http.Part){ return true ; //useDefaultResolution初始化时,默认为 true }else if(如果类型是Number或Date或URI或Locale或 int,double,Double,String,等基本数据类型支持){ return true ; }else { return false; } } }
从上述分析中,RequestParamMethodArgumentResolver真是支持大部分情况,果然如注释据说,是万能解析器。
- PathVariableMethodArgumentResolver解析器
public boolean supportsParameter(MethodParameter parameter) { if (!parameter.hasParameterAnnotation(PathVariable.class)) { return false; } if (Map.class.isAssignableFrom(parameter.getParameterType())) { String paramName = parameter.getParameterAnnotation(PathVariable.class).value(); return StringUtils.hasText(paramName); } return true; }
可以看到PathVariableMethodArgumentResolver主要是对PathVariable注解的支持
- MatrixVariableMethodArgumentResolver解析器
public boolean supportsParameter(MethodParameter parameter) { if (!parameter.hasParameterAnnotation(MatrixVariable.class)) { return false; } if (Map.class.isAssignableFrom(parameter.getParameterType())) { String variableName = parameter.getParameterAnnotation(MatrixVariable.class).name(); return StringUtils.hasText(variableName); } return true; }
MatrixVariableMethodArgumentResolver主要是对MatrixVariable注解的支持。
- RequestResponseBodyMethodProcessor解析器
public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); }
RequestResponseBodyMethodProcessor解析器主要是对RequestBody注解支持。
因为解析器太多了,这里就不做一一的讲解了。
接下来,我们来看参数解析器是如何进行参数绑定的。
HandlerMethodArgumentResolverComposite.java
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { //获取参数解析器 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]"); } return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }
AbstractNamedValueMethodArgumentResolver.java
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Class<?> paramType = parameter.getParameterType(); //获取方法参数的名称信息 NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); //获取方法参数名对应的请求参数值 Object arg = resolveName(namedValueInfo.name, parameter, webRequest); //如果请求中,没有找到对应的方法参数对应的值 if (arg == null) { //如果默认值不为空,设置默认值 if (namedValueInfo.defaultValue != null) { arg = resolveDefaultValue(namedValueInfo.defaultValue); } //如果没有默认值,并且方法参数参数是必需的,并且方法参数类型不是Optional,抛出MissingServletRequestParameterException异常 else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) { handleMissingValue(namedValueInfo.name, parameter); } //如果方法参数是基本数据类型,int,double等,但是请求值为空,抛出IllegalStateException异常 arg = handleNullValue(namedValueInfo.name, arg, paramType); } //如果方法参数对应的值是空字符串,并且设置了默认值,则用默认值替换掉空字符串 else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveDefaultValue(namedValueInfo.defaultValue); } if (binderFactory != null) { //根据绑定工厂创建WebDataBinder对象,此时的binderFactory是ServletRequestDataBinderFactory对象 //是在上述代码的createDataBinderFactory方法中返回 WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); try { //将 String 类型转换成isSimpleProperty方法支持的数据类型 //内部当然用到了conversionService注册的类型转换器 arg = binder.convertIfNecessary(arg, paramType, parameter); } catch (ConversionNotSupportedException ex) { throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } catch (TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; }
看一下RequestParamMethodArgumentResolver的类结构关系。
AbstractNamedValueMethodArgumentResolver.java
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) { NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter); if (namedValueInfo == null) { namedValueInfo = createNamedValueInfo(parameter); namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo); this.namedValueInfoCache.put(parameter, namedValueInfo); } return namedValueInfo; }
RequestParamMethodArgumentResolver.java
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestParam ann = parameter.getParameterAnnotation(RequestParam.class); //如果有RequestParam注解,基于RequestParam注解创建RequestParamNamedValueInfo //如果没有,则创建默认的RequestParamNamedValueInfo return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); }
AbstractNamedValueMethodArgumentResolver.java
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) { String name = info.name; if (info.name.length() == 0) { //获取方法参数名称 name = parameter.getParameterName(); if (name == null) { throw new IllegalArgumentException("Name for argument type [" + parameter.getParameterType().getName() + "] not available, and parameter name information not found in class file either."); } } //如果没有配置RequestParam注解 String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue); //设置名称,参数是否必需,默认值 return new NamedValueInfo(name, info.required, defaultValue); }
public String getParameterName() { //默认的discoverer是DefaultParameterNameDiscoverer ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer; if (discoverer != null) { String[] parameterNames = (this.method != null ? discoverer.getParameterNames(this.method) : discoverer.getParameterNames(this.constructor)); if (parameterNames != null) { this.parameterName = parameterNames[this.parameterIndex]; } this.parameterNameDiscoverer = null; } return this.parameterName; }
在深入源码之前,我们先来看一下DefaultParameterNameDiscoverer类关系结构。
PrioritizedParameterNameDiscoverer.java
public String[] getParameterNames(Method method) { for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) { String[] result = pnd.getParameterNames(method); if (result != null) { return result; } } return null; }
代码运行到这里,可能我们又犯迷惑了,parameterNameDiscoverers集合又是从哪里来呢?其实原因很简单,我们来看看DefaultParameterNameDiscoverer的内部实现。
DefaultParameterNameDiscoverer.java
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer { private static final boolean standardReflectionAvailable = ClassUtils.isPresent( "java.lang.reflect.Executable", DefaultParameterNameDiscoverer.class.getClassLoader()); public DefaultParameterNameDiscoverer() { if (standardReflectionAvailable) { addDiscoverer(new StandardReflectionParameterNameDiscoverer()); } addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); } }
parameterNameDiscoverers默认是StandardReflectionParameterNameDiscoverer和LocalVariableTableParameterNameDiscoverer,我们来看其内部方法getParameterNames的实现。
1. StandardReflectionParameterNameDiscoverer.java
public String[] getParameterNames(Method method) { //直接从method获取方法参数名称 Parameter[] parameters = method.getParameters(); String[] parameterNames = new String[parameters.length]; for (int i = 0; i < parameters.length; i++) { Parameter param = parameters[i]; if (!param.isNamePresent()) { return null; } parameterNames[i] = param.getName(); } return parameterNames; }
2.LocalVariableTableParameterNameDiscoverer
public String[] getParameterNames(Method method) { //先从缓存中获取 Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); Class<?> declaringClass = originalMethod.getDeclaringClass(); Map<Member, String[]> map = (Map)this.parameterNamesCache.get(declaringClass); if (map == null) { //从字节码中获取 map = this.inspectClass(declaringClass); this.parameterNamesCache.put(declaringClass, map); } return map != NO_DEBUG_INFO_MAP ? (String[])map.get(originalMethod) : null; }
private Map<Member, String[]> inspectClass(Class<?> clazz) { InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz)); if (is == null) { if (logger.isDebugEnabled()) { logger.debug("Cannot find '.class' file for class [" + clazz + "] - unable to determine constructor/method parameter names"); } return NO_DEBUG_INFO_MAP; } else { try { ClassReader classReader = new ClassReader(is); Map<Member, String[]> map = new ConcurrentHashMap(32); classReader.accept(new LocalVariableTableParameterNameDiscoverer.ParameterNameDiscoveringVisitor(clazz, map), 0); ConcurrentHashMap var5 = map; return var5; } catch (IOException var17) { if (logger.isDebugEnabled()) { logger.debug("Exception thrown while reading '.class' file for class [" + clazz + "] - unable to determine constructor/method parameter names", var17); } } catch (IllegalArgumentException var18) { if (logger.isDebugEnabled()) { logger.debug("ASM ClassReader failed to parse class file [" + clazz + "], probably due to a new Java class file version that isn't supported yet " + "- unable to determine constructor/method parameter names", var18); } } finally { try { is.close(); } catch (IOException var16) { } } return NO_DEBUG_INFO_MAP; } }
如果配置了RequestParam注解,以其值作为参数名称,如果没有配置 RequestParam注解,通过反射或者 ASM 技术从类字节码中加载方法参数名称,关于 ASM 通过字节码来获取方法的参数名称,在之前的博客 Spring源码深度解析(郝佳)-学习-ASM 类字节码解析己经做过详细的分析,这里就不再赘述。既然方法参数名称获取到了,如何来获取方法参数对应的值呢?
RequestParamMethodArgumentResolver.java
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class); Object arg; //如果是文件上传或多文件上传 if (MultipartFile.class == parameter.getParameterType()) { assertIsMultipartRequest(servletRequest); Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); arg = multipartRequest.getFile(name); } else if (isMultipartFileCollection(parameter)) { assertIsMultipartRequest(servletRequest); Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); arg = multipartRequest.getFiles(name); } else if (isMultipartFileArray(parameter)) { assertIsMultipartRequest(servletRequest); Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?"); List<MultipartFile> multipartFiles = multipartRequest.getFiles(name); arg = multipartFiles.toArray(new MultipartFile[multipartFiles.size()]); } //如果方法参数类型是Part类型,或者Part数组,或Part集合类型 else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) { assertIsMultipartRequest(servletRequest); arg = servletRequest.getPart(name); } else if (isPartCollection(parameter)) { assertIsMultipartRequest(servletRequest); arg = new ArrayList<Object>(servletRequest.getParts()); } else if (isPartArray(parameter)) { assertIsMultipartRequest(servletRequest); arg = RequestPartResolver.resolvePart(servletRequest); } else { arg = null; //文件类型处理 if (multipartRequest != null) { List<MultipartFile> files = multipartRequest.getFiles(name); if (!files.isEmpty()) { arg = (files.size() == 1 ? files.get(0) : files); } } if (arg == null) { //根据方法参数名称获取request请求参数值数组 String[] paramValues = webRequest.getParameterValues(name); if (paramValues != null) { //如果参数值长度只有1个,则获取第0个参数值,如果长度大于1,则整个字符数组返回 arg = (paramValues.length == 1 ? paramValues[0] : paramValues); } } } return arg; }
DefaultDataBinderFactory.java
public final WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception { //创建ExtendedServletRequestDataBinder对象 WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest); if (this.initializer != null) { //此时的initializer是ConfigurableWebBindingInitializer this.initializer.initBinder(dataBinder, webRequest); } initBinder(dataBinder, webRequest); return dataBinder; }
ServletRequestDataBinderFactory.java
protected ServletRequestDataBinder createBinderInstance(Object target, String objectName, NativeWebRequest request) { return new ExtendedServletRequestDataBinder(target, objectName); }
ConfigurableWebBindingInitializer.java
public void initBinder(WebDataBinder binder, WebRequest request) { binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths); //在项目中遇到某实例中的某个属性被声明为private ,而且实体类中,不提供该属性的get方法, //导致无法通过get获取该属性值。这个时候,我们可以利用DirectFieldAccessor这个类 //进行获取我们想要的属性值。 //如:User user = new User(111); User对象有 id 属性,但是没有提供 get/set 方法 //DirectFieldAccessor accessor = new DirectFieldAccessor(user); //TypeDescriptor id = accessor.getPropertyTypeDescriptor("id"); //获取 id的值: //Object idValue = accessor.getPropertyValue("id"); if (this.directFieldAccess) { binder.initDirectFieldAccess(); } //Spring MVC有一种生成错误代码以从绑定错误中呈现错误消息的策略:MessageCodesResolver。 //如果您设置spring.mvc.messagecodes-resolver.format属性PREFIX_ERROR_CODE或 //POSTFIX_ERROR_CODE(请参阅DefaultMessageCodesResolver.Format中的枚举), //Spring Boot将为您创建一个。 //这里主要是设置自定义的messageCodesResolver if (this.messageCodesResolver != null) { binder.setMessageCodesResolver(this.messageCodesResolver); } //设置绑定错误处理器 if (this.bindingErrorProcessor != null) { binder.setBindingErrorProcessor(this.bindingErrorProcessor); } //设置较验器 if (this.validator != null && binder.getTarget() != null && this.validator.supports(binder.getTarget().getClass())) { binder.setValidator(this.validator); } //设置类型转换器 if (this.conversionService != null) { binder.setConversionService(this.conversionService); } //设置自定义属性编辑器 if (this.propertyEditorRegistrars != null) { for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) { propertyEditorRegistrar.registerCustomEditors(binder); } } }
conversionService的默认值是FormattingConversionServiceFactoryBean,从现在代码中发现FormattingConversionServiceFactoryBean实现了InitializingBean接口。因此在bean的生命周期中会调用afterPropertiesSet方法。
下面我们来看一下afterPropertiesSet方法的内部实现。
public void afterPropertiesSet() { this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters); ConversionServiceFactory.registerConverters(this.converters, this.conversionService); registerFormatters(); }
public DefaultFormattingConversionService(StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters) { setEmbeddedValueResolver(embeddedValueResolver); DefaultConversionService.addDefaultConverters(this); if (registerDefaultFormatters) { addDefaultFormatters(this); } }
public static void addDefaultConverters(ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); if (jsr310Available) { Jsr310ConverterRegistrar.registerJsr310Converters(converterRegistry); } converterRegistry.addConverter(new ObjectToObjectConverter()); converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new FallbackObjectToStringConverter()); if (javaUtilOptionalClassAvailable) { converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); } }
private static void addCollectionConverters(ConverterRegistry converterRegistry) { ConversionService conversionService = (ConversionService) converterRegistry; converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService)); converterRegistry.addConverter(new CollectionToArrayConverter(conversionService)); converterRegistry.addConverter(new ArrayToArrayConverter(conversionService)); ... if (streamAvailable) { converterRegistry.addConverter(new StreamConverter(conversionService)); } }
代码运行到这里,发现DefaultFormattingConversionService对象的converters属性己经加了很多的转换器,比如 Array 转化为 Collection,Collection 转化为 Array等。
发现FormattingConversionServiceFactoryBean实现了FactoryBean接口,因此在ConfigurableWebBindingInitializer对象的conversionService属性的注入是调用了FormattingConversionServiceFactoryBean的getObject方法。
public FormattingConversionService getObject() { return this.conversionService; }
综合上述,最终setConversionService方法设置的对象是DefaultFormattingConversionService对象,对象数据如下:
InitBinderDataBinderFactory.java
public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception { for (InvocableHandlerMethod binderMethod : this.binderMethods) { //调用所有配置了InitBinder注解的方法,如果InitBinder注解的 value 属性为空 //或binder.objectName等于InitBinder注解的 value 属性,返回 true if (isBinderMethodApplicable(binderMethod, binder)) { //调用如下方法 //@InitBinder //protected void initBinder(WebDataBinder binder) { // binder.registerCustomEditor(Date.class, new MyDateEditor()); //} Object returnValue = binderMethod.invokeForRequest(request, null, binder); //如果配置了@InitBinder注解方法有返回值,则抛出IllegalStateException异常 if (returnValue != null) { throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod); } } } }
在分析解析器之前,我们先来看一个例子。
@RequestMapping(value = {"/detailInfo"}) public String detailInfo(User user) { System.out.println(JSON.toJSONString(user)); return "registersuccess"; }
请求 url 为http://localhost:8080/test/detailInfo.htm?username=zhangsan&password=123
参数是如何被封装进去的呢?接下来要分析的ServletModelAttributeMethodProcessor处理器就是对方法参数是普通对象时的处理,先来看一下ServletModelAttributeMethodProcessor类结构图。
既然是对普通对象的支持,那我们先来看一下其supportsParameter方法。
ServletModelAttributeMethodProcessor.java
public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { return true; } else if (this.annotationNotRequired) { return !BeanUtils.isSimpleProperty(parameter.getParameterType()); } else { return false; } }
我们看到了isSimpleProperty方法,这个方法是对简单的属性处理,ServletModelAttributeMethodProcessor不支持简单属性,那就肯定支持复杂属性喽。那我们来看一下resolveArgument方法的内部实现。
ModelAttributeMethodProcessor.java
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { //获取 Model的名称,如果配置了ModelAttribute注解,则取注解的value属性作为名称, //如果没有,则取Model 类型首字母小写作为 Model 名称,如 User 对象的名称是 user String name = ModelFactory.getNameForParameter(parameter); //如果mavContainer对象中包含此属性名,则从 mavContainer的Model 中获取,否则利用反射创建 Model 对象 Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest)); //初始化异常处理器,属性编辑器,类型转换器等,以及调用 InitBinder方法 WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { bindRequestParameters(binder, webRequest); validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } Map<String, Object> bindingResultModel = binder.getBindingResult().getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); }
ServletModelAttributeMethodProcessor.java
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) { ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class); ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; servletBinder.bind(request)(servletRequest); }
WebDataBinder.java
public void bind(ServletRequest request) { //获取request param 中的参数 MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class); //文件上传处理 if (multipartRequest != null) { bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } //将uri中的临时变量加入到mpvs中 addBindValues(mpvs, request); doBind(mpvs); } public ServletRequestParameterPropertyValues(ServletRequest request) { this(request, null, null); } public ServletRequestParameterPropertyValues(ServletRequest request, String prefix, String prefixSeparator) { super(WebUtils.getParametersStartingWith( request, (prefix != null ? prefix + prefixSeparator : null))); }
WebUtils.java
public static Map<String, Object> getParametersStartingWith(ServletRequest request, String prefix) { Assert.notNull(request, "Request must not be null"); Enumeration<String> paramNames = request.getParameterNames(); Map<String, Object> params = new TreeMap<String, Object>(); if (prefix == null) { prefix = ""; } while (paramNames != null && paramNames.hasMoreElements()) { String paramName = paramNames.nextElement(); if ("".equals(prefix) || paramName.startsWith(prefix)) { String unprefixed = paramName.substring(prefix.length()); String[] values = request.getParameterValues(paramName); if (values == null || values.length == 0) { // Do nothing, no values found at all. } else if (values.length > 1) { params.put(unprefixed, values); } else { params.put(unprefixed, values[0]); } } } return params; }
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE; Map<String, String> uriVars = (Map<String, String>) request.getAttribute(attr); if (uriVars != null) { for (Entry<String, String> entry : uriVars.entrySet()) { if (mpvs.contains(entry.getKey())) { logger.warn("Skipping URI variable '" + entry.getKey() + "' since the request contains a bind value with the same name."); } else { mpvs.addPropertyValue(entry.getKey(), entry.getValue()); } } } }
WebDataBinder.java
protected void doBind(MutablePropertyValues mpvs) { //替换掉mpvs设置了 field开头的请求参数 checkFieldDefaults(mpvs); //将下划线开头的请求参数置空 checkFieldMarkers(mpvs); super.doBind(mpvs); }
protected void checkFieldDefaults(MutablePropertyValues mpvs) { if (getFieldDefaultPrefix() != null) { String fieldDefaultPrefix = getFieldDefaultPrefix(); PropertyValue[] pvArray = mpvs.getPropertyValues(); for (PropertyValue pv : pvArray) { if (pv.getName().startsWith(fieldDefaultPrefix)) { String field = pv.getName().substring(fieldDefaultPrefix.length()); if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) { mpvs.add(field, pv.getValue()); } mpvs.removePropertyValue(pv); } } } }
对于checkFieldDefaults这个方法,为什么需要替换掉mpvs里的值呢?我们来看一个使用场景。
@InitBinder("user") public void initBinderUser(WebDataBinder binder) { binder.setFieldDefaultPrefix("user."); }
在 initBinderUser方法中我们设置了属性默认前缀为 user. ,请求 url 为http://localhost:8080/test/detail.htm?user.username=zhangsan&user.password=123456,此时会将 mpvs中的user.username
替换成username
protected void checkFieldMarkers(MutablePropertyValues mpvs) { if (getFieldMarkerPrefix() != null) { String fieldMarkerPrefix = getFieldMarkerPrefix(); PropertyValue[] pvArray = mpvs.getPropertyValues(); for (PropertyValue pv : pvArray) { if (pv.getName().startsWith(fieldMarkerPrefix)) { String field = pv.getName().substring(fieldMarkerPrefix.length()); if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) { Class<?> fieldType = getPropertyAccessor().getPropertyType(field); mpvs.add(field, getEmptyValue(field, fieldType)); } mpvs.removePropertyValue(pv); } } } }
在工作中遇到前端传来的参数是 _appId ,_os,_xx 这种的参数名时,后台在用springMVC接收参数获取值发现都是null ,问题就出现在上述方法。解决办法就是
@InitBinder protected void initBinder(WebDataBinder binder) { // 解决下划线开头参数无法映射到实体的问题 binder.setFieldMarkerPrefix(null); }
DataBinder.java
protected void doBind(MutablePropertyValues mpvs) { //允许某些字段或不允许某些字段较验 checkAllowedFields(mpvs); //检查指定不为空的属性,如属性被@RequestParam(value = "userName" ,required = true)指定的属性,不能为空 checkRequiredFields(mpvs); //为方法 Model 绑定属性值 applyPropertyValues(mpvs); }
protected void checkAllowedFields(MutablePropertyValues mpvs) { PropertyValue[] pvs = mpvs.getPropertyValues(); for (PropertyValue pv : pvs) { //canonicalPropertyName方法作用如下: //abc => abc //['abc'] => [abc] //[''abc''] => ['abc'] //["abc"] => [abc] String field = PropertyAccessorUtils.canonicalPropertyName(pv.getName()); if (!isAllowed(field)) { mpvs.removePropertyValue(pv); getBindingResult().recordSuppressedField(field); if (logger.isDebugEnabled()) { logger.debug("Field [" + field + "] has been removed from PropertyValues " + "and will not be bound, because it has not been found in the list of allowed fields"); } } } }
protected boolean isAllowed(String field) { //获取dataBinder的allowedFields 属性值 String[] allowed = getAllowedFields(); //获取dataBinder的disallowedFields 属性值 String[] disallowed = getDisallowedFields(); return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) && (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field))); }
上述代码的意思总结一下
- allowed为空,disallowed为空,返回 true
- allowed不为空,属性被allowed匹配到,disallowed为空,返回 true
- allowed不为空,属性被没有被allowed匹配到,disallowed为空,返回 false
- allowed为空,disallowed不为空,属性被disallowed匹配到,返回 false
- allowed为空,disallowed不为空,属性没有被disallowed匹配到,返回 true
允许将属性值设置到目标对象上的活页夹,包括对验证和绑定结果分析的支持。可以通过指定允许的字段,必填字段,自定义编辑器等来自定义绑定过程。
请注意,如果无法设置允许字段的数组,可能会带来安全隐患。例如,在使用HTTP表单POST数据的情况下,恶意客户端可以通过提供表单上不存在的字段或属性的值来尝试破坏应用程序。在某些情况下,这可能会导致在命令对象或其嵌套对象上设置非法数据。因此,强烈建议allowedFields
在DataBinder上指定 属性 。如何使用呢?请看下面例子。
@InitBinder public void setAllowedFields(WebDataBinder dataBinder) { dataBinder.setDisallowedFields("id"); dataBinder.setAllowedFields("username"); }
DataBinder.java
protected void applyPropertyValues(MutablePropertyValues mpvs) { try { //将请求参数绑定到目标对象上 // IgnoreUnknownFields:忽略在Bean中找不到的属性 // IgnoreInvalidFields:忽略找到,但是没有访问权限的值 getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields()); } catch (PropertyBatchUpdateException ex) { //使用错误处理器绑定属性绑定错误信息 for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) { getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult()); } } }
getPropertyAccessor(),获取一个属性访问器
可以看到,PropertyAccessor(也就是我们所说的属性访问器)只有两个实现类
- 第一个,BeanWrapperImpl
- 第二个,DirectFieldAccessor
那么这两个有什么区别呢?第一个我们已经知道了,它是基于内省来实现的,所以BeanWrapperImpl肯定是基于getter,setter方法来实现对属性的操作的。第二个从名字上我们可以猜测,它估计是直接通过反射来获取字段的,也就是说,不需要提供setter/getter方法。大家可以自行做个测试,这里我就直接给结论了
- BeanWrapperImpl,基于内省,依赖getter/setter方法* DirectFieldAccessor,基于反射,不需要提供getter/setter方法
那么接下来,我们思考一个问题,DataBinder中的getPropertyAccessor()访问的是哪种类型的属性访问器呢?其实结合我们之前那个使用的示例就很容易知道,它肯定返回的是一个基于内省机制实现的属性访问器,并且它就是返回了一个BeanWrapperImpl。代码如下:
DataBinder.java
protected ConfigurablePropertyAccessor getPropertyAccessor() { return getInternalBindingResult().getPropertyAccessor(); } //1.获取一个属性访问器,可以看到,是通过getInternalBindingResult()方法返回的一个对象来获取的 // 那么getInternalBindingResult()做了什么呢? protected AbstractPropertyBindingResult getInternalBindingResult() { if (this.bindingResult == null) { initBeanPropertyAccess(); } return this.bindingResult; } // 2.getInternalBindingResult()又调用了一个initBeanPropertyAccess(),从名字上来看,就是用来初始化属性访问器的,再看看这个方法干了啥 public void initBeanPropertyAccess() { Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods"); this.bindingResult = createBeanPropertyBindingResult(); } // 3.调用了一个createBeanPropertyBindingResult,创建了一个对象,也就是通过创建的这个对象返回了一个属性访问器,那么这个对象是什么呢?接着往下看 protected AbstractPropertyBindingResult createBeanPropertyBindingResult() { BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); if (this.conversionService != null) { result.initConversion(this.conversionService); } return result; } // 4.可以发现创建的这个对象就是一个BeanPropertyBindingResult public void initConversion(ConversionService conversionService) { Assert.notNull(conversionService, "ConversionService must not be null"); this.conversionService = conversionService; if (getTarget() != null) { getPropertyAccessor().setConversionService(conversionService); } }
BeanPropertyBindingResult.java
//5.跟踪这个对象的getPropertyAccessor()方法,发现就是返回了一个beanWrapper现在明朗了吧,dataBinder最终也是依赖于beanWrapper public final ConfigurablePropertyAccessor getPropertyAccessor() { if (this.beanWrapper == null) { this.beanWrapper = createBeanWrapper(); this.beanWrapper.setExtractOldValueForEditor(true); this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths); this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit); } return this.beanWrapper; } protected BeanWrapper createBeanWrapper() { Assert.state(this.target != null, "Cannot access properties on null bean instance '" + getObjectName() + "'!"); return PropertyAccessorFactory.forBeanPropertyAccess(this.target); }
PropertyAccessorFactory.java
public static BeanWrapper forBeanPropertyAccess(Object target) { return new BeanWrapperImpl(target); }
我们可以思考一个问题,为什么Spring在实现数据绑定的时候不采用DirectFieldAccessor而是BeanWrapperImpl呢?换言之,为什么不直接使用反射而使用内省呢?
我个人的理解是:反射容易打破Bean的封装性,基于内省更安全。Spring在很多地方都不推荐使用反射的方式,比如我们在使用@Autowired注解进行字段注入的时候,编译器也会提示,”Field injection is not recommended “,不推荐我们使用字段注入,最好将@Autowired添加到setter方法上。
AbstractPropertyAccessor.java
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException { List<PropertyAccessException> propertyAccessExceptions = null; List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); for (PropertyValue pv : propertyValues) { try { //这里只对简单的异常进行捕获,对于严重的异常,还是会抛出的 setPropertyValue(pv); } catch (NotWritablePropertyException ex) { //是否抛出异常可以通过 DataBinder的ignoreUnknownFields属性配置 if (!ignoreUnknown) { throw ex; } } catch (NullValueInNestedPathException ex) { //是否抛出异常可以通过 DataBinder的ignoreInvalidFields属性配置 if (!ignoreInvalid) { throw ex; } } catch (PropertyAccessException ex) { if (propertyAccessExceptions == null) { propertyAccessExceptions = new LinkedList<PropertyAccessException>(); } propertyAccessExceptions.add(ex); } } if (propertyAccessExceptions != null) { PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]); throw new PropertyBatchUpdateException(paeArray); } }
AbstractNestablePropertyAccessor.java
public void setPropertyValue(PropertyValue pv) throws BeansException { PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens; if (tokens == null) { String propertyName = pv.getName(); AbstractNestablePropertyAccessor nestedPa; try { // 这里是为了解决嵌套属性的情况,比如一个person对象中,包含一个dog对象,dog对象中有一个name属性 // 那么我们可以通过dog.name这种方式来将一个名字直接绑定到person中的dog上 // 与此同时,我们不能再使用person的属性访问器了,因为使用dog的属性访问器,这里就是返回dog的属性访问器 nestedPa = getPropertyAccessorForPropertyPath(propertyName); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", ex); } // PropertyTokenHolder是什么呢?例如我们的Person对象中有一个List name的属性, // 那么我们在绑定时,需要对List中的元素进行赋值,所有我们会使用name[0],name[1]这种方式来进行绑定, // 而PropertyTokenHolder中有三个属性,其中actualName代表name,canonicalName代表整个表达式name[0],而key则代表0这个下标位置 // getFinalPath方法主要是截取.后面的字符串,如 names[0].dogName,则截取到 dogName tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); if (nestedPa == this) { pv.getOriginalPropertyValue().resolvedTokens = tokens; } nestedPa.setPropertyValue(tokens, pv); } else { setPropertyValue(tokens, pv); } }
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) { //获取字符串中第一个有.的位置 //如dog.dogName,.所在的索引位置为3 //names[0].dogName,. 所在的索引位置为 8 int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath); if (pos > -1) { //获取点之前的字符串,如 dog.dogName //nestedProperty为 dog //nestedPath为 dogName String nestedProperty = propertyPath.substring(0, pos); String nestedPath = propertyPath.substring(pos + 1); //获取子属性访问器 AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty); //递归调用 return nestedPa.getPropertyAccessorForPropertyPath(nestedPath); } else { return this; } }
PropertyAccessorUtils.java
public static int getFirstNestedPropertySeparatorIndex(String propertyPath) { return getNestedPropertySeparatorIndex(propertyPath, false); }
private static int getNestedPropertySeparatorIndex(String propertyPath, boolean last) { boolean inKey = false; int length = propertyPath.length(); int i = (last ? length - 1 : 0); while (last ? i >= 0 : i < length) { switch (propertyPath.charAt(i)) { case '[': case ']': inKey = !inKey; break; case '.': if (!inKey) { return i; } } if (last) { i--; } else { i++; } } return -1; }
AbstractNestablePropertyAccessor.java
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) { if (this.nestedPropertyAccessors == null) { this.nestedPropertyAccessors = new HashMap<String, AbstractNestablePropertyAccessor>(); } //创建PropertyTokenHolder对象 PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty); String canonicalName = tokens.canonicalName; Object value = getPropertyValue(tokens); if (value == null || (value.getClass().equals(javaUtilOptionalClass) && OptionalUnwrapper.isEmpty(value))) { //如果允许自动增长嵌套路径 if (isAutoGrowNestedPaths()) { value = setDefaultValue(tokens); } else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName); } } //查找缓存的子属性访问器,如果找不到,则创建一个新的子属性访问器。 AbstractNestablePropertyAccessor nestedPa = this.nestedPropertyAccessors.get(canonicalName); if (nestedPa == null || nestedPa.getWrappedInstance() != (value.getClass().equals(javaUtilOptionalClass) ? OptionalUnwrapper.unwrap(value) : value)) { if (logger.isTraceEnabled()) { logger.trace("Creating new nested " + getClass().getSimpleName() + " for property '" + canonicalName + "'"); } //创建子属性访问器 nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + .); //继承所有父类的属性编辑器 copyDefaultEditorsTo(nestedPa); copyCustomEditorsTo(nestedPa, canonicalName); this.nestedPropertyAccessors.put(canonicalName, nestedPa); } else { if (logger.isTraceEnabled()) { logger.trace("Using cached nested property accessor for property '" + canonicalName + "'"); } } return nestedPa; }
// propertyName,canonicalName,actualName,keys
//names[0][1],names[0][1],names,names,[0, 1]
//names[0],names[0],names[0],names,[0]
//names[‘zhangsan’],names[zhangsan],names,[zhangsan]
//names[’“zhangsan”’],names[“zhangsan”],names,[“zhangsan”]
//names[’‘zhangsan’’],names[‘zhangsan’],names,[‘zhangsan’]
private PropertyTokenHolder getPropertyNameTokens(String propertyName) { PropertyTokenHolder tokens = new PropertyTokenHolder(); String actualName = null; List<String> keys = new ArrayList<String>(2); int searchIndex = 0; while (searchIndex != -1) { int keyStart = propertyName.indexOf("[", searchIndex); searchIndex = -1; if (keyStart != -1) { int keyEnd = propertyName.indexOf("]", keyStart + "[".length()); if (keyEnd != -1) { if (actualName == null) { actualName = propertyName.substring(0, keyStart); } String key = propertyName.substring(keyStart + "[".length(), keyEnd); if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) { key = key.substring(1, key.length() - 1); } keys.add(key); searchIndex = keyEnd + "]".length(); } } } tokens.actualName = (actualName != null ? actualName : propertyName); tokens.canonicalName = tokens.actualName; if (!keys.isEmpty()) { tokens.canonicalName += "[" + StringUtils.collectionToDelimitedString(keys, "]" + "[") + "]"; tokens.keys = StringUtils.toStringArray(keys); } return tokens; }
protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; //根据属性的真实名称到实体对象中找 //如 url 参数为http:xxx?names[0][1],那么到User对象中必需有一个属性名为names 的属性 //并且有权限访问,这里一般的有 get 方法,就是有权限访问。 //如果没有属性名或者没有权限访问,将抛出NotReadablePropertyException异常 PropertyHandler ph = getLocalPropertyHandler(actualName); if (ph == null || !ph.isReadable()) { throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName); } try { //调用get方法获值 Object value = ph.getValue(); if (tokens.keys != null) { if (value == null) { //允许自动增长嵌套路径 if (isAutoGrowNestedPaths()) { //为属设置默认值 value = setDefaultValue(tokens.actualName); } else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value of property referenced in indexed " + "property path '" + propertyName + "': returned null"); } } String indexedPropertyName = tokens.actualName; for (int i = 0; i < tokens.keys.length; i++) { String key = tokens.keys[i]; if (value == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value of property referenced in indexed " + "property path '" + propertyName + "': returned null"); } //如果是数组类型 else if (value.getClass().isArray()) { int index = Integer.parseInt(key); value = growArrayIfNecessary(value, index, indexedPropertyName); value = Array.get(value, index); } //如果是 List 类型 else if (value instanceof List) { int index = Integer.parseInt(key); List<Object> list = (List<Object>) value; //扩容数组 growCollectionIfNecessary(list, index, indexedPropertyName, ph, i + 1); value = list.get(index); } //如果是 Set 类型 else if (value instanceof Set) { //如果是Set,则将索引应用于Iterator。 Set<Object> set = (Set<Object>) value; int index = Integer.parseInt(key); if (index < 0 || index >= set.size()) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot get element with index " + index + " from Set of size " + set.size() + ", accessed using property path '" + propertyName + "'"); } Iterator<Object> it = set.iterator(); for (int j = 0; it.hasNext(); j++) { Object elem = it.next(); if (j == index) { value = elem; break; } } } //如果是 Map 类型 else if (value instanceof Map) { Map<Object, Object> map = (Map<Object, Object>) value; //获取 Map key 类型 Class<?> mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(0); //获取 Map key 的类型描述器 TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType); Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); value = map.get(convertedMapKey); } else { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + "' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]"); } indexedPropertyName += "[" + key + "]"; } } return value; } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Index of out of bounds in property path '" + propertyName + "'", ex); } catch (NumberFormatException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Invalid index in property path '" + propertyName + "'", ex); } catch (TypeMismatchException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Invalid index in property path '" + propertyName + "'", ex); } catch (InvocationTargetException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Getter for property '" + actualName + "' threw exception", ex); } catch (Exception ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Illegal attempt to get property '" + actualName + "' threw exception", ex); } }
AbstractNestablePropertyAccessor.java
private Object setDefaultValue(String propertyName) { PropertyTokenHolder tokens = new PropertyTokenHolder(); tokens.actualName = propertyName; tokens.canonicalName = propertyName; return setDefaultValue(tokens); } private Object setDefaultValue(PropertyTokenHolder tokens) { PropertyValue pv = createDefaultPropertyValue(tokens); //设置属性的默认类型 setPropertyValue(tokens, pv); //获取属性值 return getPropertyValue(tokens); }
我相信很多的读者代码跟进到这里己经被绕晕了,点击setPropertyValue,getPropertyValue方法,怎么又回到了刚刚的一大段代码,不知道聪明的读者有没有发现,在上述方法中的PropertyTokenHolder对象的 keys 属性为 null,发现这个以后,我相信读者再来理解就容易了,因为 keys 为空,因此,在setPropertyValue,getPropertyValue方法并没有复杂的逻辑,setPropertyValue方法只是将创建属性的默认值调用 set 方法将给属性赋值,调用getPropertyValue方法也只是调用 get 方法将刚刚赋值的属性获取出来。
private PropertyValue createDefaultPropertyValue(PropertyTokenHolder tokens) { TypeDescriptor desc = getPropertyTypeDescriptor(tokens.canonicalName); //获取属性类型 Class<?> type = desc.getType(); if (type == null) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + tokens.canonicalName, "Could not determine property type for auto-growing a default value"); } //创建默认值 Object defaultValue = newValue(type, desc, tokens.canonicalName); return new PropertyValue(tokens.canonicalName, defaultValue); } private Object newValue(Class<?> type, TypeDescriptor desc, String name) { try { //如果是数组类型,创建数组 if (type.isArray()) { Class<?> componentType = type.getComponentType(); //如果属性类型是二维数组类型 if (componentType.isArray()) { Object array = Array.newInstance(componentType, 1); Array.set(array, 0, Array.newInstance(componentType.getComponentType(), 0)); return array; } //属性类型是一维数组 else { return Array.newInstance(componentType, 0); } } //如果属性类型是Collection类型及子类型 else if (Collection.class.isAssignableFrom(type)) { TypeDescriptor elementDesc = (desc != null ? desc.getElementTypeDescriptor() : null); return CollectionFactory.createCollection(type, (elementDesc != null ? elementDesc.getType() : null), 16); } //如果属性类型是Map类型 else if (Map.class.isAssignableFrom(type)) { TypeDescriptor keyDesc = (desc != null ? desc.getMapKeyTypeDescriptor() : null); return CollectionFactory.createMap(type, (keyDesc != null ? keyDesc.getType() : null), 16); } else { //如果属性类型是对象类型 return BeanUtils.instantiate(type); } } catch (Exception ex) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex); } }
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; if (tokens.keys != null) { //复制一个PropertyTokenHolder对象 PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; getterTokens.keys = new String[tokens.keys.length - 1]; System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); Object propValue; try { //调用实例的 get 方法获取属性值,如果没有,则创建一个 propValue = getPropertyValue(getterTokens); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "'", ex); } String key = tokens.keys[tokens.keys.length - 1]; if (propValue == null) { //null map value case //什么时候会出现下面的情况呢? //实体属性为private Map<String, Map<Integer,Double>> userInfos; //请求为 http://xxx?userInfos[zhangsan][1]=2.0 //out put: {"userInfos":{"zhangsan":{1:2.0}}} if (isAutoGrowNestedPaths()) { int lastKeyIndex = tokens.canonicalName.lastIndexOf('['); getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex); //设置userInfos[zhangsan]的默认值为 LinkedHashMap propValue = setDefaultValue(getterTokens); } else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + "in indexed property path '" + propertyName + "': returned null"); } } //如果属性是数组类型 if (propValue.getClass().isArray()) { PropertyHandler ph = getLocalPropertyHandler(actualName); Class<?> requiredType = propValue.getClass().getComponentType(); //获取数组下标 int arrayIndex = Integer.parseInt(key); Object oldValue = null; try { if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) { oldValue = Array.get(propValue, arrayIndex); } Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, ph.nested(tokens.keys.length)); int length = Array.getLength(propValue); //如果数组下标大于数组长度 if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) { Class<?> componentType = propValue.getClass().getComponentType(); //扩容数组 Object newArray = Array.newInstance(componentType, arrayIndex + 1); System.arraycopy(propValue, 0, newArray, 0, length); //重新为对象的属性值值 setPropertyValue(actualName, newArray); propValue = getPropertyValue(actualName); } Array.set(propValue, arrayIndex, convertedValue); } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Invalid array index in property path '" + propertyName + "'", ex); } } else if (propValue instanceof List) { PropertyHandler ph = getPropertyHandler(actualName); Class<?> requiredType = ph.getCollectionType(tokens.keys.length); List<Object> list = (List<Object>) propValue; int index = Integer.parseInt(key); Object oldValue = null; if (isExtractOldValueForEditor() && index < list.size()) { oldValue = list.get(index); } Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, ph.nested(tokens.keys.length)); int size = list.size(); if (index >= size && index < this.autoGrowCollectionLimit) { //如果index 大于 size,初始化 list for (int i = size; i < index; i++) { try { list.add(null); } catch (NullPointerException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot set element with index " + index + " in List of size " + size + ", accessed using property path '" + propertyName + "': List does not support filling up gaps with null elements"); } } list.add(convertedValue); } else { try { list.set(index, convertedValue); } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Invalid list index in property path '" + propertyName + "'", ex); } } } else if (propValue instanceof Map) { PropertyHandler ph = getLocalPropertyHandler(actualName); //Map<String, Map<Integer,Double>> userInfos //mapKeyType 为 Integer类型 //mapValueType为 Double 类型 Class<?> mapKeyType = ph.getMapKeyType(tokens.keys.length); Class<?> mapValueType = ph.getMapValueType(tokens.keys.length); Map<Object, Object> map = (Map<Object, Object>) propValue; TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType); Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor); Object oldValue = null; if (isExtractOldValueForEditor()) { oldValue = map.get(convertedMapKey); } Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), mapValueType, ph.nested(tokens.keys.length)); map.put(convertedMapKey, convertedMapValue); } else { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]"); } } else { //获取属性处理器 PropertyHandler ph = getLocalPropertyHandler(actualName); if (ph == null || !ph.isWritable()) { if (pv.isOptional()) { if (logger.isDebugEnabled()) { logger.debug("Ignoring optional value for property '" + actualName + "' - property not found on bean class [" + getRootClass().getName() + "]"); } return; } else { //如果属性处理器为空或者没有 set 方法,将抛出异常 throw createNotWritablePropertyException(propertyName); } } Object oldValue = null; try { Object originalValue = pv.getValue(); Object valueToApply = originalValue; //conversionNecessary默认为 null if (!Boolean.FALSE.equals(pv.conversionNecessary)) { //如果属性己经被转换过了,直接返回转换后的值 if (pv.isConverted()) { valueToApply = pv.getConvertedValue(); } else { //isExtractOldValueForEditor方法默认返回值为 true //isReadable方法表示实体属性是否有 getXXX()方法 if (isExtractOldValueForEditor() && ph.isReadable()) { try { //调用 get 方法,获取属性值 oldValue = ph.getValue(); } catch (Exception ex) { if (ex instanceof PrivilegedActionException) { ex = ((PrivilegedActionException) ex).getException(); } if (logger.isDebugEnabled()) { logger.debug("Could not read previous value of property '" + this.nestedPath + propertyName + "'", ex); } } } //将请求参数转化为实体属性对应类型,例如: //user对象有一个属性 age 是 Interger类型,但是,请求参数 age 是 String 类型 //在下面的这个方法中,将 String类型转化成Interger类型,内部的实现 //逻辑也很复杂,感兴趣的小伙伴可以跟进去看看 valueToApply = convertForProperty( propertyName, oldValue, originalValue, ph.toTypeDescriptor()); } //将pv 中的数据转化为实体属性对应的类型 pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } //反射调用实体属性的 set方法为其属性赋值 ph.setValue(object, valueToApply); } catch (TypeMismatchException ex) { throw ex; } catch (InvocationTargetException ex) { PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue()); if (ex.getTargetException() instanceof ClassCastException) { throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException()); } else { Throwable cause = ex.getTargetException(); if (cause instanceof UndeclaredThrowableException) { // May happen e.g. with Groovy-generated methods cause = cause.getCause(); } throw new MethodInvocationException(propertyChangeEvent, cause); } } catch (Exception ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue()); throw new MethodInvocationException(pce, ex); } } }
BeanWrapperImpl.java
public void setValue(final Object object, Object valueToApply) throws Exception { final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() : this.pd.getWriteMethod()); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { writeMethod.setAccessible(true); return null; } }); } else { writeMethod.setAccessible(true); } } final Object value = valueToApply; if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { writeMethod.invoke(object, value); return null; } }, acc); } catch (PrivilegedActionException ex) { throw ex.getException(); } } else { //获取实体属性的 set 方法,为其属性赋值 writeMethod.invoke(getWrappedInstance(), value); } }
代码看到这里,我相信如果没有亲身调试的话,很多的概念还是模糊的,但是你不要担心,我们来看一个实例。
1.准备实体
@Data public class Person { private List<String> name; private Dog dog; private Integer age; private String[] catNames; private Map<String, Object> computeMaps; private List<Shoes> shoesList; private String bs[][]; private String cs[][][]; private Map<String, Map<Integer,Double>> userInfos; private Map<String, Map<Integer,Map<Long,Double>>> logs; } @Data public class Shoes { private String brand; private String color; } @Data public class Dog { String dogName; }
2.测试
//https://blog.csdn.net/qq_41907991/article/details/105333258 public class Main { private Person person = new Person(); private DataBinder binder = new DataBinder(person, "person"); private MutablePropertyValues pvs = new MutablePropertyValues(); @Test public void test0() { //{"age":18} pvs.add("age", 18); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test() { //{"dog":{"dogName":"dawang"}} pvs.add("dog.dogName", "dawang"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test1() { //{"name":["dmz0","dmz1"]} pvs.add("name[0]", "dmz0"); pvs.add("name[1]", "dmz1"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test2() { //{"catNames":["dmz0","dmz1"]} pvs.add("catNames[0]", "dmz0"); pvs.add("catNames[1]", "dmz1"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test3() { //{"computeMaps":{"zhangsan":"dmz0","wangwu":"dmz1"}} pvs.add("computeMaps[zhangsan]", "dmz0"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test3_1() { pvs.add("userInfos[zhangsan][1]", "2.0"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test3_2() { pvs.add("logs[zhangsan][1][2]", "3"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test4() { //{"shoesList":[{"brand":"耐克"},{"color":"红色"}]} pvs.add("shoesList[0].brand", "耐克"); pvs.add("shoesList[1].color", "红色"); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test5() { String [][] c = {{"a"}}; pvs.add("bs[0][0]", c); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } @Test public void test6 (){ String[][]b = new String[2][2]; String [] c = {"a"}; Array.set(b, 1, c); } @Test public void test7() { String [][][] c = {{{"a"}}}; pvs.add("cs[0][0][0]", c); binder.bind(pvs); System.out.println(JSON.toJSONString(person)); } }
有兴趣的同学可以自行去调试一下,那我们对ServletModelAttributeMethodProcessor属性封装代码的解析又告一段落了,接下来,我们来分析RequestBody注解的属性解析器RequestResponseBodyMethodProcessor。
同前面分析一样,先来看RequestResponseBodyMethodProcessor的supportsParameter方法。
RequestResponseBodyMethodProcessor.java
public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); }
从上述方法可以看出,只要方法参数配置了RequestBody注解的,都会用这个处理器来为属性赋值。先来看一个例子吧
@RequestMapping(value = {"/detail"}) public String detail(@RequestBody User user) { System.out.println(JSON.toJSONString(user)); return "registersuccess"; }
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); //创建WebDataBinder WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); if (arg != null) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } mavContainer.addAttribute(BindingResult.class.getName() + "." + name, binder.getBindingResult()); return arg; }
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); Object arg = null; arg = readWithMessageConverters(inputMessage, methodParam, paramType); if (arg == null) { if (methodParam.getParameterAnnotation(RequestBody.class).required()) { throw new HttpMessageNotReadableException("Required request body is missing: " + methodParam.getMethod().toGenericString()); } } return arg; }
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter param, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { MediaType contentType; try { //获取请求头中的contentType contentType = inputMessage.getHeaders().getContentType(); } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } if (contentType == null) { contentType = MediaType.APPLICATION_OCTET_STREAM; } Class<?> contextClass = (param != null ? param.getContainingClass() : null); Class<T> targetClass = (targetType instanceof Class<?> ? (Class<T>) targetType : null); if (targetClass == null) { ResolvableType resolvableType = (param != null ? ResolvableType.forMethodParameter(param) : ResolvableType.forType(targetType)); targetClass = (Class<T>) resolvableType.resolve(); } HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod(); inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage); Object body = NO_VALUE; for (HttpMessageConverter<?> converter : this.messageConverters) { Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass(); if (converter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter; if (genericConverter.canRead(targetType, contextClass, contentType)) { if (logger.isDebugEnabled()) { logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]"); } if (inputMessage.getBody() != null) { //getAdvice方法返回的默认值是RequestResponseBodyAdviceChain inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType); //targetType: com.spring_101_200.test_181_190.test_185_spring_mvc.User 类型 //contextClass: com.spring_101_200.test_181_190.test_185_spring_mvc.UserController 类型 //inputMessage 是EmptyBodyCheckingHttpInputMessage //关于内部是如何将 request中的内容封装成 User 对象,这个有兴趣小伙伴可以自行研究一下,这里就不再深入了。 body = genericConverter.read(targetType, contextClass, inputMessage); body = getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType); } else { body = null; body = getAdvice().handleEmptyBody(body, inputMessage, param, targetType, converterType); } break; } } else if (targetClass != null) { if (converter.canRead(targetClass, contentType)) { if (logger.isDebugEnabled()) { logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]"); } if (inputMessage.getBody() != null) { inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType); body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage); //上面己经分析了beforeBodyRead方法了,这里afterBodyRead方法的原理,就不赘述了 body = getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType); } else { body = null; body = getAdvice().handleEmptyBody(body, inputMessage, param, targetType, converterType); } break; } } } if (body == NO_VALUE) { if (!SUPPORTED_METHODS.contains(httpMethod)) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); } return body; }
上面需要注意的是messageConverters是从哪里来的,我们回头来看<mvc:annotation-driven ></mvc:annotation-driven>标签解析,其中里面有一个getMessageConverters方法,创建了所有的MessageConverter,并将messageConverters注入到RequestMappingHandlerAdapter的messageConvertersn属性中。那么我们抽其中一个MessageConverter来分析。如MappingJackson2HttpMessageConverter
我们先来分析canRead方法
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) { JavaType javaType = getJavaType(type, contextClass); if (!jackson23Available || !logger.isWarnEnabled()) { return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType)); } AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>(); //对象能被反序列化,并且是支持contentType类型 if (this.objectMapper.canDeserialize(javaType, causeRef) && canRead(mediaType)) { return true; } Throwable cause = causeRef.get(); if (cause != null) { String msg = "Failed to evaluate deserialization for type " + javaType; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } else { logger.warn(msg + ": " + cause); } } return false; }
从测试例子我们知道,mediaType是application/json 类型的。而MappingJackson2HttpMessageConverter的构造方法中可以看到是支持application/json类型。
MappingJackson2HttpMessageConverter.java
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) { super(objectMapper, new MediaType("application", "json", DEFAULT_CHARSET), new MediaType("application", "*+json", DEFAULT_CHARSET)); }
AbstractJackson2HttpMessageConverter.java
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) { super(supportedMediaTypes); this.objectMapper = objectMapper; }
AbstractGenericHttpMessageConverter.java
protected AbstractGenericHttpMessageConverter(MediaType... supportedMediaTypes) { super(supportedMediaTypes); }
AbstractHttpMessageConverter.java
protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) { setSupportedMediaTypes(Arrays.asList(supportedMediaTypes)); }
AbstractHttpMessageConverter.java
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) { Assert.notEmpty(supportedMediaTypes, "'supportedMediaTypes' must not be empty"); this.supportedMediaTypes = new ArrayList<MediaType>(supportedMediaTypes); }
从上述一系列分析中,我们可以得知supportedMediaTypes中有 application/json 和 application/*+json的
AbstractHttpMessageConverter.java
protected boolean canRead(MediaType mediaType) { if (mediaType == null) { return true; } for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.includes(mediaType)) { return true; } } return false; } public List<MediaType> getSupportedMediaTypes() { return Collections.unmodifiableList(this.supportedMediaTypes); }
对于RequestResponseBodyMethodProcessor处理器,还有两个重要方法我们要分析一下,beforeBodyRead和afterBodyRead,在分析方法之前我们先来看一个例子:
1.添加一个RequestBodyAdvice
@ControllerAdvice public class LogRequestBodyAdvice implements RequestBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return true; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException { System.out.println("=======================beforeBodyRead======================="); return httpInputMessage; } @Override public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { Method method = methodParameter.getMethod(); System.out.println("=======================afterBodyRead======================="+method.getDeclaringClass().getSimpleName() + "." + method.getName() + ":" +JSON.toJSONString(o)); return o; } @Override public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { Method method = methodParameter.getMethod(); System.out.println("=======================handleEmptyBody======================="+method.getDeclaringClass().getSimpleName() + "." + method.getName()); return o; } }
2.测试
3.结果
AbstractMessageConverterMethodArgumentResolver.java
protected RequestResponseBodyAdviceChain getAdvice() { return this.advice; } public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice) { Assert.notEmpty(converters, "'messageConverters' must not be empty"); this.messageConverters = converters; this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters); this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice); }
RequestResponseBodyAdviceChain.java
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) { if (advice.supports(parameter, targetType, converterType)) { request = advice.beforeBodyRead(request, parameter, targetType, converterType); } } return request; }
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) { List<Object> availableAdvice = getAdvice(adviceType); if (CollectionUtils.isEmpty(availableAdvice)) { return Collections.emptyList(); } List<A> result = new ArrayList<A>(availableAdvice.size()); for (Object advice : availableAdvice) { if (advice instanceof ControllerAdviceBean) { ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice; if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) { continue; } advice = adviceBean.resolveBean(); } if (adviceType.isAssignableFrom(advice.getClass())) { result.add((A) advice); } } return result; }
private List<Object> getAdvice(Class<?> adviceType) { if (RequestBodyAdvice.class == adviceType) { return this.requestBodyAdvice; } else if (ResponseBodyAdvice.class == adviceType) { return this.responseBodyAdvice; } else { throw new IllegalArgumentException("Unexpected adviceType: " + adviceType); } }
代码跟踪到这里,感觉线索断了,我们可以根据requestBodyAdvice和responseBodyAdvice在哪里赋值来查找requestBodyAdvice的来源。
RequestResponseBodyAdviceChain.java
public RequestResponseBodyAdviceChain(List<Object> requestResponseBodyAdvice) { initAdvice(requestResponseBodyAdvice); } private void initAdvice(List<Object> requestResponseBodyAdvice) { if (requestResponseBodyAdvice == null) { return; } for (Object advice : requestResponseBodyAdvice) { Class<?> beanType = (advice instanceof ControllerAdviceBean ? ((ControllerAdviceBean) advice).getBeanType() : advice.getClass()); if (RequestBodyAdvice.class.isAssignableFrom(beanType)) { this.requestBodyAdvice.add(advice); } else if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) { this.responseBodyAdvice.add(advice); } } }
我们还是先来看RequestMappingHandlerAdapter生命周期中的afterPropertiesSet方法。在这个方法中有一个initControllerAdviceCache方法,在这个方法中有一段这样的代码。
//从容器中获取所有配置了ControllerAdviceBean注解的 bean List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>(); for (ControllerAdviceBean bean : beans) { ... //如果bean实现了RequestBodyAdvice或ResponseBodyAdvice接口 //保存到requestResponseBodyAdviceBeans中 if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) { requestResponseBodyAdviceBeans.add(bean); logger.info("Detected RequestBodyAdvice bean in " + bean); } if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) { requestResponseBodyAdviceBeans.add(bean); logger.info("Detected ResponseBodyAdvice bean in " + bean); } } if (!requestResponseBodyAdviceBeans.isEmpty()) { this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); }
从上面代码中我们知道了实现了RequestBodyAdvice接口或ResponseBodyAdvice并且配置了ControllerAdvice注解的 bean被保存到requestResponseBodyAdviceBeans中,如果requestResponseBodyAdviceBeans不为空,则将requestResponseBodyAdviceBeans的值添加到requestResponseBodyAdvice中。接下来,我们继续看另外一个方法getDefaultArgumentResolvers,在这个方法中有如下代码。
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); ... resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); ... return resolvers; }
先看一下RequestResponseBodyMethodProcessor的类关系结构。
RequestResponseBodyMethodProcessor.java
public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice) { super(converters, null, requestResponseBodyAdvice); }
AbstractMessageConverterMethodProcessor.java
protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>> converters, ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) { super(converters, requestResponseBodyAdvice); this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager()); }
通过上述一系列的分析,我们知道了requestBodyAdvice和responseBodyAdvice属性来源于RequestMappingHandlerAdapter的afterPropertiesSet方法初始化的参数requestResponseBodyAdvice,代码跟进到这里,我相信大家对RequestResponseBodyMethodProcessor对方法参数的封装己经了解了。
我们分析了至少三个请求方法参数的解析器,那么,我们如何自定义一个方法参数解析器呢?
需求
我们在使用RequestBody注解时,只能接收一个对象,如果前端同时传递两个并列对象过来,这种情况怎么办呢?如:{“user”:{“username”:“zhangsan”,“password”:“123456”},“userInfo”:{“age”:1,“desc”:“xxxx”}},这种情况用 RequestBody 注解就解析不了,因此,我们来实现自定义方法参数解析器。
1.定义实体
@Data public class User { private String username; private String password; } @Data public class UserInfo implements Serializable { private int age; private String desc; }
2.添加MultiRequestBody注解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface MultiRequestBody { /** * 是否必须出现的参数 */ boolean required() default true; String requiredMessage() default "请求参数缺失"; /** * 当value的值或者参数名不匹配时,是否允许解析最外层属性到该对象 */ boolean parseAllFields() default true; /** * 解析时用到的JSON的key */ String value() default ""; }
3.定义属性解析器
/** * MultiRequestBody解析器 * 解决的问题: * 1、单个字符串等包装类型都要写一个对象才可以用@RequestBody接收; * 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。 * 主要优势: * 1、支持通过注解的value指定JSON的key来解析对象。 * 2、支持通过注解无value,直接根据参数名来解析对象 * 3、支持基本类型的注入 * 4、支持GET和其他请求方式注入 * 5、支持通过注解无value且参数名不匹配JSON串key时,根据属性解析对象。 * 6、支持多余属性(不解析、不报错)、支持参数“共用”(不指定value时,参数名不为JSON串的key) * 7、支持当value和属性名找不到匹配的key时,对象是否匹配所有属性。 */ public class MultiRequestBodyArgumentResolver implements HandlerMethodArgumentResolver { private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY"; private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 设置支持的方法参数类型 * @param parameter 方法参数 * @return 支持的类型 */ @Override public boolean supportsParameter(MethodParameter parameter) { // 支持带@MultiRequestBody注解的参数 return parameter.hasParameterAnnotation(MultiRequestBody.class); } /** * 参数解析,利用fastjson * 注意:非基本类型返回null会报空指针异常,要通过反射或者JSON工具类创建一个空对象 */ @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String jsonBody = getRequestBody(webRequest); Class<?> parameterType = parameter.getParameterType(); JSONObject jsonObject = JSON.parseObject(jsonBody); // 根据@MultiRequestBody注解value作为json解析的key MultiRequestBody parameterAnnotation = parameter.getParameterAnnotation(MultiRequestBody.class); //注解的value是JSON的key String key = parameterAnnotation.value(); Object value = null; // 注解为设置value则用参数名当做json的key key = parameter.getParameterName(); value = jsonObject.get(key); // 将 order_no 转化为 orderNo, 再次获取数据 if (value == null) { String k = MysqlUtil.field2JavaCode(key); value = jsonObject.get(k); } // 通过注解的value或者参数名解析,能拿到value进行解析 if (value != null) { //基本类型,int ,double if (parameterType.isPrimitive()) { System.out.println("这里对于基本数据类型的处理,有兴趣的同学自己封装"); return null; } //基本类型包装类 Integer ,Double 等 if (parameterType == Integer.class || parameterType == Character.class) { System.out.println("这里对于基类类型的包类型处理,有兴趣的同学自己封装"); return null; //字符串类型 } else if (parameterType == String.class) { return value.toString(); } // 其他复杂对象 return JSON.parseObject(value.toString(), parameterType); } return value; } private String getRequestBody(NativeWebRequest webRequest) { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); // 有就直接获取 String jsonBody = (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST); // 没有就从请求中读取 if (jsonBody == null) { try { jsonBody = IOUtils.toString(servletRequest.getReader()); webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST); } catch (IOException e) { throw new RuntimeException(e); } } if (StringUtils.isBlank(jsonBody)) { Map<String, String[]> readOnlyMap = servletRequest.getParameterMap(); Map writeAbleMap = new HashMap(); for (Map.Entry<String, String[]> entry : readOnlyMap.entrySet()) { String key = entry.getKey(); String value = ""; if (entry.getValue() != null) { if (entry.getValue().length == 1) { value = entry.getValue()[0]; } else { for (String v : entry.getValue()) { value = value + "," + v; } } } writeAbleMap.put(key, value); } jsonBody = JSON.toJSONString(writeAbleMap); } return jsonBody; } }
4.添加argumentResolver
@Configuration public class ResolverBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("============================"+beanName); if ("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0".equals(beanName)) { //requestMappingHandlerAdapter进行修改 RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean; List argumentResolvers = adapter.getArgumentResolvers(); //添加自定义参数处理器 List argumentResolverList = new ArrayList<>(); argumentResolverList.add(new MultiRequestBodyArgumentResolver()); argumentResolverList.addAll(argumentResolvers); adapter.setArgumentResolvers(argumentResolverList); } return bean; } }
5.定义Controller
@RequestMapping(value = "/multi/body", method = {RequestMethod.POST}) public String multiBody(@MultiRequestBody User user, @MultiRequestBody UserInfo userInfo) { System.out.println("user:" + JSON.toJSONString(user)); System.out.println("userInfo:" + JSON.toJSONString(userInfo)); return "loginout"; }
6.测试
7.结果
关于方法参数的封装,就到这里了。下面来总结一下参数封装的整个流程。
流程
1)parse():AnnotationDrivenBeanDefinitionParser里方法,解析<mvc:annotation-driven ></mvc:annotation-driven>标签 1)注册RequestMappingHandlerMapping 2)注册content-negotiation-manager内容协商 3)注册urlPathHelper和pathMatcher 4)注册跨域请求相关配置 5)注册conversion-service,默认是FormattingConversionServiceFactoryBean 6)注册validator,如果没有配置,则看是否能反射调用javax.validation.Validator,如果能,则注册OptionalValidatorFactoryBean 7)如果配置了message-codes-resolver,则注册相关 bean 8)注册ConfigurableWebBindingInitializer的bean 9)如果配置了message-converters,则注册相关的 bean 10)如果配置了register-defaults 1)注册ByteArrayHttpMessageConverter 2)注册StringHttpMessageConverter 3)注册ResourceHttpMessageConverter 4)注册SourceHttpMessageConverter 5)注册AllEncompassingFormHttpMessageConverter 6)如果能反射调用com.rometools.rome.feed.WireFeed 1)注册AtomFeedHttpMessageConverter 2)注册RssChannelHttpMessageConverter 7)如果能反射调用com.fasterxml.jackson.dataformat.xml.XmlMapper 1)注册MappingJackson2XmlHttpMessageConverter 8)如果能反射调用javax.xml.bind.Binder 1)注册Jaxb2RootElementHttpMessageConverter 9)如果能反射调用com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator 1)注册MappingJackson2HttpMessageConverter 10)如果能反射调用com.google.gson.Gson 1)注册GsonHttpMessageConverter 11)如果配置了argument-resolvers,注册下面所有的 bean元素到容器 12)如果配置了return-value-handlers,注册下面的所有 bean元素到容器 13)如果配置了async-support 1)设置default-timeout 默认超时时间 2)设置task-executor 3)注册callable-interceptors下的所有的 bean 4)注册deferred-result-interceptors下所有的 bean 14)注册RequestMappingHandlerAdapter 15)配置ignore-default-model-on-redirect或ignoreDefaultModelOnRedirect 16)注册CompositeUriComponentsContributorFactoryBean 17)注册ConversionServiceExposingInterceptor 18)注册MappedInterceptor 19)注册ExceptionHandlerExceptionResolver 20)注册ResponseStatusExceptionResolver 21)注册DefaultHandlerExceptionResolver 2)afterPropertiesSet():RequestMappingHandlerAdapter生命周期中的afterPropertiesSet方法 1)initControllerAdviceCache():初始化ControllerAdviceBean 1)modelAttributeAdviceCache:初始化ControllerAdviceBean中配置了ModelAttribute注解的方法 2)initBinderAdviceCache:初始化ControllerAdviceBean中配置了InitBinder注解方法 3)requestResponseBodyAdvice:初始化实现了RequestBodyAdvice和ResponseBodyAdvice的方法。 2)getDefaultArgumentResolvers():获取默认的参数解析器 1)RequestParamMethodArgumentResolver: 注册基本数据类型及RequestParam注解修饰的参数解析器 2)RequestParamMapMethodArgumentResolver:注册RequestParam 修饰并且参数类型是 Map参数解析器 3)PathVariableMethodArgumentResolver:注册PathVariable注解修饰方法参数解析器 4)PathVariableMapMethodArgumentResolver:注册PathVariable注册修饰,并且参数类型是 Map 的参数解析器 5)MatrixVariableMapMethodArgumentResolver:注册MatrixVariable注解修饰并且参数类型是Map的参数解析器 6)MatrixVariableMethodArgumentResolver:注册MatrixVariable注解修饰或MatrixVariable注解修饰并且MatrixVariable注解的名称不为空且参数类型是 Map的参数解析器 7)ServletModelAttributeMethodProcessor:参数被ModelAttribute修饰或不是基本数据类型(如自定义类型 user)的参数解析器 8)RequestResponseBodyMethodProcessor:参数被RequestBody修饰的参数解析器 9)RequestPartMethodArgumentResolver:参数被RequestPart注解修饰或参数类型是MultipartFile(文件上传的)参数解析器 10)RequestHeaderMethodArgumentResolver:参数被RequestHeader修饰的参数解析器 11)ServletCookieValueMethodArgumentResolver:参数被CookieValue注解修饰的参数解析器 12)ExpressionValueMethodArgumentResolver:注册参数被Value修饰的参数解析器 13)ServletRequestMethodArgumentResolver:注册参数类型是WebRequest,ServletRequest,MultipartRequest,Principal,HttpSession,TimeZone,Locale,InputStream,HttpMethod,Reader的参数解析器 14)ServletResponseMethodArgumentResolver:参数类型是ServletResponse,OutputStream,Writer的参数解析器 15)HttpEntityMethodProcessor:参数类型是HttpEntity,或RequestEntity的参数解析器 16)RedirectAttributesMethodArgumentResolver:参数类型是RedirectAttributes的参数解析器 17)ModelMethodProcessor:参数类型是Model的参数解析器 18)MapMethodProcessor:参数类型是Map的参数解析器 19)ErrorsMethodArgumentResolver:参数类型是Errors的参数解析器 20)SessionStatusMethodArgumentResolver:参数类型是SessionStatus的参数解析器 21)UriComponentsBuilderMethodArgumentResolver:参数类型是UriComponentsBuilder或ServletUriComponentsBuilder的参数解析器 22)getCustomArgumentResolvers():获取用户自定义的参数解析器 3)getDefaultInitBinderArgumentResolvers():注册InitBinder注解修饰方法的方法参数解析器 1)RequestParamMethodArgumentResolver:略 2)RequestParamMapMethodArgumentResolver:略 3)PathVariableMethodArgumentResolver:略 4)PathVariableMapMethodArgumentResolver:略 5)MatrixVariableMethodArgumentResolver:略 6)MatrixVariableMapMethodArgumentResolver:略 7)ExpressionValueMethodArgumentResolver:略 8)ServletRequestMethodArgumentResolver:略 9)ServletResponseMethodArgumentResolver:略 10)getCustomArgumentResolvers():自定义方法参数解析器 11)RequestParamMethodArgumentResolver:略 4)getDefaultReturnValueHandlers():注册默认的返回值解析器 1)ModelAndViewMethodReturnValueHandler:返回值类型是ModelAndView 2)ModelMethodProcessor:返回值类型是Model 3)ViewMethodReturnValueHandler:返回值类型是 View 类型 4)ResponseBodyEmitterReturnValueHandler:返回值类型是ResponseBodyEmitter,ResponseEntity,或ResponseBodyEmitter类型 5)StreamingResponseBodyReturnValueHandler:返回值类型是StreamingResponseBody类型 6)HttpEntityMethodProcessor:返回值类型是HttpEntity并且非RequestEntity类型 7)HttpHeadersReturnValueHandler:返回值类型是HttpHeaders 8)CallableMethodReturnValueHandler:返回值类型是Callable 9)DeferredResultMethodReturnValueHandler:返回值类型是DeferredResult 10)AsyncTaskMethodReturnValueHandler:返回值类型是WebAsyncTask 11)ListenableFutureReturnValueHandler:返回值类型是ListenableFuture 12)CompletionStageReturnValueHandler:返回值类型是CompletionStage 13)ModelAttributeMethodProcessor:方法被ModelAttribute修饰或不是基本数据类型 14)RequestResponseBodyMethodProcessor:方法所在类不被ResponseBody修饰并且方法被ResponseBody修饰 15)ViewNameMethodReturnValueHandler:返回值类型是CharSequence类型 16)MapMethodProcessor:返回值类型是 Map 类型 17)getCustomReturnValueHandlers:获取自定义返回值类型 18)ModelAndViewResolverMethodReturnValueHandler:任意类型 3)getHandlerAdapter():获取适配器 1)supports:遍历所有注册的适配器,调用其supports方法,返回 true,此适配器支持本次请求 4)handle():真正的处理本次请求的方法 1)handleInternal:处理 handler 请求,在AbstractHandlerMethodAdapter类中 1)handleInternal:RequestMappingHandlerAdapter 1)checkRequest:method 检查,session 检查 2)prepareResponse:准备 response数据 3)invokeHandlerMethod:调用目标方法 1)getDataBinderFactory:获取数据绑定工厂WebDataBinderFactory 2)getModelFactory():获取 Model 工厂 3)createInvocableHandlerMethod():创建ServletInvocableHandlerMethod对象 4)ModelAndViewContainer():创建ModelAndViewContainer容器 5)initModel():初始化 Model 1)invokeModelAttributeMethods():调用所有配置了ModelAttribute注解的方法,包括全局的,和局部的 1)invokeForRequest():反射调用配置了ModelAttribute注解的方法 1)getMethodArgumentValues():绑定所有的方法参数值 2)doInvoke():反射调用方法 6)invokeAndHandle():反射调用 Controller中的方法 1)invokeForRequest():调用 controller 中的方法 1)getMethodArgumentValues():绑定方法所有参数 1)getMethodParameters():获取方法所有参数的MethodParameter 2)循环遍历所有的MethodParameter 1)initParameterNameDiscovery():初始化方法参数名称发现器,默认为DefaultParameterNameDiscoverer 2)resolveParameterType:解析方法参数类型 3)supportsParameter:遍历所有的argumentResolvers,找到匹配的参数解析器存储于缓存 1)getArgumentResolver():如果参数解析不为空,返回 true 1)argumentResolverCache:有缓存,则从缓存中获取 2)supportsParameter:遍历所有的argumentResolvers,如果返回 true,则返回此参数解析器 3)put:调用 put 方法,将匹配的参数解析器存储于缓存argumentResolverCache 4)resolveArgument():解析方法参数 1)getArgumentResolver():获取参数解析器 2)resolveArgument():调用解析器的解析方法,为方法参数绑定数据 1)RequestParamMethodArgumentResolver:解析器 1)getParameterType():获取方法参数的类型 2)getNamedValueInfo():参数名称,如 String a,获取变量a 的字符串名称 a,这里一般用反射或 ASM字节码技术获取 3)resolveName:根据方法参数名称,从request 请求中获取参数值数组,返回只有一个请求值,则返回数组中第0个元素 4)如果参数请求值为空: 1)resolveDefaultValue:为默认值的设置默认值 2)handleMissingValue:required为 true 的抛出异常 3)handleNullValue:如果为null,并且参数类型是int,double,这些基本数据类型,抛出异常 5)createBinder():创建方法参数绑定器 1)createBinderInstance():创建参数绑定实例ExtendedServletRequestDataBinder 2)initializer.initBinder():为bind实例初始化 1)初始化directFieldAccess 2)初始化messageCodesResolver 3)初始化bindingErrorProcessor 4)初始化validator 5)初始化conversionService 6)初始化propertyEditorRegistrars 3)initBinder:调用全局或局部的initBinder方法 6)convertIfNecessary():类型转换,如请求参数是 String 类型,但是方法接收参数是 int 类型,将 String 类型转化为 int 类型 2)ServletModelAttributeMethodProcessor:普通对象参数绑定器 1)getNameForParameter:获取参数名称,默认为参数类型的首字母小写 2)createAttribute():调用BeanUtils.instantiateClass(methodParam.getParameterType())创建一个对象 3)createBinder:创建WebDataBinder 1)createBinderInstance():创建参数绑定实例ExtendedServletRequestDataBinder 2)initializer.initBinder():为bind实例初始化 1)初始化directFieldAccess 2)初始化messageCodesResolver 3)初始化bindingErrorProcessor 4)初始化validator 5)初始化conversionService 6)初始化propertyEditorRegistrars 3)initBinder:调用全局或局部的initBinder方法 4)bindRequestParameters:绑定参数 1)bind():绑定参数 1)addBindValues():预留给子类 2)doBind:真正绑定 1)checkFieldDefaults:是否设置了fieldDefaultPrefix,如果设置了,替换掉mpvs中的MutablePropertyValue 2)checkFieldMarkers:置空下划线开头的属性变量 3)super.doBind(mpvs):参数绑定 1)checkAllowedFields:参数是否允许过滤 2)checkRequiredFields:属性是否需要过滤 3)applyPropertyValues:绑定属性值 1)getPropertyAccessor:获取属性访问器 2)setPropertyValues:为对象设置属性 1)遍历请求参数中所有的属性 1)setPropertyValue:为对象设置属性 1)getPropertyAccessorForPropertyPath:通过属性名获取属性访问器 1)getFirstNestedPropertySeparatorIndex:如果请求参数值中有. 1)getNestedPropertyAccessor:获取嵌套属性访问器 2)getPropertyAccessorForPropertyPath:获取子路径获取访问器 2)getPropertyNameTokens:获取PropertyTokenHolder对象 3)setPropertyValue:设置属性值 1)如果请求参数属性名为names[0]或 names[0][0]这种 1)getPropertyValue:为接收参数实体的属性类型初始化值 2)isArray:如果接收参数的实体属性类型是数组类型,如果 names[index] 的 index 值大于数组类型,为数组扩容 3)List:如果接收参数实体的属性类型是 List 类型 4)Map:如果接收参数实体属性类型是 Map类型 2)如果没有[]或[][] 1)convertForProperty():将请求参数的属性值转换为对象的属性类型的值,如请求参数是 String 类型,User 对象的 age 是 int 类型,将 String 类型转化为 int 类型 2)setValue:反射给对象属性赋值 1)writeMethod.invoke(getWrappedInstance(), value):调用 set 方法为属性赋值 3)RequestResponseBodyMethodProcessor:方法参数被RequestBody注解修饰的方法参数处理器 1)readWithMessageConverters:读消息转换器 1)遍历所有的messageConverters: 1)beforeBodyRead:获取通知器,对RequestBody 参数封装前调用此方法 2)read:调用第三方 JSON 包,为接收参数对象赋值 3)afterBodyRead:获取通知器,对RequestBody 参数封装后调用此方法 2)doInvoke():反射调用Controller 中的方法 2)setResponseStatus():设置 response 状态 3)handleReturnValue():处理结果返回值,这里后面的博客再来分析
Spring 默认为我们实现了许多的参数解析器,本篇文章只抽取了常用的RequestParamMethodArgumentResolver,ServletModelAttributeMethodProcessor,RequestResponseBodyMethodProcessor三个进行分析,有兴趣的不伙伴不厌其烦的研究这三个解析器,也可以对其他参数解析器的实现,相信你的收获会不少,在博客了最后,也可能根据自己的业务需求去自定义了一个参数解析器,今天的参数解析器源码解析就到这里了,下一篇博客对于不同的返回值,Spring MVC又是如何处理的呢?
本文的 gitlab 地址是
https://github.com/quyixiao/spring_tiny/tree/master/src/main/java/com/spring_101_200/test_181_190/test_185_spring_mvc