Spring源碼解析二:基於註解開發的Spring MVC配置原理

一、基於註解的方式使用Spring MVC

使用@RequestMapping,@Controller,@ResponseBody

@RestController
public class HelloController {

    @RequestMapping("hello")
    public String hello (){
        return "Hello Spring MVC";
    }
}

二、如何進行註解開發的配置

  • 基於XML進行配置

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <mvc:annotation-driven/>
<beans>
  • 基於@EnableWebMvc註解實現自動配置

  • 基於SpringBoot中的自動配置

    WebMvcAutoConfiguration

@Configuration
@EnableWebMvc
public class WebMvcConfig{

}

三、配置原理

  • <mvc:annotation-driven/>原理

    添加此標籤後,Spring使用自定義的Bean定義解析器AnnotationDrivenBeanDefinitionParser加載了幾個比較幾個重要的Bean定義,主要是一些圍繞HandlerMethod進行處理的基礎組件對象

    • RequestHandlerMapping

      根據請求映射找出對應的HandlerMethod進行處理
    • RequestMappingHandlerAdapter

      執行HandlerMethodChain進入到目標方法     
      

AnnotationDrivenBeanDefinitionParser做了什麼?

public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

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

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

        // 註冊RequestMappingHandlerMapping,用於處理@RequestMaping註解的方法請求映射爲HandlerMethod
        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerMappingDef.getPropertyValues().add("order", 0);
        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);

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

        RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
        RuntimeBeanReference validator = getValidator(element, source, parserContext);
        RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);

        RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
        bindingDef.setSource(source);
        bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        bindingDef.getPropertyValues().add("conversionService", conversionService);
        bindingDef.getPropertyValues().add("validator", validator);
        bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

        ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
        ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
        ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
        String asyncTimeout = getAsyncTimeout(element);
        RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
        ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
        ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
        // 註冊RequestMappingHandlerAdapter,用於適配處理HandlerMethod
        RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
        handlerAdapterDef.setSource(source);
        handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
        handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
        addResponseBodyAdvice(handlerAdapterDef);

        if (element.hasAttribute("ignore-default-model-on-redirect")) {
            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
            handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
        }
        else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
            // "ignoreDefaultModelOnRedirect" spelling is deprecated
            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
            handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
        }

        if (argumentResolvers != null) {
            handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
        }
        if (returnValueHandlers != null) {
            handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
        }
        if (asyncTimeout != null) {
            handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
        }
        if (asyncExecutor != null) {
            handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
        }

        handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
        handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
        String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);

        String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
        RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
        uriCompContribDef.setSource(source);
        uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
        uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
        parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);

        RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
        csInterceptorDef.setSource(source);
        csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
        RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
        mappedCsInterceptorDef.setSource(source);
        mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
        mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
        String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);

        RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
        exceptionHandlerExceptionResolver.setSource(source);
        exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
        exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
        addResponseBodyAdvice(exceptionHandlerExceptionResolver);

        String methodExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);

        RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
        responseStatusExceptionResolver.setSource(source);
        responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        responseStatusExceptionResolver.getPropertyValues().add("order", 1);
        String responseStatusExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);

        // 註冊默認的異常處理解析器
        RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
        defaultExceptionResolver.setSource(source);
        defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        defaultExceptionResolver.getPropertyValues().add("order", 2);
        String defaultExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);

        parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
        parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
        parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
        parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));

        // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

        parserContext.popAndRegisterContainingComponent();

        return null;
    }
}

AnnotationDrivenBeanDefinitionParser在哪裏被註冊?

public class MvcNamespaceHandler extends NamespaceHandlerSupport {

   @Override
   public void init() {
       registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
       ......
   }

}

MvcNamespaceHandler在哪裏被註冊?

作用: 簡單來說NamespaceHandler的作用就是處理自定義標籤的,例如<mvc:annotation-driven>,每一個自定義標籤都必須隸屬於一個namespace下

NamespaceHandler被定義META-INF/spring.handles文件,MvcNamespaceHandler也不例外。

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

具體的解析過程

// 解析自定義標籤
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        // 根據標籤拿到其namespaceUri
        String namespaceUri = getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        // 使用Parser來解析標籤,上面我們說過的AnnotationDrivenBeanDefinitionParser就是其一
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

private final String handlerMappingsLocation;

