Spring源码系列 - ApplicationContext容器的功能扩展

30 篇文章 3 订阅

前言

在我Spring源码系列的前几篇文章中,看过的读者可能会发现,对于Spring的容器,我一直用的是BeanFactory。而本文将讲解Spring的另一个常用的容器ApplicationContextApplicationContext拥有BeanFactory的全部功能,同时扩展类许多的功能。两种都是用来加载Bean的一种容器。

例如,BeanFactory加载XML:(该用法过时了)

XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("user.xml"));

ApplicationContext加载XML

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("user.xml");

一. ApplicationContext容器

我们从上述的案例代码出发,来看下源码:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}
↓↓↓↓↓↓
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
	// 1.父类构造
	super(parent);
	// 2.设置配置文件的路径
	setConfigLocations(configLocations);
	if (refresh) {
		// 3.核心功能
		refresh();
	}
}

1.1 拓展功能refresh

ApplicationContext的拓展功能,必定是在解析到对应的配置文件的基础上进行的,而上述构造函数中,我们看到的方法只有refresh()函数了,这里面包含了ApplicationContext容器中几乎所有的功能。我们来分析下其源码:

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
		// 1.刷新上下文环境,做一些准备工作,如对系统属性或者环境变量进行验证
		prepareRefresh();
		// 2.初始化BeanFactory,进行XML读取。此步骤结束后,ApplicationContext就拥有了BeanFactory的功能。
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// 3.对BeanFactory进行填充,对 @Qualifier 和 @Autowired 注解进行支持
		prepareBeanFactory(beanFactory);
		try {
			// 4.处理子类覆盖方法
			postProcessBeanFactory(beanFactory);
			StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
			// 5.激活各种BeanFactory处理器
			invokeBeanFactoryPostProcessors(beanFactory);
			// 6.注册 [拦截bean的创建过程] 的处理器
			registerBeanPostProcessors(beanFactory);
			beanPostProcess.end();
			// 7.为上下文初始化Message,国际化处理
			initMessageSource();
			// 8.初始化应用消息广播器
			initApplicationEventMulticaster();
			// 9.让子类来初始化其他的bean
			onRefresh();
			// 10.在已经注册的bean中寻找监听器,并注册到消息广播器中
			registerListeners();
			// 11.初始化非惰性单例bean
			finishBeanFactoryInitialization(beanFactory);
			// 12.完成刷新过程
			finishRefresh();
		}
		catch (BeansException ex) {
			// ...
			// 若失败了,则销毁该bean,并且重置刷新状态
			destroyBeans();
			cancelRefresh(ex);
			throw ex;
		}
		finally {
			resetCommonCaches();
			contextRefresh.end();
		}
	}
}

此时,我们就根据上面的标注来进行解析。

1.2 环境准备

此时我们关注第一步prepareRefresh();看看它做了什么事情:

protected void prepareRefresh() {
	// ...省略
	// 1.让子类去覆盖,一般初始化完成后,内容交给第二步来校验
	initPropertySources();
	// 2.验证需要的属性文件是否都已经加载到环境中了
	getEnvironment().validateRequiredProperties();
	// ...省略
}

这里我将大部分不重要的代码给省略了,留下了俩核心方法:

  • initPropertySources()
  • validateRequiredProperties()

遗憾的是,点开initPropertySources方法,发现源码是空的:

protected void initPropertySources() {
	// For subclasses: do nothing by default.
}

那么这个环境准备的函数,里面的操作又是空的,那到底有啥用呢?

案例1

项目结构如下:(Spring源码中测试的话,建议在context包下,自己测试的话,记得引以下spring-context包)
在这里插入图片描述
自定义的容器MyApplicationContext,需要继承ClassPathXmlApplicationContext

public class MyApplicationContext extends ClassPathXmlApplicationContext {
	public MyApplicationContext(String... configLocations) throws BeansException {
		super(configLocations);
	}

	protected void initPropertySources() {
		getEnvironment().setRequiredProperties("ljj");
	}
}

User类:

public class User {
	private String name;
	private String address;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}
}

Test类:

public class Test {
	@org.junit.jupiter.api.Test
	void test(){
		MyApplicationContext context = new MyApplicationContext("test.xml");
		User user = (User) context.getBean("user");
		System.out.println(user.getName());
	}
}

test.xml文件:

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

	<bean name="user" class="test.User">
		<property name="name" value="11"/>
	</bean>
</beans>

测试如下:
在这里插入图片描述
那么如何让程序成功跑通嘞?我们增加个环境配置参数:
在这里插入图片描述
随便填什么,主要是ljj的参数名称就行,我填了ljj="真棒",再跑一次程序,结果如下:
在这里插入图片描述
此时我们就知道initPropertySourcesvalidateRequiredProperties方法的作用了,就是用来校验我们自己配置的环境变量的。

1.3 加载BeanFactory

