前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring源码之核心容器

Spring源码之核心容器

作者头像
Java阿呆
发布2020-11-04 15:13:10
3550
发布2020-11-04 15:13:10
举报
文章被收录于专栏:Java阿呆Java阿呆

Spring核心容器体系

BeanFactory

SpringBean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,其相互关系如下:

BeanFactory继承体系
BeanFactory继承体系

其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是最终的默认实现类是DefaultListableBeanFactory,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如ListableBeanFactory接口表示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。AutowireCapableBeanFactory接口定义Bean的自动装配规则。这三个接口共同定义了Bean的集合、Bean之间的关系、以及Bean的行为。

BeanFactory源码如下:

代码语言:javascript
复制
public interface BeanFactory {
    //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
    //如果需要得到工厂本身,需要转义
    String FACTORY_BEAN_PREFIX = "&";

    //根据bean的名字,获取在IOC容器中得到bean实例
    Object getBean(String name) throws BeansException;
    //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    Object getBean(String name. Object...args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    //提供对bean的检索,看看是否在IOC容器有这个名字的bean
    boolean corrtainsBean(String name);

    //根据bean名字得到bean实例,并同时判断这个bean是不是单例
    boolean isSingleton(String nane) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String nane) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String nane, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String nane, @Nullable Class<?> typeToMatch) throws NoSuc hBeanDefinit ionExcept ion;

    //得到bean实例的Class类型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
    String[] getAliases(String nane);

在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的Bean是如何定义怎样加载的。正如我们只关心从工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口并不关心。 而要知道工厂是如何产生对象的,需要看具体的IOC容器实现,Spring提供了许多IOC容器的实现。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是针对最基本的IOC容器的实现,这个IOC容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述),如果说XmlBeanFactory是容器中的屌丝,ApplicationContext应该算容器中的高帅富。ApplicationContext是Spring提供的一个高级的IOC容器,它除了能够提供IOC容器的基本功能外,还为用户提供了以下的附加服务:

  1. 支持信息源,可以实现国际化。(实现MessageSource接口)
  2. 访问资源。(实现ResourcePatternResolver接口)
  3. 支持应用事件。(实现ApplicationEventPublisher接口)

BeanDefinition

SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:

BeanDefinition继承体系
BeanDefinition继承体系

Bean的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过下图中的类完成:

配置文件解析
配置文件解析

IOC容器的初始化

IOC容器的初始化包括BeanDefinition的Resource定位载入注册这三个基本的过程。以ApplicationContext为例,ApplicationContext系列容器是最常用的,因为Web项目中使用的XmlWebApplicationContext就属于这个继承体系,还有ClasspathXmlApplicationContext等,其继承体系如下图所示:

ApplicationContext继承体系
ApplicationContext继承体系

ApplicationContext允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于Bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的Bean定义环境。

以FileSystemXmlApplicationContext为例,看一下初始化流程:

代码语言:javascript
复制
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }
    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }
    //实际都是在调用这个构造函数
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

通过分析FileSystemXmlApplicationContext的源代码可以知道,在创建FileSystemXmlApplicationContext容器时,构造方法做以下两项重要工作:

  • 首先,调用父类容器(一直传递到AbstractApplicationContext)的构造方法(super(parent)方法)为容器设置好Bean资源加载器。 然后,再调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法设置Bean定义资源文件的定位路径。 通过追踪FileSystemXmlApplicationContext的继承体系,发现其父类的父类AbstractApplicationContext中初始化IOC容器所做的主要源码如下:
代码语言:javascript
复制
    public abstract class AbstractApplicationContext extends DefaultResourceLoader
            implements ConfigurableApplicationContext {

        //静态初始化块,在整个容器创建过程中只执行一次
        static {
            // 为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题,加载IOC容
            // 器关闭事件(ContextClosedEvent)类
            ContextClosedEvent.class.getName();
        }
        //super方法一直传递到这里
        public AbstractApplicationContext(ApplicationContext parent) {
            this();
            setParent(parent);
        }
        // 第一步调用空构造,进一步调用getResourcePatternResolver方法
        public AbstractApplicationContext() {
            this.resourcePatternResolver = getResourcePatternResolver();
        }

        //获取一个Spring Source的加载器用于读入Spring Bean定义资源文件
        protected ResourcePatternResolver getResourcePatternResolver() {
            //AbstractApplicationContext 继承 DefaultResourceLoader,因此也是一个资源加载器
            //Spring资源加载器,其getResource(String location)方法用于载入资源
            return new PathMatchingResourcePatternResolver(this);
        }
        /** 省略很多代码*/
    }

