专栏首页Java阿呆Spring源码之核心容器

Spring源码之核心容器

Spring核心容器体系

BeanFactory

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

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

BeanFactory源码如下:

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来描述的,其继承体系如下:

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

IOC容器的初始化

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

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

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

    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容器所做的主要源码如下:
    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资源加载器:

    public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");
        //设置Spring的资源加载器
        this.resourceLoader = resourceLoader;
    }

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

    //处理单个资源文件路径为一个字符串的情况
    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定义的载入过程:

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();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 单例模式(Singleton Pattern)

    单例模式是最简单的设计模式之一,这种设计模式是一种创建型的模式,提供了创建对象的最佳方式。

    Java阿呆
  • java NIO浅析

    NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器)。传统IO基于字节流和字符流进行操作,而NIO基于Chann...

    Java阿呆
  • Docker基础(一)

    格式:docker [image] pull NAME[:TAG] 例如:获取一个Ubuntu18.04系统的基础镜像:$ docker pull ubuntu...

    Java阿呆
  • 从非唯一对策降为布尔唯一对策(CS CC)

    本文将证明“布尔唯一对策猜想”(对于任何C> 1,间隙为1-delta相对于1-C * delta,并且足够小的delta> 0)的问题简化为证明一个非唯一对策...

    用户7454091
  • 微服务重要的容器技术Docker的核心实现技术

    Docker 作为一种容器虚拟化技术,应用了操作系统的多项底层支持技术。其中的技术层包含Linux操作系统的命名空间Namespace,控制组,联合文件系统,...

    用户2196435
  • Python-tkinter图形界面设计初探

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    bering
  • Kafka组消费之Rebalance机制

    《Kafka重要知识点之消费组概念》讲到了kafka的消费组相关的概念,消费组有多个消费者,消费组在消费一个Topic的时候,kafka为了保证消息消费不重不漏...

    大数据技术与架构
  • nginx rewrite 跨域

    作用域:server 、location、if 功能:如果一个URI匹配指定的正则表达式regex,URI就按照 replacement 重写。 rewri...

    IT云清
  • 接触到的git的三个重要知识点

    卡少
  • 你不曾见过的酷炫地图可视化作品(二)

    本文是《你不曾见过的酷炫地图可视化作品(一)》的系列文章,上回整理了诸如:世界顶级学者全球迁移图、百度春运迁徙图、中国人口分布图、中美博物馆分布情况图、移民图、...

    古柳_DesertsX

扫码关注云+社区

领取腾讯云代金券