紧接着,我们里看下拓展功能里的第二步,初始化BeanFactory操作。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
↓↓↓↓↓
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		// 1.初始化BeanFactory,进行XML读取
		refreshBeanFactory();
		// 2.返回BeanFactory实体类对象
		return getBeanFactory();
	}
}

我们来看下第一步refreshBeanFactory()方法。首先其是AbstractApplicationContext类中的一个抽象方法,并没有具体的实现,因此该方法最终会委派给子类AbstractRefreshableApplicationContext来完成:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		// 1.若已经存在BeanFactory实例,则销毁,重新生成一个
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			// 2.创建DefaultListableBeanFactory实例
			// 注意,我们的案例中,XmlBeanFactory是DefaultListableBeanFactory的一个子类。
			// 因此DefaultListableBeanFactory可以说是容器的基础了。
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			// 3.序列化指定的id,若有必要,将从该id进行反序列化得到一个BeanFactory对象
			beanFactory.setSerializationId(getId());
			// 4.定制BeanFactory,设置相关的属性。例如:是否允许覆盖同名称的不同定义的对象、循环依赖、@Autowired、@Qualifier注解等
			customizeBeanFactory(beanFactory);
			// 5.初始化DocumentReader,进行XML的读取和解析
			loadBeanDefinitions(beanFactory);
			// 6.将完成好的BeanFactory赋值
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
}

其中我们先来重点看下第四步:定制BeanFactory

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

同样,在这里没有做任何具体的实现,只是单单的进行赋值罢了,那么这段代码又有什么作用呢?同理,和案例1同样,该方法若希望真正发挥作用,同样需要我们自定义一个子类去完成,例如:

public class MyApplicationContext extends ClassPathXmlApplicationContext {
	public MyApplicationContext(String... configLocations) throws BeansException {
		super(configLocations);
	}

	protected void initPropertySources() {
		getEnvironment().setRequiredProperties("ljj");
	}
	// 设置不允许覆盖名称的不同定义的对象以及不允许发生循环依赖
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		super.setAllowBeanDefinitionOverriding(false);
		super.setAllowCircularReferences(false);
	}
}

紧接着,在定制完自己的Spring容器后(重写customizeBeanFactory方法),来看下第五步:加载BeanDefinition做了什么事情。同样loadBeanDefinitions方法,也是AbstractApplicationContext类中的一个抽象方法,并没有具体的实现,因此该方法最终会委派给子类AbstractXmlApplicationContext去完成:

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// 1.为指定的BeanFactory创建一个XmlBeanDefinitionReader
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// 2.对XmlBeanDefinitionReader进行环境变量的设置。
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// 3.允许对BeanDefinitionReader进行覆盖
	initBeanDefinitionReader(beanDefinitionReader);
	// 4.BeanDefinition的加载
	loadBeanDefinitions(beanDefinitionReader);
}

对于最后一步loadBeanDefinitions,本文不再详细展开,有需要则开启传送门:Spring源码系列:容器的基本实现

那么在这里对1.3节做个总结,ApplicationContext容器对BeanFactory的加载阶段做了什么事情,很简单,两步:

  1. 进行功能的拓展,用户可自定义属性,是否允许覆盖名称的不同定义的对象以及bean之间发生发生循环依赖。
  2. 加载BeanDefinition。(做和BeanFactory一样的事情)

1.4 功能的拓展(BeanFactory的属性填充)

从1.3小节我们可以知道,完成BeanFactory的加载之后,此时ApplicationContext容器已经完成了对配置的解析和BeanDefinition的加载。已经可以正常地去获取Bean了。那么此时,也是时候对容器的功能进行拓展了。我们来看下第三步prepareBeanFactory(beanFactory);

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 1.设置BeanFactory的类加载器为当前context的类加载器
	beanFactory.setBeanClassLoader(getClassLoader());
	// 2.设置BeanFactory的表达式语言处理器。
	if (!shouldIgnoreSpel) {
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	}
	// 3.为BeanFactory增加一个propertyEditor,用于对bean属性的管理
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

	// 4.配置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);

	// 5.设置几个自动装配的特殊规则
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);

	// 注册监听器类型的bean
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

	// 6.增加对AspectJ的支持。(AOP)
	if (!NativeDetector.inNativeImage() && 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()));
	}

	// 注册默认的系统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. 增加一些内置类,以及设置依赖注入可以忽略的接口。
  4. 注册一些固定依赖的属性以及系统相关的bean
  5. 增加AspectJ的支持。

1.4.1 SpEL语言支持

Spring表达式的全称为:Spring Expression Language,缩写SpEL。用于在运行时构建复杂的表达式、存取对象图属性、对象方法调用等。可以用来配置bean的定义。其使用#{...}作为定界符。

我们先来看下案例:
User类:

public class User {
	private String name;
	private String path;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}
}

