• SpringMVC源码解析- HandlerAdapter初始化


    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 }

      

  • 相关阅读:
    Why to define my own blog skin
    安装drupal7.7
    同步和异步的区别
    神马是云计算神马是物联网
    zen主题安装图文记录
    《那些年啊,那些事——一个程序员的奋斗史》——127
    《一个程序员的奋斗史》帮我选封面哇! —— 猜封面页数赢赠书活动~
    《那些年啊,那些事——一个程序员的奋斗史》——126
    《那些年啊,那些事——一个程序员的奋斗史》——128 (终章)
    伍定轩乱语
  • 原文地址:https://www.cnblogs.com/leftthen/p/5219645.html
Copyright © 2020-2023  润新知