spring MVC 启动过程

典型的Spring MVC的开发过程为:

  1. 配置web.xml;
    1. 配置容器配置位置:contextConfigLocation
    2. 配置context启动后的监听器ContextLoaderListener
    3. 配置前端控制器,即对某url访问的业务支持,DispatcherServlet
  2. 创建spring的核心启动配置applicationContext.xml;
  3. 业务逻辑处理model/view/controller;
  4. 配置bean

WebApplicationContext 启动原理

tomcat context启动后,会自动触发ContextLoaderListener,然后加载WebApplicationContext。为什么?

ContextLoaderListener 继承自ServletContextListener

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

ServletContextListener完成对ServletContext状态的监听,响应servletContext的initializeddestroy事件。

public interface ServletContextListener extends EventListener {
    default void contextInitialized(ServletContextEvent sce) {
    }

    default void contextDestroyed(ServletContextEvent sce) {
    }
}

ContextLoaderListener 实现了监听ServletContext的方法,从而能获取到ServletContext的启动时间通知,被触发。

ContextLoaderListener的实现逻辑:

    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }

可知WebApplicationContext的初始化逻辑位于ContextLoader.initWebApplicationContext()

  1. 如果WebApplicationContext不存在,那么创建一个;
  2. 如果WebApplicationContext是可配置的context(ConfigurableWebApplicationContext),那么完成配置更新(ContextLoader.configureAndRefreshWebApplicationContext

创建WebApplicationContext逻辑

从ServletContext中判断WebApplicationContext类型,并使用BeanUtils工具类(底层使用反射)生成实例。

怎么确定使用的是那种类型的WebApplicationContext

  1. 通过ServletContext中的contextParam中,获取contextClass,此配置在web.xml中配置,如果实现时配置了,那么使用这个配置类;
  2. 如果没有配置contextClass,那么使用springMVC的默认策略(defaultStrategies)选择contextClass。默认策略配置在ContextLoader.properties文件中(位于spring-web工程中的org.springframework.web.context包路径下,与ContextLoader类位于同一包中),默认的WebApplicationContext实现类为org.springframework.web.context.support.XmlWebApplicationContext

配置WebApplicationContext逻辑

核心逻辑位于ContextLoader.configureAndRefreshWebApplicationContext,完成了:

  1. 配置contextID,
  2. 配置在web.xml中设置的contextConfigLocation
  3. 初始化PropertySource
  4. 记载和刷新配置refresh(),这是spring中配置生效的核心函数

配置生效的核心逻辑位于refresh(),由于XmlWebApplicationContext实现了AbstractApplicationContext,所以实际的逻辑位于AbstractApplicationContext.refresh()

配置生效逻辑主要包括:

  1. 初始化各种配置;
  2. 获取ApplicationContext中的BeanFactory;
  3. 配置BeanFactory,并在BeanFactory初始化后,在生成Bean前,做一些准备工作postProcessBeanFactory
  4. 向BeanFactory中注册BeanPostProcessor,即Bean的初始化完成后的处理器,进一步完善初始化。
  5. 然后就开始各种Bean的实例化。

配置生效的核心逻辑中包括获取BeanFactory的逻辑(obtainFreshBeanFactory()),此逻辑是Bean生命周期管理的核心!

获取BeanFactory

获取BeanFactory的入口函数如下:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        return getBeanFactory();
    }

其中getBeanFactory()仅仅是从属性中获取beanFactory,而refreshBeanFactory()完成了(AbstractRefreshableApplicationContext.refreshBeanFactory()由于XmlWebApplicationContext的继承关系推出来):

  1. BeanFactory创建和自定义
  2. BeanDefinition的解析和加载(这块和Bean的实例化紧密关联
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        //...省略前面的判断
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            //省略后面的设置
    }
BeanFactory创建

为ApplicationContext创建一个BeanFactory(BeanFactory是ApplicationContext的属性),默认的BeanFactory类型是DefaultListableBeanFactory

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
BeanDefinition的解析和加载

XmlWebApplicationContext重写了loadBeanDefinitions()方法加载BeanDefinition。

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

核心逻辑是:

  1. 生成XmlBeanDefinitionReader,并配置属性文件和环境配置,其中initBeanDefinitionReader(beanDefinitionReader);是留给子类做定制化用的。
  2. 使用XmlBeanDefinitionReader来解析BeanDefinition

那么怎么使用XmlBeanDefinitionReader解析出BeanDefinition的?

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }
配置文件是什么?

获取WebApplicationContext配置文件,此配置文件为前面初始化和配置WebApplicationContext时设置的配置文件,即configureAndRefreshWebApplicationContext()时设置的:

    if (configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

那么根源就是web.xml中配置的contextConfigLocation对应的配置文件,为XML格式

怎么加载为BeanDefinition的?

下面的函数只取出了核心部分:

@Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
            Resource[] resources = resourceLoader.getResources(location);
                int count = loadBeanDefinitions(resources);
                return count;
   
    }

通过分析,可知,resourceLoader可以帮助将配置文件的文件名,转为Spring通用的Resource抽象。

其中XML文件的BeanDefinitionReader的类型为:XmlBeanDefinitionReader,重写了loadBeanDefinitions方法。

它使用EncodedResource解决xml文件的编码问题,然后读入文件,在doLoadBeanDefinitions(inputSource, encodedResource.getResource())中完成BeanDefinition的加载

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
    }

doLoadBeanDefinitions主要完成了将XML文件专为Docuemnt类型,然后注册读出的BeanDefinition:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);
            return count;
        }

registerBeanDefinitions实现如下:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }

protected void doRegisterBeanDefinitions(Element root) {
        preProcessXml(root);
        
        //实际解析XML Element生成BeanDefinition的位置
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

    }

可知从Document中读取Xml的rootElement,然后parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

循环遍历XML,获取XML中的元素,并解析Bean 定义。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

获取Element并解析封装成BeanDefinitionHolder,并注册到ReaderContext

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
    }
总结

默认从XML配置中读入Spring BeanDefinition,读取解析的过程如下:

  1. 在WebApplicationContext refresh()时,初始化WebApplicationContext内置的BeanFactory;
  2. 创建DefaultListableBeanFactory类型的BeanFactory;
  3. 使用XmlBeanDefinitionReader读取Xml Resource,内部对XML文件解析,针对每个ELement,解析为BeanDefinition,然后封装成BeanDefinitionHolder;
  4. 将BeanDefinition注册到BeanFactory上;

--Posted from Rpc

转载于:https://my.oschina.net/hgfdoing/blog/3095994

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值