Test类:

public class Test {
	@org.junit.jupiter.api.Test
	void test(){
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spel/test.xml");
		User user = (User) context.getBean("user");
		System.out.println(user.getName());
		System.out.println(user.getPath());
	}
}

test.xml文件:我们通过表达式动态地为 userBean 注入 osName(操作系统名)与 classPath(类路径)属性。

<?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-4.0.xsd">

	<bean id="user" class="test.spel.User"
		  p:name="#{systemProperties['os.name']}"
		  p:path="#{systemProperties['java.class.path']}"/>
</beans>

结果如下:
在这里插入图片描述

1.4.2 增加属性注册编辑器

背景:Spring进行属性注入的时候,注入intString这类属性,是可以正常注入的,但是像Date类型就无法被识别,例如:

我们在上述的案例基础上修改下:
test.xml:

<bean id="user" class="test.spel.User">
	<property name="date" value="2022-03-16"/>
</bean>

User类:

public class User {
	private Date date;
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
}

Test类:

@org.junit.jupiter.api.Test
void test(){
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spel/test.xml");
	User user = (User) context.getBean("user");
	System.out.println(user.getDate());
}

运行结果如下:
在这里插入图片描述

针对这种情况,Spring提供了两种解决方法:

(1) 使用自定义属性编辑器

这种方式,需要我们编写自定义的属性编辑器,继承PropertyEditorSupport类,重写其setAsText方法。案例如下:

DatePropertyEditor类:

import java.beans.PropertyEditorSupport;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DatePropertyEditor extends PropertyEditorSupport {
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		System.out.println("自定义解析Date,解析前:" + text);
		try {
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
			Date date = dateFormat.parse(text);
			this.setValue(date);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

test.xml文件增加bean配置:在customEditors属性中注入自定义的属性编辑器。(注意:该属性的类型是个Map,因此用了Map标签,同时entrykey应该指的是Date类型,意思是一旦遇到了该类型的属性,就会调用自定义的DatePropertyEditor进行解析

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<entry key="java.util.Date" value="test.spel.DatePropertyEditor"/>
		</map>
	</property>
</bean>

结果如下:
在这里插入图片描述

(2) 利用Spring自带的CustomDateEditor编辑器

案例如下:

创建DateEditorRegister类,同时实现PropertyEditorRegistrar接口:

import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateEditorRegister implements PropertyEditorRegistrar {
	@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
		registry.registerCustomEditor(Date.class,
				new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
	}
}

test.xml文件更改如下:改为注入propertyEditorRegistrars属性

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="propertyEditorRegistrars">
		<list>
			<bean class="test.spel.DateEditorRegister"/>
		</list>
	</property>
</bean>

结果依旧如上图所示,这里就不展开了。

1.4.3 添加ApplicationContextAwareProcessor处理器

这里我们关注第四步代码块:

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);

这行代码的作用就是注册一个BeanPostProcessor,它有啥作用呢?

在调用beaninitMethod方法前后分别调用postProcessBeforeInitializationpostProcessAfterInitialization方法,那么我们来看下ApplicationContextAwareProcessor处理器做了什么:

class ApplicationContextAwareProcessor implements BeanPostProcessor {
	// postProcessAfterInitialization方法没有重写,就默认的什么都不干返回bean,这里就不说了
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		// 这里的类型都是上文希望忽略的依赖接口,调用了ignoreDependencyInterface方法。
		// 如果不是这几种bean,直接返回
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
				bean instanceof ApplicationStartupAware)) {
			return bean;
		}

		AccessControlContext acc = null;
		if (System.getSecurityManager() != null) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}
		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				// 核心调用
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}else {
			// 核心调用
			invokeAwareInterfaces(bean);
		}

		return bean;
	}
	private void invokeAwareInterfaces(Object bean) {
		// 这里就是对各种类型的bean,添加各种资源属性而已
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationStartupAware) {
			((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}
}

提问:为何要设置忽略依赖,即调用ignoreDependencyInterface方法?

答:Spring将ApplicationContextAwareProcessor注册后,在invokeAwareInterfaces方法调用的Aware类已经不是普通的bean。例如EnvironmentAware类,那么需要在Spring做bean的依赖注入的时候忽略他们。他们不适用于一般bean的依赖注入。有另外的逻辑做处理。

总结下,添加ApplicationContextAwareProcessor处理器的作用也就是:让一些实现了Aware接口的bean,在初始化的时候能够获得对应的资源。

例如某bean实现了EnvironmentAware接口,那么就会执行这段代码,添加当前容器的系统环境相关属性:

if (bean instanceof EnvironmentAware) {
	((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}

关于对AspectJ的支持,将在后续文章中讲解。

1.5 BeanFactory的后处理

这一环节,我们将跳出prepareBeanFactory()代码,回到refresh()代码,同时定位到第五步,激活各种BeanFactory处理器:

invokeBeanFactoryPostProcessors(beanFactory);

首先,我们来了解下BeanFactoryPostProcessor是干啥用的。一般用来在实例化一个bean之前,读取相关元数据并进行修改操作。 Spring中就已经有对BeanFactoryPostProcessor的一个应用:PropertyPlaceholderConfigurer类。

1.5.1 Spring中BeanFactoryPostProcessor的应用

User类:

public class User {
	private String address;

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}
}

Test类:

@org.junit.jupiter.api.Test
void test(){
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
	User user = (User) context.getBean("user");
	System.out.println(user.getAddress());
}

然后在resources下创建目录config,并创建文件bean.properties
在这里插入图片描述

message=Hello World

text.xml文件如下:

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

	<bean id="mesHandler" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>config/bean.properties</value>
			</list>
		</property>
	</bean>

	<bean name="user" class="test.User">
		<!--这里的message则是个变量,最终从配置的config/bean.properties文件中读取-->
		<property name="address" value="${message}"/>
	</bean>
</beans>

程序运行结果如下:
在这里插入图片描述
从结果而言,我们能看出PropertyPlaceholderConfigurer类的作用,也就是指定了配置文件的地址。我们来看下这个类的层次结构:
在这里插入图片描述
可见其间接地继承了BeanFactoryPostProcessor接口,而当Spring加载了任何实现了该接口的bean后,都会在bean工厂载入所有bean之后,调用postProcessBeanFactory方法。

我们来看下PropertyResourceConfigurer类中对postProcessBeanFactory方法的重写:

public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
		implements BeanFactoryPostProcessor, PriorityOrdered {
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			// 合并配置
			Properties mergedProps = mergeProperties();
			// 转换配置
			convertProperties(mergedProps);
			// 将配置信息载入到BeanFactory工厂里,也是因此,
			// BeanFactory在实例化前能得到配置信息,而正确的解析我们XML配置文件中的变量引用
			processProperties(beanFactory, mergedProps);
		}
		// ..catch
	}
}

