zoukankan      html  css  js  c++  java
  • SpringMVC学习记录1

    起因

    以前大三暑假实习的时候看到公司用SpringMVC而不是Struts2,老司机告诉我SpringMVC各种方便,各种解耦.

    然后我自己试了试..好像是蛮方便的....

    基本上在Spring的基础上配置一小段就可以了....

    但是我一直搞不明白...像我用Struts2的时候需要继承框架的Action父类,所以请求可以委派过来...但是SpringMVC自己写的Controller完全就是POJO.那为什么请求还可以委派过来涅?

    最近有点空...稍微研究了一下SpringMVC....算是对它有了点新的理解...

     

    mvc:annotation-driven到底干了啥?

    SpringMVC插入的一小段配置中有一段就是mvc:annotation-driven......这个神奇的配置到底干了啥?为什么加了就可以用@Controller来接受请求了呢?why????

    查阅资料发现.这个配置会加载2个bean

    <mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。

    http://kingliu.iteye.com/blog/1972973

    但是实际上我去看了下SpringMVC的代码..发现这2个类以及过期了(我的SPringMVC版本是4.1.7)...现在实际上现在注册的是RequestMappingHandlerMapping和RequestMappingHandlerAdapter..如下图

    这里之所以@Controller能起作用主要是靠RequestMappingHandlerMapping.....所以我看了一下RequestMappingHandlerMapping的代码...

    RequestMappingHandlerMapping

    RequestMappingHandlerMapping的是实现了InitializingBean接口的

    所以当各种bean初始化以后,属性被设置以后RequestMappingHandlerMapping的afterPropertiesSet方法会被调用.(请参考bean的生命周期http://www.cnblogs.com/zrtqsk/p/3735273.html).

     1 /**
     2      * Detects handler methods at initialization.
     3      */
     4     @Override
     5     public void afterPropertiesSet() {
     6         initHandlerMethods();
     7     }
     8 
     9     /**
    10      * Scan beans in the ApplicationContext, detect and register handler methods.
    11      * @see #isHandler(Class)
    12      * @see #getMappingForMethod(Method, Class)
    13      * @see #handlerMethodsInitialized(Map)
    14      */
    15     protected void initHandlerMethods() {
    16         if (logger.isDebugEnabled()) {
    17             logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    18         }
    19 
    20         String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
    21                 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
    22                 getApplicationContext().getBeanNamesForType(Object.class));
    23 
    24         for (String beanName : beanNames) {
    25             if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
    26                     isHandler(getApplicationContext().getType(beanName))){
    27                 detectHandlerMethods(beanName);
    28             }
    29         }
    30         handlerMethodsInitialized(getHandlerMethods());
    31     }

    afterPropertiesSet会调用initHandlerMethods

    initHandlerMethods里22行会把所有的bean都取出来.然后26行一个一个去做isHandler方法

    1     @Override
    2     protected boolean isHandler(Class<?> beanType) {
    3         return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
    4                 (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
    5     }

    从isHandler方法里可以看到因为我们自己写的Controller上面有annotation @Controller所以isHandler返回是true...

    从代码中我们额外还可以发现...如果我们的Controller上面有注解@RequestMapping....那我们不写@Controller也是可以的....(⊙﹏⊙)b

    有@Controller注解的类会接下去做detectHandlerMethods方法(27行).

     1 /**
     2      * Look for handler methods in a handler.
     3      * @param handler the bean name of a handler or a handler instance
     4      */
     5     protected void detectHandlerMethods(final Object handler) {
     6         Class<?> handlerType =
     7                 (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
     8 
     9         // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
    10         final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
    11         final Class<?> userType = ClassUtils.getUserClass(handlerType);
    12 
    13         Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
    14             @Override
    15             public boolean matches(Method method) {
    16                 T mapping = getMappingForMethod(method, userType);
    17                 if (mapping != null) {
    18                     mappings.put(method, mapping);
    19                     return true;
    20                 }
    21                 else {
    22                     return false;
    23                 }
    24             }
    25         });
    26 
    27         for (Method method : methods) {
    28             registerHandlerMethod(handler, method, mappings.get(method));
    29         }
    30     }

    看这个方法的名字我们大概也能知道它是干啥的...第16行getMappingForMethod会去把这个Controller里标注了@RequestMapping的方法全部找出来..如下面代码片段

     1     @Override
     2     protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
     3         RequestMappingInfo info = null;
     4         RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
     5         if (methodAnnotation != null) {
     6             RequestCondition<?> methodCondition = getCustomMethodCondition(method);
     7             info = createRequestMappingInfo(methodAnnotation, methodCondition);
     8             RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
     9             if (typeAnnotation != null) {
    10                 RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
    11                 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
    12             }
    13         }
    14         return info;
    15     }

    全部找出来以后再做27行-29行

    1 for (Method method : methods) {
    2              registerHandlerMethod(handler, method, mappings.get(method));
    3          }

    把这个标注为@RequestMapping的方法注册一下.....

    总结

    1.这样就能解释为了我们标注了@Controller的方法里面的标注了@RequestMapping的方法能够接受到请求...即使我们没有继承SpringMVC的任何类...

    这全靠RequestMappingHandlerMapping.它参与了Spring bean的生命周期...会将所有bean一个一个检测...只要这个bean被标注了Controller注解.就会将@RequestMapping标注的方法取出来并注册..

    2.至此以前另外一个疑问也有了解答..为什么@Controller标注的bean与<mvc:annotation-driven>标签需要配置在SpringMVC的配置里而不是Spring里.(全部配置在Spring里也可以.必须都在一起.)

    首先, Spring不需要注入Controller的bean,Controller的bean只有SpringMVC需要用到.但这个不是重点.

    重点是其次, mvc:annotation-driven会注入参与Spring bean生命周期的特殊的bean,他们会检测Controller的bean.

    如果情况1:这些特殊的bean是由Spring加载的,而Controller bean是由SpringMVC加载的.那这些特殊的bean是检测不到Controller bean的.因为Spring父环境是由ServletContextListener加载的,必定先于DispatcherServlet加载的子环境.所以创建Spring的XMLWebApplicationContext的时候Controller bean都还未加载.

    情况2:Controller bean由Spring加载,<mvc:annotation-driven>写在SpringMVC里.

    AbstractHandlerMethodMapping.class

    1
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? 2 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : 3 getApplicationContext().getBeanNamesForType(Object.class));

    detectHandlerMethodsInAncestorContexts默认是false.所以SpringMVC的xmlWebApplicationContext不会去父容器找bean.只会在自身这里找Object.class.所以也不会包括Controller bean.那结果可想而知. 解决办法是可以通过设置detectHandlerMethodsInAncestorContexts 为true来解决.

    但是最简单的办法就是@Controller标注的bean与<mvc:annotation-driven>标签都配置在SpringMVC的配置文件里.

  • 相关阅读:
    NC13950 Alliances(LCA)
    NC13884 Paint Box(容斥原理)
    Gym102174B 炼金术(AC自动机)
    CF301D Yaroslav and Divisors(树状数组)
    CF1443D Extreme Subtraction(思维)
    Gym102174L 旅行的意义(期望dp)
    Gym102220F Mini-game Before Contest(博弈论)
    Gym102220D Master of Data Structure (虚树)
    用random.simple来解决从0-99这100个数中随机取10个不重复的数
    一个栈的输入序列为1,2,3,4,5,则下列序列中不可能是栈的输出序列的是()
  • 原文地址:https://www.cnblogs.com/abcwt112/p/5201109.html
Copyright © 2011-2022 走看看