的作用


一、开篇

在使用SpringMvc框架创建工程时,每次都要在xml中添加【<mvc:annotation-driven/>】这一行配置。

到底这行配置起到了什么作用呢?

启用后,Spring会默认帮我们注册处理请求,参数和返回值的类。

主要是实现了以下两个接口:HandlerMapping与HandlerAdapter。

那到底是如何实现的呢,我们在此展开以下。

二、RequestMappingHandlerMapping

首先,由官方帮助文档可以看到,【<mvc:annotation-driven/>】会帮我们注册RequestMappingHandlerMapping的Bean。

RequestMappingHandlerMapping实现了InitializingBean接口,那么在初始化Bean时,会调用afterPropertiesSet()方法。如下

    public void afterPropertiesSet() {
        initHandlerMethods();
    }
    protected void initHandlerMethods() {
     // 遍历候选Bean
for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
          // 执行并注册候选Bean processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
     // 判断是不是Handler的Bean
if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } }
    protected boolean isHandler(Class<?> beanType) {
     // 判断是否被Controller和RequestMapping注解标注
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 选择被@RequestMapping标注的方法 Map
<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try {
                  // 根据方法上的@RequestMapping来创建RequestMappingInfo实例
return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> {
Method invocableMethod
= AopUtils.selectInvocableMethod(method, userType);
          // 注册请求映射 registerHandlerMethod(handler, invocableMethod, mapping); }); } }
    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }
        public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                assertUniqueMethodMapping(handlerMethod, mapping);
          // 将处理器方法添加到映射表中
this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }

由此可以看到,在对RequestMappingHandlerMapping进行Bean的初始化时,就将被@Controller和@RequestMapping注解的处理器方法放入了映射表

三、RequestMappingHandlerAdapter

因为Handler的格式是不固定的,所以处理之前需要HandlerAdapter来做适配,在此给出了其中最重要的handlerInternal方法

    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        checkRequest(request);

        // Execute invokeHandlerMethod in synchronized block if required.
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No synchronization on session demanded at all...
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }

可以看出其中通过invokeHandlerMethod代理执行了方法。

四、HandlerInterceptor

在执行上述三中代理方法时,HandlerIntercepor会对该方法执行前和执行后进行一些准备和善后处理,具体如下

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }


    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }


    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
    }

preHandle:在handle执行前调用, 通常是做一些环境准备工作,如登录检查

postHandle:在handle执行后,渲染视图前调用,在此处可以修改ModelAndView

afterCompletion:在渲染视图后调用,或是preHandle执行出错后调用,通常是做一些资源回收工作

调用顺序如下:

正常情况下:preHandle(1) -> preHandle(2) -> postHandle(2) -> postHandle(1) -> afterCompletion(2) -> afterCompletion(1)

异常情况下(preHandle(2)处理异常)):preHandle(1) -> preHandle(2) -> afterCompletion(1)


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM