在IOC(1)中介绍了Spring Bean的生命周期,并引出了AbstractApplicationContext#refresh方法,本节将介绍refresh方法中的

  1. prepareRefresh:创建容器前的工作。
  2. obtainFreshBeanFactory: 创建BeanFactory
  3. prepareBeanFactory: 准备BeanFactory

prepareRefresh 方法

Spring的前戏准备大概就是做了以下几件事:

  • 设置容器的启动时间
  • 设置容器的停止状态为false
  • 设置容器的激活状态为true
  • 获取环境信息并验证必要的属性
  • 准备监听器和事件的容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
protected void prepareRefresh() {
// Switch to active.
// 设置启动时间 设置标识位
this.startupDate = System.currentTimeMillis();
// 设置容器停止标识为false
this.closed.set(false);
// 设置容器激活标识为true
this.active.set(true);

// ...日志

// Initialize any placeholder property sources in the context environment.
// 初始化上下文环境中的任何占位符属性源
// 留给子类进行扩展,比如添加必须的属性值验证
initPropertySources();

// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
// 获取环境对象,并验证需要的属性
getEnvironment().validateRequiredProperties();

// Store pre-refresh ApplicationListeners...
// 准备应用监听器和实践的容器初始化
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
// 如果不为空,那么就清空掉,并设置新的早期的监听器进去
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}

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

环境信息是何时设置进去的呢?

在容器启动时调用了父类构造函数时设置进去的,Environment他是一个接口,他有个重要的实现类叫StandardEnvironment ,在Spring启动的时候就会使用这个类进行环境信息的加载,最终他会调用到System#getPropertiesSystem#getenv方法,然后将加载到属性放在Map中进行保存。

img

要加载的信息就是系统的环境变量,比如在Windows中配置的环境变量或者启动类中使用-D参数配置的启动参数都会进行加载到StandardEnvironment 这个类中,类似于使用-Dxxx.name=123这种参数会加载到systemProperties中,配置的windows环境变量会加载systemEnvironment中。

创建BeanFactory

Spring创建 BeanFactory 的方式

按照Bean的配置方式手动创建可以分为两种:

  1. 使用XMl配置的Bean

    这种方式使用xml配置文件配置Bean的信息并且设置扫描的路径,扫描到的包可以使用注解进行配置Bean信息,一般来说手动创建BeanFactory容器的实现类为ClassPathXmlApplicationContextSystemFileXmlApplicationContext,设置xml的路径即可创建出IOC容器。

    1
    2
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-test.xml");
    User user = context.getBean(User.class);
  2. 使用注解配置的Bean

    这种方式不使用xml配置文件,全部基于注解方式配置Bean的信息,比如使用@Component@Configuration进行Bean的配置,实现类为AnnotationConfigApplicationContext 设置扫描的包,然后调用refresh方法进行IOC容器的创建。

    1
    2
    3
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.scan("com.redwinter.test");
    context.refresh();

我们在开发中一般是使用web容器进行IOC容器的创建,例如tomcat容器、jetty容器、undertow容器、netty容器。

Spring中有一个BeanFactory的实现类:GenericApplicationContext,他的子类有一个叫GenericWebApplicationContext,在Spring Boot中,就是通过实现这个类完成Web容器的创建+IOC容器的创建的。在Spring Boot中有个类叫ServletWebServerApplicationContext就是继承了GenericWebApplicationContext这个类,然后ServletWebServerApplicationContext中有个属性叫webServer,这个是一个接口,这个接口对应的实现就是Web容器的实现:

1
2
3
4
5
6
7
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
public static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
// web 容器,实现类有TomcatWebServer、JettyWebServer、NettyWebServer、UndertowWebServer
private volatile WebServer webServer;
// .... 去掉其他代码
}

创建IOC容器

Spring Bean IOC 的创建流程中的第二个方法:

1
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

这个方法会创建一个DefaultListableBeanFactory,默认的可列出Bean的工厂,主要功能是刷新BeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// AbstractApplicationContext
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}

// AbstractApplicationContext子类 AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
// 如果有BeanFactory 就销毁掉并关闭
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 直接new一个BeanFactory DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 根据上一步创建BeanFactory创建的Id进行获取
beanFactory.setSerializationId(getId());
// 定制化BanFactory ,比如设置allowBeanDefinitionOverriding 和allowCircularReferences 的属性
customizeBeanFactory(beanFactory);
// 加载BeanDefinitions 从xml 和注解定义的Bean,有从注解加载的
// 从configLocations -> String[] -> String -> Resource[] -> Resource -> InputStream -> Document -> 解析成一个一个的BeanDefinition 对象
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