1.5.2 自定义BeanFactoryPostProcessor

自定义MyPostProcessor类,做一个敏感词屏蔽器。用于替换一些敏感的值,实现BeanFactoryPostProcessor接口:

package test;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.util.StringValueResolver;

import java.util.HashSet;
import java.util.Set;

public class MyPostProcessor implements BeanFactoryPostProcessor {
	private Set<String> obscenties;

	public MyPostProcessor() {
		obscenties = new HashSet<>();
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		String[] definitionNames = beanFactory.getBeanDefinitionNames();
		for (String beanName : definitionNames) {
			BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
			StringValueResolver valueResolver = new StringValueResolver() {
				@Override
				public String resolveStringValue(String strVal) {
					if (isObscene(strVal)) {
						return "******";
					}
					return strVal;
				}
			};
			BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
			visitor.visitBeanDefinition(bd);
		}
	}
	// 判断当前值是否在敏感集合中,若在,则替换为******
	public boolean isObscene(Object value) {
		String str = value.toString().toUpperCase();
		return this.obscenties.contains(str);
	}

	public void setObscenties(Set<String> obscenties) {
		this.obscenties.clear();
		for (String obscenty : obscenties) {
			this.obscenties.add(obscenty.toUpperCase());
		}
	}
}

text.xml文件:

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

	<bean id="mybd" class="test.MyPostProcessor">
		<property name="obscenties">
			<set>
				<value>123456</value>
			</set>
		</property>
	</bean>

	<bean name="user" class="test.User">
		<!--这里的message则是个变量,最终从配置的config/bean.properties文件中读取-->
		<property name="address" value="上海"/>
		<property name="phone" value="123456"/>
	</bean>
</beans>

Test类:

@org.junit.jupiter.api.Test
void test(){
	ConfigurableListableBeanFactory bf = new XmlBeanFactory(new ClassPathResource("test.xml"));
	BeanFactoryPostProcessor mybd = (BeanFactoryPostProcessor) bf.getBean("mybd");
	mybd.postProcessBeanFactory(bf);

	User user = (User) bf.getBean("user");
	System.out.println(user.getAddress());
	System.out.println(user.getPhone());
}

@org.junit.jupiter.api.Test
void test2(){
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
	User user = (User) context.getBean("user");
	System.out.println(user.getAddress());
	System.out.println(user.getPhone());
}

结果如下,可见Spring自动将123456这个敏感词给屏蔽掉了。
在这里插入图片描述
至于我为什么写俩test呢,首先他们两个的效果是等价的。本篇文章主要围绕着容器ApplicationContext来展开的,那么从代码的比较上来看,我们可以发现,若是BeanFactory容器,我们需要自己手动调用postProcessBeanFactory方法,而ApplicationContext容器则自动调用了,这也是两个容器的一个区别。

1.5.3 激活处理器原理

