Spring源码-启动过程

Spring源码分析(一)-Spring环境的初始化

Spring的框架特征

以下摘自知道词条
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
我们将分章节介绍Spring是如何实现这些功能的。

Spring是如何初始化的

我们知道在WEB项目中集成Spring是需要在web.xml中添加一个监听器

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

非Web项目:

java
public static void main(String[] args) {

//所有配置文件
args = new String[] {
"classpath:spring/spring-servlet.xml",
"classpath:spring/ApplicationContext.xml",
"classpath:spring/mybatis-config.xml",
};
ApplicationContext actx = new ClassPathXmlApplicationContext(args);
//得到类的实例
UserService userService = (UserService) actx.getBean("userService");
//调用类的方法
userService.deleteUser(2);
}

这里先以非Web项目为准,虽说Spring最长使用场景是在Web项目中,但是非Web项目这种用法更直接,更易跟踪。

看一下这个构造方法,歌词大意是创建一个ClassPathXmlApplicationContext,并从指定的配置文件加载定义,并自动的刷新Context(上下文)

java
    /** * Create a new ClassPathXmlApplicationContext, loading the definitions * from the given XML file and automatically refreshing the context. * @param configLocation resource location * @throws BeansException if context creation failed */
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

跟踪下来看看多参构造器

java
    /** * Create a new ClassPathXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of resource locations * @param refresh whether to automatically refresh the context, * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * @param parent the parent context * @throws BeansException if context creation failed * @see #refresh() */
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent); 
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

super(ApplicationContext parent) 暂时不管,因为我们通过那个构造器转进来,parent参数是null
setConfigLocations(configLocations); 这个是初始化context中的配置文件路径
refresh() 追进去看下

其实这个方法定义在父类org.springframework.context.support.AbstractApplicationContext 中,从这代码的书写方式上是可以看出Spring一些加载流程的(中文我自己加的,渣英语)

java
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing. //为正在进行的刷新准备bean工厂
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory. //告诉子类去刷新内部的beanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context. 
            // 在当前上下文中,准备Bean工厂
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context
                // subclasses.
                // 在bean被装载后,提供一个修改BeanFactory的入口 ,即允许子类做一些后处理
                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.
                // 检查是否有监听器bean并注册他们
                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.
                // 销毁一些已经创建的bean
                destroyBeans();

                // Reset 'active' flag.
                // 重置'active'标记
                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();
            }
        }
    }

从以上代码,大概可知一个Spring上下文启动或刷新的过程

java
/** * Prepare this context for refreshing, setting its startup date and active * flag as well as performing any initialization of property sources. */
    protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false); //AtomicBoolean 类型 
        this.active.set(true);  //AtomicBoolean 类型 

        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }

        // Initialize any placeholder property sources in the context
        // environment
        initPropertySources();

        // Validate that all properties marked as required are resolvable
        // see ConfigurablePropertyResolver#setRequiredProperties
        getEnvironment().validateRequiredProperties();

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
    }

obtainFreshBeanFactory 看方法名是一个刷新Bean工厂的方法,其方法签名为 protected ConfigurableListableBeanFactory obtainFreshBeanFactory()
结合Spring源码注释 “Tell the subclass to refresh the internal bean factory.” ,方法含义为刷新并返回内部BeanFactory工厂

/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory(); //静态方法,子类覆写
        ConfigurableListableBeanFactory beanFactory = getBeanFactory(); //静态方法子类覆写
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

那么实现该方法的子类是谁呢?,回到ClassPathXmlApplicationContext,看看其UML图
《Spring源码-启动过程》
查看refreshBeanFactory的子类覆写情况,如图,明显是org.springframework.context.support.AbstractRefreshableApplicationContext 这个类
《Spring源码-启动过程》

通过代码看看BeanFactory是如何刷新的

/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {//判断是否已经有BeanFactory
            destroyBeans();  //销毁单例bean,代码(getBeanFactory().destroySingletons()),具体是如何销毁的由具体的BeanFactory去实现
            closeBeanFactory(); //关闭BeanFactory,直接设置实例变量(this.beanFactory)的引用为null
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory(); //创建一个新的BeanFactory
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory); //干了两个事,1:allowBeanDefinitionOverriding(是否允许ID相同的Bean覆盖,不指定时默认为true) 2.allowCircularReferences(是否允许循环引用,不指定时默认为true)
            loadBeanDefinitions(beanFactory); //加载Bean的定义(读取配置文件,并向bean工厂中注册Bean)customizeBeanFactory中设置的作用会在读取Bean定义并注册的时候体现
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory; //将新的Bean工厂注册给当前的Context
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

createBeanFactory();方法中创建了一个beanFactory,Factory实现与层级结构暂不分析,只看Spring的启动准备,loadBeanDefinitions(beanFactory);加载并注册Bean,refreshBeanFactory()主要干了两个事,先把Context中原来的工厂干掉,在重新创建和初始化一个新的Beanfactory.

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/weiythi/article/details/74194083
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