的作用


一、開篇

在使用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