这里我们回到invokeBeanFactoryPostProcessors(beanFactory)这行代码,来看下它的处理流程:

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
	Set<String> processedBeans = new HashSet<>();
	// 针对BeanDefinitionRegistry进行处理
	if (beanFactory instanceof BeanDefinitionRegistry) {
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
		// 用于存放常规的 BeanFactoryPostProcessor
		List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
		// 存放 BeanDefinitionRegistryPostProcessor
		List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
		// 处理硬编码注册的后处理器(通过参数传进来的)
		for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
			if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
				// 因为对于BeanDefinitionRegistryPostProcessor类型,其在 BeanFactoryPostProcessor 的基础上还有自定义的方法
				BeanDefinitionRegistryPostProcessor registryProcessor =
						(BeanDefinitionRegistryPostProcessor) postProcessor;
				// 这里则进行调用
				registryProcessor.postProcessBeanDefinitionRegistry(registry);
				// 添加到registryProcessor中,为的是最后执行postProcessBeanFactory方法
				registryProcessors.add(registryProcessor);
			} else {
				// 记录常规的 BeanFactoryPostProcessor
				regularPostProcessors.add(postProcessor);
			}
		}
		// 用于保存本次要执行的BeanDefinitionRegistryPostProcessor
		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
		// 3.调用所有实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor实现类
		// 找出所有实现BeanDefinitionRegistryPostProcessor接口的Bean的beanName
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			// 校验是否实现了PriorityOrdered接口
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				// 将要被执行的加入processedBeans,避免后续重复执行
				processedBeans.add(ppName);
			}
		}
		// 排序、执行、情况
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
		currentRegistryProcessors.clear();

		// 调用了所有实现了Ordered接口的 BeanDefinitionRegistryPostProcessor 实现类
		postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
		currentRegistryProcessors.clear();

		// 调用剩下的 BeanDefinitionRegistryPostProcessor 实现类
		boolean reiterate = true;
		while (reiterate) {
			reiterate = false;
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
					reiterate = true;
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
			currentRegistryProcessors.clear();
		}
		invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
	} else {
		invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
	}
	// 此时,入参中的处理器和容器中的BeanDefinitionRegistryPostProcessor已经被处理完毕
	// 接下来开始处理容器中所有的 BeanFactoryPostProcessor
	String[] postProcessorNames =
			beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

	// 对处理器进行分类、具有优先级的、排过序的、无序的
	List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
	List<String> orderedPostProcessorNames = new ArrayList<>();
	List<String> nonOrderedPostProcessorNames = new ArrayList<>();
	for (String ppName : postProcessorNames) {
		if (processedBeans.contains(ppName)) {
			// skip - already processed in first phase above
		} else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
			priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
		} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
			orderedPostProcessorNames.add(ppName);
		} else {
			nonOrderedPostProcessorNames.add(ppName);
		}
	}

	// 优先调用 有优先级顺序 的处理器
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

	// 其次调用 排过序 的处理器
	List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
	for (String postProcessorName : orderedPostProcessorNames) {
		orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	sortPostProcessors(orderedPostProcessors, beanFactory);
	invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

	// 最后调用 剩下 的处理器
	List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
	for (String postProcessorName : nonOrderedPostProcessorNames) {
		nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

	// 清除元数据缓存
	beanFactory.clearMetadataCache();
}

代码有点冗长,这里做个宏观的总结,方便理解:

首先,我们将处理器分为两大类:

  • BeanFactoryPostProcessor,本文简称BF处理器。主要执行postProcessBeanFactory方法。
  • BeanDefinitionRegistryPostProcessor,本文简称R处理器。主要执行postProcessBeanDefinitionRegistry方法。

其次,先处理入参中beanFactoryPostProcessors对象,进行分类。处理入参中的BeanFactoryPostProcessor和容器中的BeanDefinitionRegistryPostProcessor对象:

  • 调用实现了PriorityOrdered接口的R处理器(具有优先级)。(包括校验操作)
  • 调用实现了Ordered接口的R处理器(排过序)。
  • 调用普通的R处理器。
  • 调用入参中普通的BF处理器。

然后开始处理容器中所有的BF处理器,进行分类。

  • 调用实现了PriorityOrdered接口的BF处理器(具有优先级)。(包括校验操作)
  • 调用实现了Ordered接口的BF处理器(排过序)。
  • 调用普通的BF处理器。

这里做个小区分哈,BF处理器和R处理器,都属于Spring的后置处理器,R处理器优先于BF处理器执行。 可以实现它们以达到动态注册bean定义,动态修改bean定义,以及动态修改bean 而他们的作用也恰恰可以当做上述那一大段代码的总结。

紧接着,我们来看下registerBeanPostProcessors(beanFactory)这行代码是干什么用的。

1.5.4 注册BeanPostProcessor

首先,希望小伙伴们将BeanPostProcessor和上述的BF处理器R处理器进行区分。

  • R处理器继承了B处理器,两者都是基于bean factory来调整上下文的bean的属性值的。他们并不会使用bean实例。
  • BeanPostProcessor则允许动态修改应用程序上下文的bean这时候bean已经实例化成功。 他的执行顺序也在最后。

我们先来看下一个简单地案例:

自定义一个后处理器,实现InstantiationAwareBeanPostProcessor接口

public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		System.out.println("**************Hello World***************");
		return null;
	}
}

