Spring 源码学习~12、容器的拓展功能简介

容器的拓展功能简介

一、简介

ApplicationContext 和 BeanFactory 两者都是用于加载 bean 的,相比之下,ApplicationContext 除了包含 BeanFactory 的所有功能之外,还提供了更多的扩展功能。

那么 ApplicationContext 比 BeanFactory 多出了哪些功能呢?

写法上的不同:

BeanFactory

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/app-context.xml"));
TestBean testBean = (TestBean) beanFactory.getBean("testBean");
System.out.println(testBean);

ApplicationContext

ApplicationContext bf = new ClassPathXmlApplicationContext("spring/app-context.xml");
TestBean testBean1 = (TestBean)bf.getBean("testBean");
System.out.println(testBean1);

下面我们以 ClassPathXmlApplicationContext 为切入点,来对整体功能进行分析。

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   
    this(new String[]{
   configLocation}, true, (ApplicationContext)null);
}

public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
   
    this(configLocations, true, (ApplicationContext)null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
   
    super(parent);
    this.setConfigLocations(configLocations);
    if (refresh) {
   
        this.refresh();
    }

}

可以看到首先是设置路径,然后解析和功能的实现都在 refresh() 中实现。

二、设置配置路径

ClassPathXmlApplicationContext 支持多个配置文件以数组的形式同时传入:

public void setConfigLocations(@Nullable 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) {
   
            //解析给定路径
            this.configLocations[i] = this.resolvePath(locations[i]).trim();
        }
    } else {
   
        this.configLocations = null;
    }

}

如果数组中包含特殊符号,如${var},那么在 resolvePath 中会搜寻匹配的系统变量进行替换。

三、核心方法 refresh 简介

设置路径之后,便可以根据路径对配置文件进行解析,以及各种功能的实现了,我们来分析 refresh() 函数。

public void refresh() throws BeansException, IllegalStateException {
   
    synchronized(this.startupShutdownMonitor) {
   
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        //准备刷新的上下文环境
        this.prepareRefresh();
        //初始化 beanFactory,并进行 XML 文件读取
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        //对 beanFactory 进行各种功能填充
        this.prepareBeanFactory(beanFactory);

        try {
   
            //子类覆盖方法做额外处理
            this.postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            
            //激活各种 BeanFactory 处理器
            this.invokeBeanFactoryPostProcessors(beanFactory);
            
            //注册拦截 Bean 创建时的后置处理器,这里只是注册,真正的调用是在 getBean 的时候
            this.registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            //为上下文初始化 Message 源,即不同语言的消息体,国际化处理
            this.initMessageSource();
            
            //初始化应用消息广播器,并放入 “ApplicationEventMulticaster” bean 中
            this.initApplicationEventMulticaster();
            
            //留给子类来初始化其他的 bean
            this.onRefresh();
            
            //在所有注册的 bean 中查找 Listener bean,注册到消息广播器中
            this.registerListeners();
            
            //初始化剩下的单实例(非惰性的)
            this.finishBeanFactoryInitialization(beanFactory);
            
            //完成刷新过程,通知声明周期处理器 lifecycleProcessor 刷新过程,同时发出
            //ContextRefreshEvent 通知别人
            this.finishRefresh();
        } catch (BeansException var10) {
   
            if (this.logger.isWarnEnabled()) {
   
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
            }

            //出异常,销毁所有 bean
            this.destroyBeans();
            //取消刷新
            this.cancelRefresh(var10);
            throw var10;
        } finally {
   
            //重置缓存
            this.resetCommonCaches();
            contextRefresh.end();
        }

    }
}

下面概括一下 ClassPathXmlApplicationContext 初始化的步骤:

  • 1、初始化前的准备工作。如:对系统属性或环境变量进行准备及验证
  • 2、初始化 BeanFactory,并进行 XML 文件读取。
  • 3、对 BeanFactory 进行各种功能填充。
    • @Qualifier 和 @Autowired 正是在这一步骤中增加的支持。
  • 4、子类覆盖方法做额外处理。
    • Spring 中随处可见到一些可拓展的空函数,如 postProcessBeanFactory 函数可以方便程序员在业务上做进一步的拓展。
  • 5、激活各种 BeanFactory 处理器。
  • 6、注册拦截 bean 创建的 bean 处理器,这里只是注册,真正的调用是在 getBean 的时候
  • 7、为上下文初始化 Message 源,即不同语言的消息体,国际化处理
  • 8、初始化应用消息广播器,并放入 “ApplicationEventMulticaster” bean 中
  • 9、留给子类来初始化其他的 bean
  • 10、在所有注册的 bean 中查找 Listener bean,注册到消息广播器中
  • 11、初始化剩下的单实例(非惰性的)
  • 12、完成刷新过程,通知声明周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人

