Spring 的核心组件详解

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

      Spring总共有十几个组件,但是真正核心的组件只有三个:Core、Context 和 Bean。它们构建起了整个Spring的骨骼架构,没有它们就不可能有AOP、Web等上层的特性功能。

一、Spring的设计理念


      Bean组件是Spring核心中的重点,Spring就是面向Bean编程的(Bean Oriented Programming,BOP)就像Object对OOP的意义一样,没有对象的概念就像没有面向对象的编程,在Spring中没有Bean也就没有Spring存在的意义。我们使用Spring的主要一个原因就是Spring会把对象之间的依赖关系转而用配置文件来管理。也就是依赖注入机制。而这个注入关系在一个叫IOC的容器中管理,而IOC容器就是被Bean包裹的对象。Spring正是通过把对象包装在Bean中从而达到管理这些对象及做一系列额外操作的目的。
      前面如果把Bean比作一场演出中的演员,Context就是这场演出的舞台背景,而Core应该就是演出的道具了。Bean包装的是Object,而Object必然有数据,如何给这些数据提供生存环境就是Context要解决的问题,对Context来说它就是要发现每个Bean之间的关系并维护好这种关系。所以Context就是一个Bean关系的集合,这种关系集合又叫IOC容器。Core组件用来发现、建立和维护每个Bean之间所需的一系列工具,把Core组件也可以看成是Util。
 

二、Bean组件


      Bean组件在Spring的org.springframework.beans包下,在这个包下的所有类主要解决了3件事:Bean的定义、Bean的创建及对Bean的解析。对Spring使用者来说唯一需要关心的就是Bean的创建,其他两个由Spring在内部完成。BeanDefination:Bean的定义完整的描述了在Spring配置文件中定义的<bean/>节点中所有的信息,包括各种子节点。当Spring成功解析<bean/>节点后,在Spring内部它就被转化成BeanDefinition对象,以后所有操作都是对这个对象操作。
      Bean的解析过程非常复杂,功能被分的很细。因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析

三、Context组件


      Context在Spring的org.springframework.context包下,给Spring提供一个运行时的环境,用于保存各个对象的状态。ApplicationContext是Context的父类,它除了能标识一个应用环境的基本信息外,还集成了5个接口来扩展Context的功能。例如:通过继承BeanFactory表明容器中运行的主体对象是Bean,另外继承了ResourceLoader接口,使得ApplicationContext可以访问外部资源(在Core中说明)。
      ApplicationContext的子类主要包含两个方面:
         ●  ConfigurableApplicationContext表示该Context是可修改的,也就是在构建Context中,用户可以动态添加或修改已有的配置信息,其中最经常使用的是可更新的Context,即AbstractRefreshableApplicationContex类。
         ●  WebApplicationContext 为Web装备的Context,可直接访问ServletContext。
      再往下分别就是构建Context的文件类型,接着就是访问Context的方式,这样一级一级构成了完成的Context等级层次。
      总体来说ApplicationContext必须要完成一下几件事情:
         ✔  标识一个应用环境
         ✔  利用BeanFactory创建Bean对象
         ✔  保存对象关系表
         ✔  能够捕捉各种事件
      Context作为Spring的IOC容器,基本上整合了Spring的大部分功能,或者说是大本分功能的基础。

四、Core组件


      Core组件作为Spring的核心组件,其中包含了很多关键类,例如:定义了资源的访问方式。这种将所有资源都抽象成一个接口的方式很值得以后的设计中拿来学习。
      Context与Core之间的关系:比如Context一般会把资源的加载、解析和描述工作委托给ResourcePatternResolver类来完成,它相当于一个接头人,把资源的加载、解析和资源的定义整合在一起便于其他组件使用,在Core组件中还有很多类似的方式。