同时在xml文件中添加bean的配置:

<bean class="test.MyBeanPostProcessor"/>

结果如下:
在这里插入图片描述
知道了BeanPostProcessor的一个使用之后,我们来回到代码本身,我们来看下源码:

// 注册 [拦截bean的创建过程] 的处理器
registerBeanPostProcessors(beanFactory);
↓↓↓↓↓↓
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
↓↓↓↓↓↓
public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
	// 1.查找 BeanPostProcessor 类型的 Bean 的名称集合
	String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
	/**
	 * 当Spring的配置中的后处理器还没有被注册单就已经开始了bean的初始化时
	 * 会打印出 BeanPostProcessorChecker 中设定的信息
	 * 这里则注册一个 BeanPostProcessorChecker 用于记录bean 在 beanPostProcessor 实例化时的信息
	 */
	int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
	beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

	// 2.四个集合 区分实现不同接口的 BeanPostProcessors
	List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
	List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
	List<String> orderedPostProcessorNames = new ArrayList<>();
	List<String> nonOrderedPostProcessorNames = new ArrayList<>();
	for (String ppName : postProcessorNames) {
		if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			priorityOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
			orderedPostProcessorNames.add(ppName);
		} else {
			nonOrderedPostProcessorNames.add(ppName);
		}
	}

	// 3.注册实现了PriorityOrdered接口的 BeanPostProcessors
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

	// 4.注册实现了 Ordered 接口的 BeanPostProcessors
	List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
	for (String ppName : orderedPostProcessorNames) {
		BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
		orderedPostProcessors.add(pp);
		if (pp instanceof MergedBeanDefinitionPostProcessor) {
			internalPostProcessors.add(pp);
		}
	}
	sortPostProcessors(orderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, orderedPostProcessors);

	// 5.注册其余常规的 BeanPostProcessors
	List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
	for (String ppName : nonOrderedPostProcessorNames) {
		BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
		nonOrderedPostProcessors.add(pp);
		if (pp instanceof MergedBeanDefinitionPostProcessor) {
			internalPostProcessors.add(pp);
		}
	}
	registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

	// 6.最后,注册所有 MergedBeanDefinitionPostProcessor 类型的 BeanPostProcessors
	sortPostProcessors(internalPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, internalPostProcessors);

	// 7.添加ApplicationListenerDetector探测器
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

总的来看。也就是对不同类型的BeanPostProcessor进行分类,然后分别注册。似乎和1.5.3 节没什么区别。但是还是要做好区分。

  • 1.5.3 节对于BF处理器和R处理器,会调用了后处理逻辑。
  • BeanPostProcessor的注册环节,仅仅是将其注册到BeanFactory中,并没有真正的执行!真正的执行实在bean实例化的时候发生的!

1.5.5 初始化消息资源

背景:我们的应用程序需要支持多语言。即 i18n国际化问题。

国际化信息也称为本地化信息,一般需要两个条件才可以确定一个特定类型的本地化信息:

  • 语言类型。
  • 国家/地区的类型。

Java则通过java.util.Locale来标识一个本地化对象,例如:

Locale locale = new Locale("zh", "CN");

同时util包下提供了几个支持本地化的格式化操作类:NumberFormatMessageFormat等。Spring中的国际化资源操作也就是在这些类的基础上进行封装。我们先来看下MessageFormat类的使用:

@org.junit.jupiter.api.Test
void messageFormatTest() {
	String pattern1 = "{0},你好!你于{1}在银行存入{2}元";
	String pattern2 = "At {1,time,short} On{1,date,long} {0} paid {2,number,currency}";
	Object[] params = {"LJJ", new GregorianCalendar().getTime(), 1.0E3};
	
	String s = MessageFormat.format(pattern1, params);
	System.out.println(s);

	MessageFormat mf = new MessageFormat(pattern2, Locale.US);
	String s2 = mf.format(params);
	System.out.println(s2);
}

结果如下:
在这里插入图片描述
Spring中则定义了访问国际化信息的MessageSource接口。我们来看下refresh()源码中的第七步:为上下文初始化Message,国际化处理。

initMessageSource();