接下来具体分析下每个步骤的具体功能。

四、环境准备

prepareRefresh 函数主要是做些准备工作,例如对系统属性及环境变量的初始化及验证。

protected void prepareRefresh() {
   
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);
    if (this.logger.isDebugEnabled()) {
   
        if (this.logger.isTraceEnabled()) {
   
            this.logger.trace("Refreshing " + this);
        } else {
   
            this.logger.debug("Refreshing " + this.getDisplayName());
        }
    }

    //留给子类覆盖
    this.initPropertySources();
    //验证需要的属性文件是否已经放入到环境中
    this.getEnvironment().validateRequiredProperties();
    if (this.earlyApplicationListeners == null) {
   
        this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
    } else {
   
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }

    this.earlyApplicationEvents = new LinkedHashSet();
}

咋一看这个步骤好像没什么作用,因为 initPropertySources() 是空函数,this.getEnvironment().validateRequiredProperties() 也因为没有验证的属性而没有做任何处理。但是如果这个函数用好了,作用还是很大的,我们需要来探讨下怎么使用它?首先我们研究下这个函数里面各个步骤的作用。

  • 1、initPropertySources 符合开放式结构设计,给用户最大扩展 Spring 的能力。用户可以根据自身的需要重写 initPropertySources 方法,并在方法中进行个性化的属性处理及设置。
  • 2、validateRequiredProperties 是对属性进行验证。

我们举例说明 validateRequiredProperties 的验证功能。

**场景描述:**工程运行过程中用到的某个设置(例如 VAR)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程不会工作。这样的场景下,我们可以自定义类:

package com.luo.spring.guides.helloworld.applicationcontext.validate.required;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : archer
 * @date : Created in 2022/11/29 16:03
 * @description :
 */
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

    public MyClassPathXmlApplicationContext(String... configLocations){
        super(configLocations);
    }

    @Override
    protected void initPropertySources() {
        //添加验证要求
        getEnvironment().setRequiredProperties("VAR");
    }
}

测试

package com.luo.spring.guides.helloworld.applicationcontext.validate.required;

import com.luo.spring.guides.helloworld.common.TestBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : archer
 * @date : Created in 2022/11/29 16:07
 * @description :
 */
public class Main {
   

    public static void main(String[] args) {
   
        ApplicationContext bf = new MyClassPathXmlApplicationContext("spring/app-context.xml");
        TestBean testBean1 = (TestBean)bf.getBean("testBean");
        System.out.println(testBean1);
    }
}

//输出 Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [VAR]

五、加载 BeanFactory

obtainFreshBeanFactory 是实现 BeanFactory 的地方,经过此函数后,ApplicationContext 就包含了 BeanFactory 的全部功能了。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   
    //初始化 BeanFactory ,并进行 XML 文件读取,并将得到的 BeanFactory 记录到当前实体的属性中
    this.refreshBeanFactory();
    // 返回 BeanFactory
    return this.getBeanFactory();
}

protected final void refreshBeanFactory() throws BeansException {
   
    if (this.hasBeanFactory()) {
   
        this.destroyBeans();
        this.closeBeanFactory();
    }

    try {
   
        //创建 DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = this.createBeanFactory();
        
        //为了序列化指定 id,如果需要的话,让 BeanFactory 从 id 反序列化到 BeanFactory 对象
        beanFactory.setSerializationId(this.getId());
        
        //定制 BeanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象、循环依赖
        this.customizeBeanFactory(beanFactory);
        //初始化 DocumentReader,并进行 XML 文件读取及解析
        this.loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    } catch (IOException var2) {
   
        throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
    }
}

我们详细分析上面每个步骤。

  • 1、创建 DefaultListableBeanFactory。
    • 之前使用的 XmlBeanFactory 继承自 DefaultListableBeanFactory,并提供了 XmlBeanDefinitionReader 类型的 reader 属性,所以 DefaultListableBeanFactory 是基础,这里就首先实例化。
  • 2、指定序列化 ID
  • 3、定制 BeanFactory。
  • 4、加载 BeanDefinition。
  • 5、使用全局变量记录 BeanFactory 类实例。