BeanFactory的创建流程分为:

  1. 首先判断是否已经有BeanFactory了,如果有就销毁掉并且关闭工厂。
  2. 直接创建一个BeanFactory,默认就是使用DefaultListableBeanFactory,不过在创建的过程中可能会默认初始化一些属性,比如:allowBeanDefinitionOverridingallowCircularReferences 允许Bean覆盖和解决循环依赖的问题,还有就是BeanFactory的序列化id等属性。
  3. 设置序列化id
  4. customizeBeanFactory(beanFactory),定制BeanFactory,这里是一个扩展点,你可以对BeanFactory进行定制
  5. 加载BeanDefinition,这里从由他的子类进行实现。例如XML,Annoation等多种加载的方法。
1
2
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;

至此BeanFactory初始化完成

定制个性化的BeanFactory

customizeBeanFactory(beanFactory)这个方法中,spring设置了两个属性,一个是设置是否可以覆盖Bean,一个是否允许循环依赖。

1
2
3
4
5
6
7
8
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}

我们新建一个类继承ClassPathXmlApplicationContext,然后重写customizeBeanFactory这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

public MyClassPathXmlApplicationContext(String... configLocation) throws BeansException {
super(configLocation);
}

@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 扩展点 设置不去处理循环依赖或者beanDefinition覆盖
super.setAllowBeanDefinitionOverriding(true);
// 设置不允许循环依赖
super.setAllowCircularReferences(false);
// 调用父类的方法
super.customizeBeanFactory(beanFactory);
}
}

这样当Spring发现我们在代码出现循环依赖,就会在创建Spring IOC时报错。

BeanDefinition 的加载

在刷新BeanFactory的方法中,有个方法叫loadBeanDefinitions,这个方法就是进行BeanDefinition的加载的,他的大致流程是这样的:

img

Spring中一般解析XML文件的时候都是从网上下载对应的标签解析,比如Spring配置文件中的https://www.springframework.org/schema/beans/spring-beans-3.1.xsd ,但是一般来说都是不需要进行下载的,Spring提供了本地文件的xsd文件,这些xsd文件就配置在META-INF/spring.schemas文件中进行配置,由于文件中内容比较多我就不复制出来了。

Spring进行xml解析之前会创建一个namespace的处理器的解,解析完之后就会调用注册,将解析到的BeanDefinition放在beanDefinitionMapbeanDefinitionNames集合中,最终完成了BeanDefinition的加载过程。

现在开发基本都是使用Spring Boot,是全注解方式,这种BeanDefinition的加载实际上就是指定了一个包的扫描,然后扫描这些包下标记了

解析的主要流程在AnnotationConfigApplicationContext这个类中。

准备BeanFactory

AbstractApplicationContext#refresh方法的第三个方法:prepareBeanFactory,准备BeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// 设置类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置Spel 表达式解析器,用于属性填充时对值进行表达式解析
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 扩展点,添加一个属性编辑器的注册器,也可以使用 CustomEditorConfigurer 进行设置
// 后面在进行属性填充的时候会调用这个属性编辑器进行属性的解析
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

// Configure the bean factory with context callbacks.
// 扩展点,添加一个BeanPostProcessor 这里添加这个进行处理,使用前置处理器执行下面忽略的六个Aware接口
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 由于上面设置了这六个接口,因此需要忽略掉,不让Spring使用自动装配进行Bean的装配,而是使用BeanPostProcessor
// 的后置处理器的前置方法进行调用,因为如果不忽略,那么自定义的Bean中就会使用Setter注入进行装配,
// spring 这样做是为了统一的进行处理在Bean增强的时候
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 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.
// 添加一个事件监听器的装饰器
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

// Detect a LoadTimeWeaver and prepare for weaving, if found.
// aop织入 编译器织入、运行期织入、类加载织入
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

// Register default environment beans.
// 注册环境信息
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());

这个方法中主要做了以下事情:

  1. 设置BeanFactory的类加载器。
  2. 设置Bean的SPEL表达式的解析器,其作用是对值进行表达式的解析,比如在属性填充时,针对值是Properties或者String类型的时候就会使用el表达式进行解析。
  3. 设置属性编辑器的注册器,作用是对属性进行解析,比如在属性填充时,针对字符串String类型的时候进行类型转换,就可以自定义属性编辑器针对性的进行解析操作。
  4. 添加一些内置的BeanPostProcessor用于后面对象初始化时调用。
  5. 设置环境信息,系统属性,系统环境变量等。

这个方法预留了一些扩展点,比如可以添加自定义的属性编辑器,添加自定义的BeanPostProcessor等。

参考:

https://www.cnblogs.com/redwinter/p/16165274.html

https://www.cnblogs.com/redwinter/p/16167214.html