你的位置:首页 > Java教程

[Java教程]SpringMVC源码解析


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 defined11   */12   String[] getBeanDefinitionNames();13   /**14   * Find a {@link Annotation} of {@code annotationType} on the specified15   * bean, traversing its interfaces and super classes if no annotation can be16   * found on the given class itself.17   * @param beanName the name of the bean to look for annotations on18   * @param annotationType the annotation class to look for19   * @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 }