protected void initMessageSource() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	// bean的名称额必须是messageSource
	if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
		this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
		// Make MessageSource aware of parent MessageSource.
		if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
			HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
			if (hms.getParentMessageSource() == null) {
				// // 如果已经注册的父上下文没有消息源,则只能将父上下文设置为父消息源
				hms.setParentMessageSource(getInternalParentMessageSource());
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Using MessageSource [" + this.messageSource + "]");
		}
	}
	else {
		// 若用户没有定义配置文件,那么使用临时的 DelegatingMessageSource 作为调用 getMessage方法的返回
		DelegatingMessageSource dms = new DelegatingMessageSource();
		dms.setParentMessageSource(getInternalParentMessageSource());
		this.messageSource = dms;
		beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
		if (logger.isTraceEnabled()) {
			logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
		}
	}
}

这段代码很简单:

  1. 将实现了MessageSource接口的bean存放在ApplicationContext的成员变量中。
  2. 先看是否有此配置,如果有就实例化。
  3. 否则就创建一个DelegatingMessageSource实例的bean

那源码中出现的HierarchicalMessageSource类是干啥的呢?它是MessageSource接口的一个扩展接口。而其有个重要的实现类为ResourceBundleMessageSource类,我们来看下其使用:

1.创建国际化配置文件:
在这里插入图片描述
2.创建一个名字为message的文件:选中左侧的英语,然后点下→按钮
在这里插入图片描述
此时会自动生成两种文件:
在这里插入图片描述
test.xml配置文件如下:

<bean id="messageSource"
	 class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
	<property name="defaultEncoding" value="UTF-8" />
	<property name="basenames">
		<list>
			<value>test/message</value>
		</list>
	</property>
</bean>

Test类:

@org.junit.jupiter.api.Test
void messageFormatTest2() {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
	String str1 = context.getMessage("test", null, Locale.ENGLISH);
	String str2 = context.getMessage("test", null, Locale.CHINA);

	System.out.println(str1);
	System.out.println(str2);
}

结果如下:
在这里插入图片描述
将这个小案例和上述源码结合起来看也就是:

  1. 若我们配置了国际化资源文件,并且存在实现了MessageSource接口的bean
  2. 那么ApplicationContext会将其整合到容器里面。
  3. 那么此时我们调用getMessage()方法,也就是调用对应子类的实现而已。

1.5.6 初始化消息广播器

首先我们来看下Spring中监听器的使用:

1.定义监听事件:

public class MyEvent extends ApplicationEvent {
	public String msg;

	public MyEvent(Object source) {
		super(source);
	}

	public MyEvent(Object source, String msg) {
		super(source);
		this.msg = msg;
	}

	public void print() {
		System.out.println(msg);
	}
}

2.定义监听器:

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

public class MyListener implements ApplicationListener {
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof MyEvent) {
			MyEvent myEvent = (MyEvent) event;
			((MyEvent) event).print();
		}
	}
}

3.test.xml文件:

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

	<bean id="myListener" class="test.listener.MyListener"/>
</beans>

4.Test方法:

@org.junit.jupiter.api.Test
void test() {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("listener/test.xml");
	MyEvent myEvent = new MyEvent("hello", "你好,上海");
	context.publishEvent(myEvent);
}

结果如下:
在这里插入图片描述
我们发现,程序运行的时候,Spring会将发出的MyEvent事件转给我们自定义的监听器MyListener处理(观察者模式)。

1.6 初始化非延迟加载单例

这一环节我们主要围绕finishBeanFactoryInitialization这个方法来展开。其主要做完成BeanFactory的初始化工作,其中包括conversionService的设置、配置冻结和非延迟加载单例的初始化工作:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// 1.初始化此上下文的转换服务,完成conversionService的设置
	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
		beanFactory.setConversionService(
				beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
	}
	// 如果beanFactory之前没有注册嵌入值解析器,则注册默认的嵌入值解析器:主要用于注解属性值的解析。
	if (!beanFactory.hasEmbeddedValueResolver()) {
		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
	}
	// 初始化LoadTimeWeaverAware Bean实例对象
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}
	// Stop using the temporary ClassLoader for type matching.
	beanFactory.setTempClassLoader(null);
	// 2.冻结所有的bean定义,说明此时注册的bean定义 将不能够被修改。此时马上要创建bean实例对象了
	beanFactory.freezeConfiguration();
	// 3.初始化剩下的单例bean
	beanFactory.preInstantiateSingletons();
}

先来看下第一步,我们来熟悉下ConversionService是干什么的:

@org.junit.jupiter.api.Test
void test3() {
	DefaultConversionService conversionService = new DefaultConversionService();
	double d = conversionService.convert("1.2", double.class);
	System.out.println(d); 

	int i = conversionService.convert("2", int.class);
	System.out.println(i);
	Byte b = conversionService.convert("0x10", Byte.class);
	System.out.println(Integer.toBinaryString(b));

}