1、定制 BeanFactory

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   
    //如果属性 allowBeanDefinitionOverriding 不为 null,设置给 beanFactory 对象相应属性,
    //此属性含义:是否允许覆盖同名称的不同定义的对象
    if (this.allowBeanDefinitionOverriding != null) {
   
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    
    //如果属性 allowCircularReferences 不为 null,设置给 beanFactory 对象相应属性,
    //此属性含义:是否允许 bean 之间存在循环依赖
    if (this.allowCircularReferences != null) {
   
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}

想要实现相应的设置,可以自己在实现的子类中使用方法覆盖:

package com.luo.spring.guides.helloworld.applicationcontext.validate.required;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : archer
 * @date : Created in 2022/11/29 16:03
 * @description :
 */
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
   

    public MyClassPathXmlApplicationContext(String... configLocations){
   
        super(configLocations);
    }

    @Override
    protected void initPropertySources() {
   
        //添加验证要求
        getEnvironment().setRequiredProperties("VAR");
    }

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   
        super.setAllowBeanDefinitionOverriding(false);
        super.setAllowCircularReferences(false);
        super.customizeBeanFactory(beanFactory);
    }
}

2、加载 BeanDefinition

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

    // Configure the bean definition reader with this context's
    // resource loading environment.
    //对 beanDefinitionReader 进行环境变量的设置
    beanDefinitionReader.setEnvironment(this.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.
    //对 BeanDefinitionReader 进行设置,可以被子类覆盖
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

在初始化 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 后,就可以进行配置文件的读取了。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
   
        reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
   
        reader.loadBeanDefinitions(configLocations);
    }
}

到 XmlBeanDefinitionReader#loadBeanDefinitions 这一步,就到之前我们分析 XmlBeanFactory 解析配置文件的步骤了,这里不再赘述。

六、功能扩展

进入 prepareBeanFactory 前,Spring 已经完成了对配置的解析,而 ApplicationContext 在功能上的扩展也由此展开。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   
    // Tell the internal bean factory to use the context's class loader etc.
    //设置 beanFactory 的 classLoader 为当前 context 的classLoader
    beanFactory.setBeanClassLoader(getClassLoader());
    if (!shouldIgnoreSpel) {
   
        //设置 beanFactory 的表达式语言处理器,Spring 3 增加了表达式语言的支持
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    }
    //为 beanFactory 增加一个默认的 PropertyEditor,这个主要是对 bean 的属性等设置管理的一个工具
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // Configure the bean factory with context callbacks.
    //增加 BeanPostProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    //设置几个忽略自动装配的接口
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);

    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    //设置几个自动装配的特殊规则
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    //将用于检测内部 bean 的早期后处理器注册为 ApplicationListeners。
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    //检测 LoadTimeWeaver 和 增加对 AspectJ 的支持
    if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
   
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        //为类型匹配设置一个临时的ClassLoader。
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // Register default environment beans.
    //注册默认的系统环境系列的 bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
   
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
   
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
   
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
    if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
   
        beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
    }
}

上面函数主要增加了如下拓展:

  • 1、增加对 SpEl 语言的支持。
  • 2、增加对属性编辑器的支持
  • 3、增加对一些内置类,比如 EnvironmentAware、MessageSourceAware 的信息注入的支持。
  • 4、设置了依赖功能可忽略的接口。
  • 5、注册一些固定依赖的属性。
  • 6、增加对 AspectJ 的支持。
  • 7、将相关环境变量即属性注册以单例模式注册。

1、增加对 SpEl 语言的支持

beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

Spring 表达式语言全称为 Spring Expression Language,简称 SpEl。它能在运行时构建复杂表达式、存取对象图属性、对象方法调用等,并且能与 Spring 功能完美整合(比如用来配置 bean 定义)。SpEL 是单独的模块,只依赖于 core 模块。可以单独使用。

示例

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.luo.spring.guides.common.TestBean"/>

    <bean id="spelTestBean" class="com.luo.spring.guides.spel.SpelTestBean">
        <property name="testBean" value="#{testBean}"/>
    </bean>
</beans>

等价于

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.luo.spring.guides.common.TestBean"/>

    <bean id="spelTestBean" class="com.luo.spring.guides.spel.SpelTestBean">
        <property name="testBean" ref="testBean"/>
    </bean>
</beans>

源码中是通过 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())) 来注册语言解析器。

注册完后是什么时候调用此解析器来解析的呢?Spring 中在 bean 进行初始化的时候会有属性填充这一步,这一步中的 AbstractAutowireCapableBeanFactory 类的 applyPropertyValues 函数就是用来完成这个调用解析器解析的功能的。具体源码是在下面这一步:

@Nullable
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
   
    if (this.beanExpressionResolver == null) {
   
        return value;
    }

    Scope scope = null;
    if (beanDefinition != null) {
   
        String scopeName = beanDefinition.getScope();
        if (scopeName != null) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值