AbstractApplicationContext构造方法中调用PathMatchingResourcePatternResolver的构造方法创建Spring资源加载器:

代码语言:javascript
复制
    public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");
        //设置Spring的资源加载器
        this.resourceLoader = resourceLoader;
    }

在设置完容器的资源加载器之后,接下来FileSystemXmlApplicationContext执行setConfigLocations方法,通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位,该方法的源码如下:

代码语言:javascript
复制
    //处理单个资源文件路径为一个字符串的情况
    public void setConfigLocation(String location) {
        //String CONFIG_LOCATION_DELIMITERS =    ", ; \t\n”;
        //即多个资源文件路径之间用", ; \t\n”分隔,解析成数组形式
        setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
    }
    //解析Bean定义资源文件的路径,处理多个资源文件字符串数组
    public void setConfigLocations(String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
            for (int i = 0; i < locations.length; i++) {
                // resolvePath 同一个类中将字符串解析为路径的方法
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }

也就是说既可以使用一个字符串来配置多个SpringBean定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的: ClasspathResource res=new ClasspathResource(“a.xml,b.xml,……”);多个资源文件路径之间可以是用”,;\t\n”等分隔。 ClasspathResource res=new ClasspathResource(newString[]{“a.xml”,”b.xml”,……}); 至此,SpringIOC容器在初始化时将配置的Bean定义资源文件定位为Spring封装的Resource。

接下来,FileSystemXmlApplicationContext执行refresh函数,SpringIOC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法,refresh()方法的作用是:在创建IOC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IOC容器。refresh的作用类似于对IOC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。

FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IOC容器对Bean定义的载入过程:

代码语言:javascript
复制
public void refresh  throws BeansExceptlon, IllegalStateException (
    synchronized (this.startupShutdownMonitor) (
    //调用容器准备刷新的方法,获取容器的当时时间.同时给容器设置同步标识
    prepareRefresh();
    //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
    //子类的refreshBeanFactory()方法启动
    ConfigurableListableBeanFactory beanFactory = obtalnFreshBeanFactory();
    //为BeanFactory配置容器特性,例如类加载器、事件处理器等
    prepareBeanFactory(beanFactory);
    try {
        //为容器的某些于类指定特殊的BeanPost事件处理器
        postProcessBeanFactory(beanFactory);
        //调用所有注册的 BeanFactoryPostProcessor 的 Bean
        InvokeBeanFactoryPostProcessors(beanFactory);
        //为BeanFactory注朋BeanPost事件处理悬.
        //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
        registerBeanPostProcessors(beanFactory);
        //初始化信息源,和国际化相关.
        lnitMessageSource();
        //初始化容器事件传播器.
        InitAppllcationEventHulticaster();
        //调用子类的某些特殊Bean初始化方法
        onRefresh();
        //为事件传播器注明事件监听器.
        registerListeners();
        //初始化所有剩余的单例Bean
        flnishBeanFactorylnitialization(beanFactory);
        //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
        finishRefresh();
    }
    catch (BeansException ex) {
        if (logger.isMamEnabled() {
        logger.wam("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex);
    }
    //x销毁已创建的Bean
    destroySeans();
    // 取消refresh 操作,重置容器的同步标识.
    cancelR«fresh(ex);
    throw ex;
    }
    finally {
        resetComonCaches ();
    }
}

refresh()方法主要为IOC容器Bean的生命周期管理提供条件,SpringIOC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory=obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。

代码语言:javascript
复制
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3raoq7cjs82so

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring核心容器体系
    • BeanFactory
      • BeanDefinition
      • IOC容器的初始化
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档