1. 程式人生 > 實用技巧 >SpringMvc之構造對映關係

SpringMvc之構造對映關係

  springmvc正是通過構造請求的模式和其對應的method,然後通過反射機制執行方法,並將結果返回。

  本篇就分析下對映關係的初始化過程

一 關鍵類講解

  HandlerMethod

public class HandlerMethod {

    /** Logger that is available to subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    private final Object bean;

    private final BeanFactory beanFactory;

    
private final Class<?> beanType; private final Method method;//實際的method private final Method bridgedMethod; private final MethodParameter[] parameters;//入參

  它有一個非常重要的子類,ServletInvocableHandlerMethod

  ServletInvocableHandlerMethod這個類在HandlerAdapter對每個請求處理過程中,都會例項化一個出來(上面提到的屬性由HandlerAdapter進行設定),分別對請求和返回進行處理。  (RequestMappingHandlerAdapter原始碼,例項化ServletInvocableHandlerMethod的時候分別set了上面提到的重要屬性)

  MethodParameter

public class MethodParameter {

    private final Method method;//方法名

    private final Constructor<?> constructor;

    private final int parameterIndex;//該引數的下標,表示第幾個入參

    private int nestingLevel = 1;

    /** Map from Integer level to Integer type index */
    Map<Integer, Integer> typeIndexesPerLevel;

    
private volatile Class<?> containingClass; private volatile Class<?> parameterType;//型別 private volatile Type genericParameterType; private volatile Annotation[] parameterAnnotations; private volatile ParameterNameDiscoverer parameterNameDiscoverer; private volatile String parameterName;//引數名 private volatile MethodParameter nestedMethodParameter;

  

  RequestMappingInfo 是一個封裝了各種請求對映條件並實現了RequestCondition介面的類。

  有各種RequestCondition實現類屬性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,這個請求條件看屬性名也瞭解,分別代表http請求的路徑模式、方法、引數、頭部等資訊。

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

    private final String name;

    private final PatternsRequestCondition patternsCondition;//URL模式 

    private final RequestMethodsRequestCondition methodsCondition;//GET 還是POST

    private final ParamsRequestCondition paramsCondition;

    private final HeadersRequestCondition headersCondition;

    private final ConsumesRequestCondition consumesCondition;

    private final ProducesRequestCondition producesCondition;

  

  RequestMappingHandlerMapping

  處理請求與HandlerMethod對映關係的一個類

二 初始化過程

  我們先從名稱空間開始說起

  當要使用mvc都是這麼開始的<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>

  熟悉spring的都知道名稱空間是怎麼回事,直接到jar包的META-INF裡去找

  http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

  

public class MvcNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
        registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
        registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
        registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
        registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
        registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
        registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
        registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
        registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
    }
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {

    public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();//預設的bean

    public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();//預設的bean

  

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);
        XmlReaderContext readerContext = parserContext.getReaderContext();

        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
        parserContext.pushContainingComponent(compDefinition);

        RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);

        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerMappingDef.getPropertyValues().add("order", 0);
        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

        if (element.hasAttribute("enable-matrix-variables")) {
            Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
        }
        else if (element.hasAttribute("enableMatrixVariables")) {
            Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
        }

        configurePathMatchingProperties(handlerMappingDef, element, parserContext);
        readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

  上面的程式碼說明了,在解析<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>

  系統自動給容器中註冊了兩個BeanDefinition,分別是RequestMappingHandlerMapping ,RequestMappingHandlerAdapter

  接下來我們就分析RequestMappingHandlerMapping 的初始化過程

三 RequestMappingHandlerMapping 初始化

  初始化的工作在AbstractHandlerMethodMapping

@Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().getType(beanName);
                }
                catch (Throwable ex) {
                    // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                    }
                }
                if (beanType != null && isHandler(beanType)) {//拿到當前容器的所有的bean,判斷是不是controller
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

  

protected void detectHandlerMethods(final Object handler) {//handler就是實際的controller實現類
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        final Class<?> userType = ClassUtils.getUserClass(handlerType);

        Map<Method, T> methods = MethodIntrospector.selectMethods(userType, //這裡的methods的結構是key是Method,而Value是一個RequestMappingInfo 
                new MethodIntrospector.MetadataLookup<T>() {
                    @Override
                    public T inspect(Method method) {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    }
                });//這部分的邏輯並不複雜,就是遍歷這個物件裡的所有方法,並記錄下方法上的路徑

        if (logger.isDebugEnabled()) {
            logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
        }
        for (Map.Entry<Method, T> entry : methods.entrySet()) {
            Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
            T mapping = entry.getValue();
            registerHandlerMethod(handler, invocableMethod, mapping);
        }
    }

  我們看一下RequestMappingHandlerMapping

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
        }
        return info;
    }   

  

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

  入參 AnnotatedElement 是public com.suning.rdrs.admin.controller.ResponseResult com.suning.rdrs.admin.controller.audit.RdrsAuditCallbackController.manualAudit(java.lang.String)

  requestMapping的返回值是@org.springframework.web.bind.annotation.RequestMapping(name=, value=[/alarm/summary], path=[/alarm/summary], method=[GET], params=[], headers=[], produces=[], consumes=[])

  我這裡的condition是null,因為我沒有配特殊模式

  然後就是構造RequestMappingInfo

protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, RequestCondition<?> customCondition) {

        return RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name())
                .customCondition(customCondition)
                .options(this.config)
                .build();
    }

  整個過程最後我們得到Map<Method, T> methods key是Method,Value是RequestMappingInfo

  讓我們回到AbstractHandlerMethodMapping.detectHandlerMethods看看剩下的部分

for (Map.Entry<Method, T> entry : methods.entrySet()) {
            Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
            T mapping = entry.getValue();
            registerHandlerMethod(handler, invocableMethod, mapping);
        }

  AbstractHandlerMethodMapping中的方法

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);

                if (logger.isInfoEnabled()) {
                    logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                }
                this.mappingLookup.put(mapping, handlerMethod); //key是RequestMappingInfo物件,Value是實際的方法

                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping); //儲存url和mapping關係注意 mapping是一個RequestMappingInfo物件
                }

                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<T>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

  以上就是分析怎麼把請求url和對應的處理方法關聯起來的,這裡面有很多細節比如RequestMappingInfo在構建中,有些使用者會寫很複雜的url模式,比如帶萬用字元的,萬用字元還有很多鐘模式等等。本文要討論的是原理,哪些字串解析就不管他了。