结果如下:
在这里插入图片描述
第二步冻结所有的bean定义,无非就是将beanDefinitionNames集合转化为String数组,本身没什么好探索的。我们直接来看第三步,非延迟bean 的加载。

  • 懒加载:用的时候才加载构造,不用的时候不加载。
  • 非懒加载(非延迟):容器启动的时候立刻创建对象。

接下来看下preInstantiateSingletons的源码:

@Override
public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// 1.将所有BeanDefinition的名字创建一个集合
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
	// 2.触发所有 非延迟加载单例bean的初始化
	for (String beanName : beanNames) {
		// 3.合并父类BeanDefinition
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		// 4.条件判断:抽象、单例、非懒加载
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			// 是否实现了FactoryBean接口
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				// 类型转换
				if (bean instanceof FactoryBean) {
					FactoryBean<?> factory = (FactoryBean<?>) bean;
					// 判断该FactoryBean是否希望立即初始化
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(
								(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				getBean(beanName);
			}
		}
	}

	// 遍历beanNames,触发所有SmartInitializingSingleton的后初始化回调
	// 当所有单例 bean 都初始化完成以后,会执行afterSingletonsInstantiated()方法
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		// 判断singletonInstance是否实现了SmartInitializingSingleton接口
		if (singletonInstance instanceof SmartInitializingSingleton) {
			StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
					.tag("beanName", beanName);
			SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					smartSingleton.afterSingletonsInstantiated();
					return null;
				}, getAccessControlContext());
			}
			else {
				smartSingleton.afterSingletonsInstantiated();
			}
			smartInitialize.end();
		}
	}
}

总结下本方法主要做了这么几件事:

  1. 准备转换器ConversionService。其作用也就是bean的一个属性解析。
  2. 然后在初始化bean之前,把这些bean冻结起来,不让他们被修改。
  3. 然后对于那些希望在容器启动阶段就创建的bean(非延迟),调用getBean()方法创建。(其详细过程:Spring源码系列:Bean的加载

1.7 完成上下文的刷新工作

终于来到了refresh()方法的最后一步:finishRefresh

protected void finishRefresh() {
	// 1.清理缓存
	clearResourceCaches();
	// 2.为此上下文初始化生命周期处理器,它有啥作用呢?
	// ApplicationContext容器在启动或者停止的时候,需要LifecycleProcessor来和所有的bean的生命周期做状态更新
	initLifecycleProcessor();
	// 3.将刷新完毕事件传播到生命周期处理器。启动所有实现了Lifecycle接口的bean
	getLifecycleProcessor().onRefresh();
	// 4.推送上下文刷新完毕事件到相应的监听器
	publishEvent(new ContextRefreshedEvent(this));
	if (!NativeDetector.inNativeImage()) {
		LiveBeansView.registerApplicationContext(this);
	}
}

二. 总结

ApplicationContext容器在传统的BeanFactory容器上扩展了很多功能,而这些功能的扩展都靠refresh()方法的调用,而该方法又拓展了这么几个主要的功能:

1.环境准备。

  • 比如我们可以校验该程序是否在特定的环境变量下运行的。

2.加载BeanFactory

  • 进行功能的拓展,用户可自定义属性,是否允许覆盖名称的不同定义的对象以及bean之间发生发生循环依赖。
  • 加载BeanDefinition。(做和BeanFactory一样的事情)

3.填充BeanFactory,增加一些功能支持:

  • 增加对SpEL语言的支持。
  • 增加对属性编辑器的支持。(例如支持String类型的属性转Date
  • 让一些实现了Aware接口的bean,在初始化的时候能够获得对应的资源,如添加当前容器的系统环境相关属性
  • 增加AspectJ的支持

4.BeanFactory的后处理:

  • 按顺序调用BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()
  • BeanFactoryPostProcessor.postProcessBeanFactory()。 可以实现它们以达到动态注册bean定义,动态修改bean定义,以及动态修改bean
  • 激活BeanPostProcessor处理器。
  • 初始化各种实现了MessageSource接口的子类。完成消息和资源的初始化。例如国际化i18n配置。
  • 初始化消息广播器。

5.初始化非延迟加载单例:

  • 准备转换器ConversionService。其作用也就是bean的一个属性解析。
  • 然后在初始化bean之前,把这些bean冻结起来,不让他们被修改。
  • 然后对于那些希望在容器启动阶段就创建的bean(非延迟),调用getBean()方法创建。(其详细过程:Spring源码系列:Bean的加载

6.完成上下文的刷新工作。


最后,我们可以看出来,ApplicationContextBeanFactory的一个优缺点比较:

  • 功能的丰富程度上,ApplicationContext拓展的功能更多,使用率也更高。

  • 但是功能多了,启动过程复杂了,那么ApplicationContext的启动速度必然要比BeanFactory要慢。

下一篇文章准备学习下Spring-AOP,也正好弥补本文当中ApplicationContext容器对AspectJ功能支持 这一环节源码的讲解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值