五、IOC容器如何工作


      IOC容器实际上是Context组件结合其他两个组件共同构建了一个Bean关系网,如何构建这个关系网,构建的入口就在AbstractApplicationContext类的refresh方法中,这个方法代码如下:https://www.cnblogs.com/GooPolaris/p/8184429.html

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 为刷新做准备的 Context
        prepareRefresh();
        // 刷新所有BeanFactory子容器
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 创建BeanFactory
        prepareBeanFactory(beanFactory);

        try {
            // 注册实现了BeanPostProcess接口的Bean
            postProcessBeanFactory(beanFactory);
            // 初始化和执行BeanFactoryPostProcess beans
            invokeBeanFactoryPostProcessors(beanFactory);
            // 初始化和执行BeanPostProcess beans
            registerBeanPostProcessors(beanFactory);
            // 初始化 message source
            initMessageSource();
            // 初始化 event multicaster
            initApplicationEventMulticaster();
            // 刷新子类实现的方法
            onRefresh();
            // 检查注册事件
            registerListeners();
            // 初始化 (non-lazy-init) 单例Bean
            finishBeanFactoryInitialization(beanFactory);
            // 执行LifecycleProcessor.onRefresh()和ContextRefreshedEvent事件
            finishRefresh();
        }

        catch (BeansException ex) {
            destroyBeans();//如果刷新失败那么就会将已经创建好的单例Bean销毁掉
            cancelRefresh(ex);//重置context的活动状态
            throw ex;//抛出异常
        }
    }
}

       这个方法就是构建整个IOC容器过程的完整代码,了解里面的每一行代码,基本上就了解了大部分Spring的原理和功能。这段主要包含一下步骤:
      1)、构建BeanFactory,以便于产生所需的“演员”。
      2)、注册可能感兴趣的事件。
      3)、创建Bean实例对象。
      4)、触发被监听的事件。
      首先创建和配置BeanFactory,这里是refresh,也就是刷新配置。前面介绍了Context有可更新的子类,这里正是实现这个功能,当BeanFactory存在时就更新,如果不存在就新创建。更新BeanFactory的方法如下:

    @Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {//如果已经存在一个bean工厂那么就将其销毁,关闭。
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();//新建一个Bean工厂。
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);//自定义bean工厂。
            loadBeanDefinitions(beanFactory);//加载BeanDefinition
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;//将创建好的bean工厂的引用交给的context来管理。
            }
        }
        catch (IOException ex) {//加载bean定义资源的时候可能会抛出异常。
            throw new ApplicationContextException("I/O error parsing bean definition source for " 
                        + getDisplayName(), ex);
        }
    }

      这个方法实现了AbstractApplictionContext的抽象方法refreshBeanFactory,这段代码清楚的说明了BeanFactory的创建过程。注意BeanFactory对象的类型的变化,BeanFactory有很多子类,在什么情况下使用非常关键。BeanFactory的原始对象是DefaultListableBeanFactory,这个非常关键,因为它涉及后面对这个对象的多种操作,下面看一下这个类的继承关系图:

      从上图可以发现除了BeanFactory相关类外,还发现与Bean的register相关的类。这在refreshBeanFactory方法的loadBeanDefinitions(beanFactory)一行将找到答案,这个方法将加载、解析Bean的定义,也就是把用户定义的数据结构转化为IOC容器中的特定数据结构。
     创建好BeanFactory后,添加一些Spring本身需要的工具类,这个操作在AbstractApplictionContext的prepareBeanFactory的prepareBeanFactory方法中完成。
      在AbstractApplicationContext中接下来的3行代码Spring的功能扩展性能起了至关重要的作用。前两行主要是让你现在可以对已经构建的BeanFactory的配置做修改,后面一行就是让你可以对以后再创建Bean的实例对象时添加一些自定义的操作。所以它们都扩展了Spring的功能,要学习Spring必须搞清楚这一部分。
      其中在invokeBeanFactoryPostProcessors方法中主要是获取实现BeanFactoryPostProcess接口的子类,并执行它的postProcessBeanFactory方法,这个方法的声明如下:

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException

      它的参数是beanFactory,说明可以对beanFactory做修改,beanFactory是ConfigurableListableBeanFactory类型的,这也印证了前面介绍的不同BeanFactory所使用的场合不同,这里只能是可配置的BeanFactory,防止一些数据被用户随意修改。
     registerBeanPostProcess方法也可以获取用户定义的实现了BeanPostProcessor接口的子类,并把他们注册到BeanFacotry对象中的beanPostProcessor变量中。在BeanPostProcessor中声明了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别用于Bean对象初始化时执行,可以执行用户自定义的操作。
      后面几行代码是初始化监听事件和对系统的其他监听者的注册,监听者必须是ApplicationListener的子类。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	//不使用TempClassLoader
	beanFactory.setTempClassLoader(null);
	//禁止修改当前Bean的配置信息
	beanFactory.freezeConfiguration();
	//实例化non-lazy-init类型的bean
	beanFactory.preInstantiateSingletons();
}

      从上面的代码中可以发现Bean的实例化是在BeanFactory中发生的。PreInstantiateSingletons方法的代码如下:

    @Override
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) { //日志(这是一种好的日志处理方式,当没有开启debug级别时,就会省下一次字符串拼接),spring源码里到处都是这样的处理,可以学习。
            this.logger.debug("Pre-instantiating singletons in " + this);
        }

        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        //拷贝一个新的list ,this.beanDefinitionNames 干嘛的,上文大概已有说明,可以结合《Spring 源码阅读 BeanFactory(二) 之registerBeanDefinition方法》一块看
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) { //遍历beanName
            //这里先解释一下getMergedLocalBeanDefinition方法的含义,因为这个方法会常常看到。Bean定义公共的抽象类是AbstractBeanDefinition,普通的Bean在Spring加载Bean定义的时候,实例化出来的是GenericBeanDefinition,而Spring上下文包括实例化所有Bean用的AbstractBeanDefinition是RootBeanDefinition,这时候就使用getMergedLocalBeanDefinition方法做了一次转化,将非RootBeanDefinition转换为RootBeanDefinition以供后续操作。
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//非抽象的,单例的,非懒加载的
                if (isFactoryBean(beanName)) { //Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean, 工厂Bean 再起炉灶
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else { //非工厂Bean 调用该方法
                    getBean(beanName);
                }
            }
        }
    }

      这里出现了一个Bean(Factory Bean),可以说Spring有一大半的扩展功能与这个Bean有关,它是一个工厂Bean,用于产生Bean实例,如果一个类继承FactoryBean,用户可以自定义产生对象的方法,只需要实现它的getObject方法即可。然而在Spring内部,这个Bean的实例对象是FactoryBean通过调用这个对象的getObject方法就能获取用户自定义产生的对象,从而为Spring提供了很好的扩展性。Spring获取FactoryBean本身的对象是通过在前面加上&来完成的。
      如何创建Bean的实例对象及如何构建Bean实例对象之间的关联关系是Spring中的一个核心,还有就是建立Bean对象实例之间的关系,也是Spring框架的核心竞争力。 Bean实例创建流程图如下:
     

六、IOC容器的扩展点


     对Spring的IOC容器来说,主要有BeanFactoryPostProcessor和BeanPostProcessor,他们分别在构建BeanFactory和构建Bean对象时调用。还有就是InItializingBeanDisposableBean,他们分别在Bean创建和销毁时调用。用户可以实现在这些接口中定义的方法,Spring会在适当的时候调用它们。还有一个是FactoryBean,它是个特殊的Bean,可以被用户更多的控制。
     这些扩展点通常也是我们使用Spring来完成特殊任务的地方,如何精通Spring就看是否掌握好Spring有哪些扩展点,以及如何使用它们。要知道如何使用它们就必须了解它们内在的机制。
      ApplicationContext.xml就是IOC容器的默认配置文件,Spring的所有特性功能都是基于IOC容器工作的。我们可以通过实现Spring的扩展点来改变Spring的通用行为。至于如何实现扩展点来得到我们想要的结果,在Spring中有很多例子,其中AOP的实现就是通过扩展点达到想要的特性功能,可以拿来参考。

猜你喜欢

转载自blog.csdn.net/zhengzhaoyang122/article/details/85098610