HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存.
目录:
注解解析器初始化注册:@ModelAttribute(往model中添加属性)
注解解析器初始化注册:@InitBinder(用于注册校验器,参数编辑器等)
返回值处理returnValueHandlers初始化
全局的@ControllerAdvice注解使用类的@ModelAttribute 和 @InitBinder信息读取并缓存
注:具体解析器的分析还是看后续文章吧,要不文章跟裹脚布似的.
注解@ModelAttritue解析器初始化并注册
我们先看下@ModelAttribute注解的使用吧:
1. 在注解中定义属性名,方法返回值
2. 通过model直接设置
3. 暂时没搞定
1 // 在注解中定义属性名,方法返回值 2 @ModelAttribute("clazzName") 3 public String setModel() { 4 return this.getClass().getName(); 5 } 6 // 通过model直接设置 7 @ModelAttribute 8 public void setModel1(Model model){ 9 model.addAttribute("movie", "who"); 10 } 11 // 暂时没搞定 12 @ModelAttribute() 13 public String setModel2(){ 14 return "actor"; 15 }
新建解析器时的逻辑:
1 如果没有配置,直接读取默认实现
这边默认实现多达24种+自定义实现,主要分为4类解析器:基于注解,基于类型,自定义,号称解析全部
2 通过Composite封装,并注册
解析策略实在太多,这边封装一个HandlerMethodArgumentResolverComposite,迭代解析器委托处理(有点责任链的味道)
先看初始化解析器,并注册的代码:
1 package org.springframework.web.servlet.mvc.method.annotation; 2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, 3 InitializingBean { 4 public void afterPropertiesSet() { 5 if (this.argumentResolvers == null) { 6 List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); 7 this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); 8 } 9 // ... 10 } 11 private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { 12 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); 13 14 // Annotation-based argument resolution 基于注解的解析器 15 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); 16 resolvers.add(new RequestParamMapMethodArgumentResolver()); 17 resolvers.add(new PathVariableMethodArgumentResolver()); 18 resolvers.add(new PathVariableMapMethodArgumentResolver()); 19 resolvers.add(new MatrixVariableMethodArgumentResolver()); 20 resolvers.add(new MatrixVariableMapMethodArgumentResolver()); 21 resolvers.add(new ServletModelAttributeMethodProcessor(false)); 22 resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters())); 23 resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters())); 24 resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); 25 resolvers.add(new RequestHeaderMapMethodArgumentResolver()); 26 resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); 27 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); 28 29 // Type-based argument resolution 基于类型的解析器 30 resolvers.add(new ServletRequestMethodArgumentResolver()); 31 resolvers.add(new ServletResponseMethodArgumentResolver()); 32 resolvers.add(new HttpEntityMethodProcessor(getMessageConverters())); 33 resolvers.add(new RedirectAttributesMethodArgumentResolver()); 34 resolvers.add(new ModelMethodProcessor()); 35 resolvers.add(new MapMethodProcessor()); 36 resolvers.add(new ErrorsMethodArgumentResolver()); 37 resolvers.add(new SessionStatusMethodArgumentResolver()); 38 resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); 39 40 // Custom arguments 自定义解析器 41 if (getCustomArgumentResolvers() != null) { 42 resolvers.addAll(getCustomArgumentResolvers()); 43 } 44 45 // Catch-all 全能的解析器 46 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); 47 resolvers.add(new ServletModelAttributeMethodProcessor(true)); 48 49 return resolvers; 50 } 51 }
然后是HandlerMethodArgumentResolverComposite迭代具体解析器委托处理的代码:
GOF对责任链意图的定义是:
使多个对象都有机会iu处理请求,从而避免请求的发送者和接受者直接的耦合关系.将这些对象连成一条链,并沿这条链传递该请求,直到有一个对象处理它为止.
从百度百科盗了个类图,来类比下:
上面是标准的责任链,下面是HandlerMethodReturnValueHandler部分类图
1 package org.springframework.web.method.support; 2 public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { 3 public Object resolveArgument( 4 MethodParameter parameter, ModelAndViewContainer mavContainer, 5 NativeWebRequest webRequest, WebDataBinderFactory binderFactory) 6 throws Exception { 7 8 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); 9 return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); 10 } 11 public boolean supportsParameter(MethodParameter parameter) { 12 return getArgumentResolver(parameter) != null; 13 } 14 private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { 15 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); 16 if (result == null) { 17 for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { 18 if (methodArgumentResolver.supportsParameter(parameter)) { 19 result = methodArgumentResolver; 20 this.argumentResolverCache.put(parameter, result); 21 break; 22 } 23 } 24 } 25 return result; 26 } 27 // ... 28 }
注解@InitBinder解析器初始化
先看@InitBinder注解的使用吧:
1 /** 2 * 使用WebDataBinder实现日期校验 3 * @param dataBinder 4 */ 5 @InitBinder 6 public void dateFormat(WebDataBinder dataBinder){ 7 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 8 dateFormat.setLenient(false); 9 dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,false)); 10 }
这边的代码逻辑其实跟@ModelAttribute解析器初始化的逻辑是一样的,就不具体分析,只是这边初始化使用的解析器是不一样的.至于差异的原因,暂时还不知道,哪位有兴趣可以帮忙科普科普.
1 package org.springframework.web.servlet.mvc.method.annotation; 2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, 3 InitializingBean { 4 private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() { 5 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); 6 7 // Annotation-based argument resolution 基于注解的解析器 8 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); 9 resolvers.add(new RequestParamMapMethodArgumentResolver()); 10 resolvers.add(new PathVariableMethodArgumentResolver()); 11 resolvers.add(new PathVariableMapMethodArgumentResolver()); 12 resolvers.add(new MatrixVariableMethodArgumentResolver()); 13 resolvers.add(new MatrixVariableMapMethodArgumentResolver()); 14 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); 15 16 // Type-based argument resolution 基于类型的解析器 17 resolvers.add(new ServletRequestMethodArgumentResolver()); 18 resolvers.add(new ServletResponseMethodArgumentResolver()); 19 20 // Custom arguments 自定义解析器 21 if (getCustomArgumentResolvers() != null) { 22 resolvers.addAll(getCustomArgumentResolvers()); 23 } 24 25 // Catch-all 全能的解析器 26 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); 27 28 return resolvers; 29 } 30 // ... 31 }
返回值处理returnValueHandlers初始化
用于将handler处理器的返回值封装成ModelAndView.
这边的处理逻辑跟@ModelAttribute注解解析器的初始化高度雷同,我们还是看看使用的HandlerMethodReturnValueHandler接口
接口的定义方式也是高度雷同,一个api问是否支持,一个api进行具体处理.
1 package org.springframework.web.method.support; 2 public interface HandlerMethodReturnValueHandler { 3 boolean supportsReturnType(MethodParameter returnType); 4 void handleReturnValue(Object returnValue, 5 MethodParameter returnType, 6 ModelAndViewContainer mavContainer, 7 NativeWebRequest webRequest) throws Exception; 8 9 }
分类貌似也有一定的相似.
1 package org.springframework.web.servlet.mvc.method.annotation; 2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, 3 InitializingBean { 4 private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { 5 List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(); 6 7 // Single-purpose return value types 单一目的 8 handlers.add(new ModelAndViewMethodReturnValueHandler()); 9 handlers.add(new ModelMethodProcessor()); 10 handlers.add(new ViewMethodReturnValueHandler()); 11 handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager)); 12 handlers.add(new CallableMethodReturnValueHandler()); 13 handlers.add(new DeferredResultMethodReturnValueHandler()); 14 handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); 15 16 // Annotation-based return value types 基于注解 17 handlers.add(new ModelAttributeMethodProcessor(false)); 18 handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager)); 19 20 // Multi-purpose return value types 多目的 21 handlers.add(new ViewNameMethodReturnValueHandler()); 22 handlers.add(new MapMethodProcessor()); 23 24 // Custom return value types 自定义 25 if (getCustomReturnValueHandlers() != null) { 26 handlers.addAll(getCustomReturnValueHandlers()); 27 } 28 29 // Catch-all 又是全能 30 if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { 31 handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); 32 } 33 else { 34 handlers.add(new ModelAttributeMethodProcessor(true)); 35 } 36 37 return handlers; 38 } 39 }
全局的@ControllerAdvice注解使用类的@ModelAttribute 和 @InitBinder信息读取并缓存
@ControllerAdvice注解主要是为了解决以下的场景问题:
如果@ModelAttribute或@InitBinder注解如果需要在很多地方使用,怎么办?
使用集成的话,由于java的单继承会限制父类,不够灵活.
使用时只需要如下添加注解就可以
1 @ControllerAdvice() 2 public class AdviceController { 3 // ... 4 }
源码解析时,是通过InitializingBean的afterPropertiesSet调用initControllerAdviceCache初始化的解析器.
查找使用注解@ControllerAdivce的类时,通过spring迭代容器过滤容器中全部的类,找到使用ControllerAdvice.class的类并注册.
1 package org.springframework.web.servlet.mvc.method.annotation; 2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, 3 InitializingBean { 4 public void afterPropertiesSet() { 5 // ... 6 initControllerAdviceCache(); 7 } 8 private void initControllerAdviceCache() { 9 // ... 10 // 扫描方式找到使用@ControllerAdvice的类 11 List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); 12 Collections.sort(beans, new OrderComparator()); 13 14 for (ControllerAdviceBean bean : beans) { 15 Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS); 16 if (!attrMethods.isEmpty()) { 17 this.modelAttributeAdviceCache.put(bean, attrMethods); 18 } 19 Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS); 20 if (!binderMethods.isEmpty()) { 21 this.initBinderAdviceCache.put(bean, binderMethods); 22 } 23 } 24 } 25 }
这边扫描获取类的方式跟之前的有所不同,我们可以细看下.
1 package org.springframework.web.method;
2 public class ControllerAdviceBean implements Ordered { 3 /** 4 * Find the names of beans annotated with 5 * {@linkplain ControllerAdvice @ControllerAdvice} in the given 6 * ApplicationContext and wrap them as {@code ControllerAdviceBean} instances. 7 */ 8 public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) { 9 List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>(); 10 for (String name : applicationContext.getBeanDefinitionNames()) { 11 if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) { 12 beans.add(new ControllerAdviceBean(name, applicationContext)); 13 } 14 } 15 return beans; 16 } 17 }
看下两个api在接口中定义:
1. getBeanDefinitionNames 返回容器中定义的全部bean 的 name
2. findAnnotationOnBean 查找类上定义的注解,包括父类与接口
1 package org.springframework.beans.factory;
2 public interface ListableBeanFactory extends BeanFactory { 3 // ... 4 /** 5 * Return the names of all beans defined in this factory. 6 * <p>Does not consider any hierarchy this factory may participate in, 7 * and ignores any singleton beans that have been registered by 8 * other means than bean definitions. 9 * @return the names of all beans defined in this factory, 10 * or an empty array if none defined 11 */ 12 String[] getBeanDefinitionNames(); 13 /** 14 * Find a {@link Annotation} of {@code annotationType} on the specified 15 * bean, traversing its interfaces and super classes if no annotation can be 16 * found on the given class itself. 17 * @param beanName the name of the bean to look for annotations on 18 * @param annotationType the annotation class to look for 19 * @return the annotation of the given type found, or {@code null} 20 */ 21 <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType); 22 23 }
顺便关心下@ControllerAdvice信息是如何保存的,就是对应的pojo ControllerAdviceBean:
这边只是记录bean,还没有添加添加根据类,包进行过滤匹配类的功能.
这边值得说的有3个:
1. 根据类是否实现Ordered接口,设置排序顺序
2. 扫描应用下使用ControllerAdvice注解的类就是上面说的api
3. 实现getBeanType获取类的类型 和resolveBean获取类实例 ,这个算是advice的行为吧.作为容器的行为吧.
1 package org.springframework.web.method;
2
3 public class ControllerAdviceBean implements Ordered { 4 // 使用注解的类 5 private final Object bean; 6 private final int order; 7 private final BeanFactory beanFactory; 8 9 public ControllerAdviceBean(String beanName, BeanFactory beanFactory) { 10 // ... 11 } 12 13 public ControllerAdviceBean(Object bean) { 14 // ... 15 } 16 17 // 如果类实现Ordered,根据order的值设置排序 18 private static int initOrderFromBeanType(Class<?> beanType) { 19 Order annot = AnnotationUtils.findAnnotation(beanType, Order.class); 20 return (annot != null) ? annot.value() : Ordered.LOWEST_PRECEDENCE; 21 } 22 23 private static int initOrderFromBean(Object bean) { 24 return (bean instanceof Ordered) ? ((Ordered) bean).getOrder() : initOrderFromBeanType(bean.getClass()); 25 } 26 27 /** 28 * 扫描应用下使用ControllerAdvice注解的类 29 */ 30 public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) { 31 List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>(); 32 for (String name : applicationContext.getBeanDefinitionNames()) { 33 if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) { 34 beans.add(new ControllerAdviceBean(name, applicationContext)); 35 } 36 } 37 return beans; 38 } 39 40 public Class<?> getBeanType() { 41 Class<?> clazz = (this.bean instanceof String) 42 ? this.beanFactory.getType((String) this.bean) : this.bean.getClass(); 43 44 return ClassUtils.getUserClass(clazz); 45 } 46 47 public Object resolveBean() { 48 return (this.bean instanceof String) ? this.beanFactory.getBean((String) this.bean) : this.bean; 49 } 50 // ... 51 }