spring之我见--Controller注册到DispatchServlet请求处理(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lovejj1994/article/details/79176514

对应上一章 《spring之我见–从spring的启动到ioc容器的创建》

今天我们探讨一下Springmvc的工作原理,Springmvc的核心是Controller请求部分,所以我们的探讨从Controller被注册开始,到Controller如何被请求的。

1.Controller注册前的准备工作

1.1 refresh()

上一章我们知道IOC容器是在ContextLoaderListener(ServletContextListener的实现类)下初始化的,而初始化的重要步骤是在ConfigurableApplicationContext的refresh ()方法下完成的,根据下面的注释,refresh 方法负责加载配置文件,比如常见的spring配置文件applicationContext.xml

/**
 *  加载或刷新配置的持久表示,这可能是XML文件、属性文件或关系数据库模式。
 * Load or refresh the persistent representation of the configuration,
 * which might an XML file, properties file, or relational database schema.
 * <p>As this is a startup method, it should destroy already created singletons
 * if it fails, to avoid dangling resources. In other words, after invocation
 * of that method, either all or no singletons at all should be instantiated.
 * @throws BeansException if the bean factory could not be initialized
 * @throws IllegalStateException if already initialized and multiple refresh
 * attempts are not supported
 */
void refresh() throws BeansException, IllegalStateException;

这是refresh()具体实现,做了很多事,我们具体进obtainFreshBeanFactory()方法。

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

当走到refreshBeanFactory()方法,我们看到loadBeanDefinitions()方法,官方解释为 将bean定义加载到给定的bean工厂中,通常是通过委托给一个或多个bean定义阅读器。话句话说 这里就是生成BeanDefinition元数据的地方,BeanFactory不直接存储实例,而是元数据,根据需要用反射等手段将BeanDefinition转换成实例,我们的Controller当然也需要先转换成元数据,

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

spring的调用栈比较深,我这里只是挑重要的地方解释,最主要还是自己debug走一遍。
当走到loadBeanDefinitions(XmlBeanDefinitionReader reader)方法(这跟上一个loadBeanDefinitions方法不是一样,是重载方法),我们发现它开始读配置文件,这个configLocations数组就一个String对象:“/WEB-INF/applicationContext.xml” .

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }

而我们的applicationContext.xml文件就写了两句,为了注册一个controller。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- @Controller 扫描 -->
    <context:component-scan base-package="controller" />

    <!-- 注解驱动: 作用:替我们自动配置最新版的注解的处理器映射器和处理器适配器 -->
    <mvc:annotation-driven />

</beans>

顺便我也把controller贴出来吧。

package controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * @author panqian
 * @Description:
 * @date 2018/1/26
 */
@RestController
@RequestMapping("controller")
public class TestController {

    @GetMapping("test")
    public void test(HttpServletRequest request) {
        System.out.println("");
    }
}

有人问为什么系统知道“/WEB-INF/applicationContext.xml” 这个路径,一开始我也纳闷,但是既然能debug,我们就能知道程序运行的堆栈链,所以我在setConfigLocation打一个断点,根据堆栈链往前找,直接就可以找到答案。
这里写图片描述

sc就是ServletContext,还记得上一章我们在web.xml写的一个启动的InitParameter?路径就是从这里取的。

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
    wac.setConfigLocation(configLocationParam);
}

1.2XmlBeanDefinitionReader负责配置文件解析

让我们继续往下看,刚刚拿到了文件路径,那我们的解析就交给了XmlBeanDefinitionReader负责,这里面涉及解析节点,提取信息,又是很深的调用栈,主要介绍一下MvcNamespaceHandler ,它针对不同的配置提供多种解析器,比如annotation-driven就用AnnotationDrivenBeanDefinitionParser。

package org.springframework.web.servlet.config;

import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * {@link NamespaceHandler} for Spring MVC configuration namespace.
 *
 * @author Keith Donald
 * @author Jeremy Grelle
 * @author Sebastien Deleuze
 * @since 3.0
 */
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());
    }

}

AnnotationDrivenBeanDefinitionParser又具体做了什么事呢?看它的parse()方法,

    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);
// 生成 RequestMappingHandlerMapping,用于处理@Controller和@RequestMapping注解的  
        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);

// 将该BeanDefinition加入BeanFactory工厂(ApplicationContext)
        readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

。。。。。。
    }

上面注册的RequestMappingHandlerMapping 和RequestMappingHandlerAdapter是为后面controller注册和使用准备的,只有配置了<mvc:annotation-driven /> 才会注册这些组件。

说了<mvc:annotation-driven /> 是不是还有一个<context:component-scan base-package="controller" /> ?这个标签也有自己的parse–ComponentScanBeanDefinitionParser,它的parse方法更加简单。

basePackage 拿到参数是我配的 “controller”,所以scanner.doScan方法直接到该包下拿到了testController的beanDefinitions,随后注册到 ApplicationContext中。

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

        // Actually scan for bean definitions and register them.
        ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

        return null;
    }

2.生火烧饭! Controller注册

通过前面的分析,我们已经有了用于 Controller注册的RequestMappingHandlerMapping,TestController也被注册进了IOC容器中,万事俱备,我们重新返回refresh()方法中,刚刚都是走的obtainFreshBeanFactory();而接下来的finishBeanFactoryInitialization()很重要,首先这个方法用于实例化所有(非懒加载)单例对象。 然后RequestMappingHandlerMapping是单例的,非懒加载的,所以RequestMappingHandlerMapping也会在这个方法里处理,再加上 RequestMappingHandlerMapping的afterPropertiesSet方法是具体注册Controller的地方,所以我们需要重点跟踪finishBeanFactoryInitialization()的代码。

这是我根据的doGetBean()的部分代码,当轮到RequestMappingHandlerMapping开始创建实例时,会走复写的getObject()方法。

    protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
// Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                // Explicitly remove instance from singleton cache: It might have been put there
                                // eagerly by the creation process, to allow for circular reference resolution.
                                // Also remove any beans that received a temporary reference to the bean.
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

}

继续往里走,populateBean是在封装实例字段值,继续进initializeBean方法

// Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }

重点关注invokeInitMethods方法,这个方法给实例机会去 做一些初始化操作,而这个机会就是复写afterPropertiesSet()方法。

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
。。。。
try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
。。。。
}

invokeInitMethods方法会调用afterPropertiesSet(),那RequestMappingHandlerMapping怎么复写了这个方法呢?

((InitializingBean) bean).afterPropertiesSet();
@Override
    public void afterPropertiesSet() {
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setUrlPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
        this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
        this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
        this.config.setContentNegotiationManager(getContentNegotiationManager());

        super.afterPropertiesSet();
    }
@Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }

这里真相大白,它会先通过isHandler(beanType)判断是不是有@Controller或@RequestMapping注释,是的话进入detectHandlerMethods(),最后将映射写入mappingRegistry,这是一个MappingRegistry类型,所有的前端映射都会记录在这里,也就达到了注册controller的目的。

    /**
     * Scan beans in the ApplicationContext, detect and register handler methods.
     * @see #isHandler(Class)
     * @see #getMappingForMethod(Method, Class)
     * @see #handlerMethodsInitialized(Map)
     */
    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)) {
                    detectHandlerMethods(beanName);
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

3.参考文献

https://www.cnblogs.com/kindevil-zx/p/6603154.html
http://blog.csdn.net/lovesomnus/article/details/49801593

猜你喜欢

转载自blog.csdn.net/lovejj1994/article/details/79176514