// 解析獲得能處理該namespace下標籤的命名空間處理器
public NamespaceHandler resolve(String namespaceUri) {
        Map<String, Object> handlerMappings = getHandlerMappings();
        // 根據給定的namespaceUri找到對應的NamespaceHandler
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            String className = (String) handlerOrClassName;
            try {
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                            "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "] not found", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "]: problem with handler class file or dependent class", err);
            }
        }
    }

private Map<String, Object> getHandlerMappings() {
        if (this.handlerMappings == null) {
            synchronized (this) {
                if (this.handlerMappings == null) {
                    try {
                        // 加載所有的spring.handlers文件內容爲Properties對象
                        Properties mappings =
                                PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                        }
                        Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                        this.handlerMappings = handlerMappings;
                    }
                    catch (IOException ex) {
                        throw new IllegalStateException(
                                "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                    }
                }
            }
        }
        return this.handlerMappings;
    }
 at org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve(DefaultNamespaceHandlerResolver.java:114)
      at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1419)
      at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1414)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:189)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:143)
      at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:110)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:335)
      at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303)
      at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
      at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216)
      at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187)
      at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
      at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
      at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
      at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:452)

// 通過打印堆棧信息,我們知道處理自定義標籤的步驟發生在spring容器的啓動過程中,即refresh->obtainFreshBeanFactory->loadBeanDefinitions

// refresh()是構建IOC容器的最核心代碼,obtainFreshBeanFactory()加載xml中定義bean,轉換爲抽象世界中的BeanDefinition

Namespace在XML中的含義

<?xml version="1.0"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3school.com.cn"
xmlns="http://www.w3school.com.cn"
elementFormDefault="qualified">

...
...
</xs:schema>

(1)xmlns:xs="http://www.w3.org/2001/XMLSchema":表明schema中用到的元素和數據類型出自此命名空間,同時規定來自此命名空間的元素和數據都要使用前序xs.
(2)targetNamespace="http://www.w3school.com.cn":顯示 schema 中用到的元素和數據類型來自此命名空間.
(3)elementFormDefault="qualified":指出任何xml實例文檔所使用的且在此schema中聲明過的元素必須被命名空間限定.

  • @EnableWebMvc原理

使用@EnableWebMvc的作用大體上是與<annotation-driven>標籤的作用一致。具體可以看WebMvcConfigurationSupport源碼

四、請求處理原理

  • 重要對象含義

    • HandlerMapping
      作用是根據request請求找到對應的Handler處理器以及Interceptor攔截器,並將它們封裝在HandlerExecutionChain對象中
    • RequestMappingInfo
      存儲@RequestMapping註解中保存的信息,作用類似於TransactionInfo,存儲@Transactional註解信息
  • 初始化請求映射

映射存儲在

// RequestMapping->HandlerMethod
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();

初始化映射的方法AbstractHandlerMethodMapping.initHandlerMethods()

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
        // 獲得IOC容器中的所有對象
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));
        
        for (String beanName : beanNames) {
            // 提取標註有@Controller或@RequestMapping的對象
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
                    isHandler(getApplicationContext().getType(beanName))){
                // 提取所有標註有@RequestMapping的方法,並註冊到handlerMethods 中
                detectHandlerMethods(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected boolean isHandler(Class<?> beanType) {
        return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
                (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
    }

protected void detectHandlerMethods(final Object handler) {
        Class<?> handlerType =
                (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

        // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
        final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 遍歷對象篩選標註有@RequestMaping註解的方法
        Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
            @Override
            public boolean matches(Method method) {
                // 將@RequestMapping註解標註的信息抽象爲@RequestMappingInfo對象
                T mapping = getMappingForMethod(method, userType);
                if (mapping != null) {
                    mappings.put(method, mapping);
                    return true;
                }
                else {
                    return false;
                }
            }
        });
        // 註冊請求映射
        for (Method method : methods) {
            registerHandlerMethod(handler, method, mappings.get(method));
        }
    }
  • 請求原理

  1. 根據HttpRequest請求構建對應的RequestMappingInfo對象。
  2. 遍歷handlerMethods映射找到對應的處理器方法。
  3. 根據RequestMappingHandlerAdaptor執行目標方法。

上面三步只是簡化總結出了請求映射原理,更深一步的請求處理原理還請閱讀DispatcherServlet.doDispatch()方法源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章