這篇文章主要介紹了Spring5源碼之ApplicationContext的接口,用于加載bean,詳細分析了初始化和重新整理的過程。需要的朋友可以參考一下。
1、ClassPathXmlApplicationContext源碼解析
獨立的XML應用程式上下文,擷取上下文定義檔案,從類路徑中将普通路徑解釋為類路徑資源名。
/**
* Standalone XML application context, taking the context definition files
* from the class path, interpreting plain paths as class path resource names
* that include the package path (e.g. "mypackage/myresource.txt"). Useful for
* test harnesses as well as for application contexts embedded within JARs.
*
* <p>The config location defaults can be overridden via {@link #getConfigLocations},
* Config locations can either denote concrete files like "/myfiles/context.xml"
* or Ant-style patterns like "/myfiles/*-context.xml" (see the
* {@link org.springframework.util.AntPathMatcher} javadoc for pattern details).
*
* <p>Note: In case of multiple config locations, later bean definitions will
* override ones defined in earlier loaded files. This can be leveraged to
* deliberately override certain bean definitions via an extra XML file.
*
* <p><b>This is a simple, one-stop shop convenience ApplicationContext.
* Consider using the {@link GenericApplicationContext} class in combination
* with an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}
* for more flexible context setup.</b>
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see #getResource
* @see #getResourceByPath
* @see GenericApplicationContext
*/
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
2、ClassPathXmlApplicationContext構造器
使用給定的父類建立一個新的ClassPathXmlApplicationContext,從給定的XML檔案中加載定義。
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
// 設定配置路徑
setConfigLocations(configLocations);
// 是否自動重新整理上下文
if (refresh) {
// 解析和功能實作方法
refresh();
}
}
3、設定路徑setConfigLocations
此方法主要用于解析給定的路徑數組,當然,如果數組中包含特殊字元,如${var},那麼resolvePath方法中會搜尋尋找比對的系統變量并進行替換。
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] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
4、擴充功能refresh
refresh方法中包含了幾乎ApplicationContext中提供的全部功能,而且此方法中邏輯非常清晰明了,使得我們很容易分析對應的層級和邏輯功能。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1 準備重新整理上下文
prepareRefresh();
// 2 初始化BeanFactory,并進行XML檔案讀取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3 對BeanFactory進行各種功能的填充
prepareBeanFactory(beanFactory);
try {
// 4 子類覆寫方法做額外的處理
postProcessBeanFactory(beanFactory);
// 5 激活各種BeanFactory的處理器
invokeBeanFactoryPostProcessors(beanFactory);
// 6 注冊攔截Bean建立Bean處理器,這裡隻是注冊,真正的調用時在getBean時候
registerBeanPostProcessors(beanFactory);
// 7 為上下文初始化Message源,即不同語言的消息體,國際化處理
initMessageSource();
// 8 初始化應用消息廣播器,并放入"applicationEventMulticaster"bean中
initApplicationEventMulticaster();
// 9 留給子類來初始化其他的Bean
onRefresh();
// 10 在所有注冊的bean中查找Listen beans,注冊到消息廣播中
registerListeners();
// 11 初始化剩下的單執行個體(非惰性的)
finishBeanFactoryInitialization(beanFactory);
// 12 最後一步:完成重新整理過程,通知生命周期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreshedEvent通知
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 1 銷毀已經建立的單例,以避免挂起資源。
destroyBeans();
// 2 重新設定active标志
cancelRefresh(ex);
// 3 将異常傳播給調用者
throw ex;
}
finally {
// 4 重置Spring核心的普通内省緩存,因為我們可能再也不需要單例bean的中繼資料了
resetCommonCaches();
}
}
}
5、ClassPathXmlApplicationContext初始化步驟
-
1 初始化前的準備工作,例如對系統屬性或者環境變量進行準備及驗證。
在某種情況下項目使用需要讀取某些系統變量,而這個變量的設定可能會影響系統的正确性,那麼ClassPathXmlApplicationContext為我們提供的這個準備函數就顯得非常必要,它可以在Spring啟動的時候提前對必需的變量進行存在性驗證。
-
2 初始化BeanFactory,并進行XML檔案讀取。
之前有提到ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特征,那麼在這一步驟中将會複用BeanFactory中的配置檔案讀取解析其他功能,這一步之後ClassPathXmlApplicationContext實際上就已經包含了BeanFactory所提供的功能,也就是可以進行bean的提取等操作了。
-
3 對BeanFactory進行各種功能填充
@Qualifier與@Autowired應該是大家非常熟悉的注解,那麼這兩個注解正是在一步驟中增加支援。
-
4 子類覆寫方法做額外的處理
Spring之是以強大,為世人所推崇,除了它能上為大家提供了便利外,還有一方面是它的完美架構,開放式的架構讓使用它的程式員很容易根據業務需要擴充已經存在的功能。這種開放式的設計在Spring中随處可見,例如本例中就提供了一個空的函數實作postProcessBeanFactory(模闆方法)來友善程式員在業務上做進一步的擴充。
- 5 激活各種BeanFactory的處理器。
- 6 注冊攔截Bean建立Bean處理器,這裡隻是注冊,真正的調用時在getBean時候。
- 7 為上下文初始化Message源,即不同語言的消息體,國際化處理。
- 8 初始化應用消息廣播器,并放入"applicationEventMulticaster"bean中。
- 9 留給子類來初始化其他的Bean。
- 10 在所有注冊的bean中查找Listen beans,注冊到消息廣播中。
- 11 初始化剩下的單執行個體(非惰性的)。
- 12 最後一步:完成重新整理過程,通知生命周期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreshedEvent通知。
6、環境準備prepareRefresh源碼解析
prepareRefresh方法主要是做些準備工作,例如對系統屬性及環境變量的初始化及驗證。
/**
* 為重新整理準備這個上下文,設定它的啟動日期和活動标志,并執行任何初始化的屬性
*/
protected void prepareRefresh() {
// 1 切換到活躍
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 2 留給子類覆寫
initPropertySources();
// 3 驗證需要的屬性檔案是否都已經放入環境中
getEnvironment().validateRequiredProperties();
// 4 存儲早期應用監聽器ApplicationListeners
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// 5 重置早期應用監聽器ApplicationListeners
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// 6 允許收集早期的ApplicationEvents,一旦事件中的助手類可用,就會釋出
this.earlyApplicationEvents = new LinkedHashSet<>();
}
-
initPropertySources擴充
initPropertySources正符合Spring的開放式結構設計,給使用者最大擴充Spring的能力。使用者可以根據自身的需要重寫initPropertySources方法,并在方法中進行個性化的屬性處理及設定。
-
initPropertySources使用案例
普通的bean:MyTestBean
package com.test.bean;
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
自定義的ClassPathXmlApplicationContext:MyClassPathXmlApplicationContext
package com.test.application;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Description: 自定義ClassPathXmlApplicationContext
* @Author: Janson
* @Date: 2020/4/12 12:32
**/
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
super(configLocations);
}
@Override
protected void initPropertySources() {
getEnvironment().setRequiredProperties("myProperties");
}
}
resources目錄下的資源檔案application.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.xsd">
<bean id="myTestBean" class="com.test.bean.MyTestBean"></bean>
</beans>
ClassPathXmlApplicationContext測試類:MyClassPathXmlApplicationContextTest
package com.test;
import com.test.application.MyClassPathXmlApplicationContext;
import com.test.bean.MyTestBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
/**
* @Description: MyClassPathXmlApplicationContext測試類
* @Author: Janson
* @Date: 2020/4/12 15:04
**/
@Slf4j
public class MyClassPathXmlApplicationContextTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new MyClassPathXmlApplicationContext("application.xml");
MyTestBean myTestBean = (MyTestBean) applicationContext.getBean("myTestBean");
log.info("testStr:{}", myTestBean.getTestStr());
}
}
測試類輸出結果
- 不加環境變量myProperties,控制台輸出結果
16:28:46.864 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'myProperties' in any property source
Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [myProperties]
- 加環境變量myProperties,控制台輸出結果及添加環境變量如下圖所示
16:21:25.511 [main] INFO com.test.MyClassPathXmlApplicationContextTest - testStr:testStr
7、加載BeanFactory源碼解析
obtainFreshBeanFactory方法從字面了解就是擷取BeanFactory。之前有說過,ApplicationContext是對BeanFactory的功能上的擴充,不但包含了BeanFactory的全部功能,更在其基礎上添加了大量的擴充應用,那麼obtainFreshBeanFactory正是實作BeanFactory的地方,也就是經過這個函數後ApplicationContext就已經擁有了BeanFactory的全部功能。
- obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 初始化BeanFactory,并進行XML檔案讀取,并将得到的BeanFactory記錄在目前實體的屬性中
refreshBeanFactory();
// 傳回目前實體的beanFactory屬性
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
- 方法中核心實作委托給refreshBeanFactory
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 1 建立DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 2 為了序列化指定id,如果需要的話,讓這個BeanFactory從id反序列化到BeanFactory對象
beanFactory.setSerializationId(getId());
// 3 定制beanFactory,設定相關屬性,包括是否允許覆寫同名稱的不同定義的對象以及循環依賴
customizeBeanFactory(beanFactory);
// 4 初始化DocumentReader,并進行XML檔案讀取及解析
loadBeanDefinitions(beanFactory);
// 5 使用全局變量記錄BeanFactory類執行個體
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
-
分析上面的步驟
1、建立DefaultListableBeanFactory。
在介紹BeanFactory的時候,聲明方式為:
其中XmlBeanFactory繼承自DefaultListableBeanFactory并提供了
XmlBeanDefinitionReader類型的reader屬性,也就是說DefaultListableBeanFactory是容器的為基礎。必須先執行個體化,那麼在這裡就是執行個體化DefaultListableBeanFactory的步驟。
2、指定序列化ID。
3、定制BeanFactory。
4、加載BeanDefinition。
5、使用全局變量記錄BeanFactory類執行個體。
因為DefaultListableBeanFactory類型的變量beanFactory是函數内的局部變量,是以使用全部變量記錄解析結果。
定制BeanFactory
- customizeBeanFactory方法
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 如果屬性allowBeanDefinitionOverriding不為空,設定給beanFactory對象相應屬性
// 此屬性含義是否允許覆寫同名稱不同定義的對象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 如果屬性allowCircularReferences不為空,設定給beanFactory對象相應屬性
// 此屬性含義是否允許bean之間存在循環依賴
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
- 自定義MyClassPathXmlApplicationContext(采用子類覆寫方法)
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.setAllowBeanDefinitionOverriding(false);
super.setAllowCircularReferences(false);
super.customizeBeanFactory(beanFactory);
}
-
解析器AutowireCandidateResolver
解析autowire類型時會首先調用的方法:
- QualifierAnnotationAutowireCandidateResolver解析器的getSuggestedValue方法
/**
- 确定給定的依賴項是否聲明了值注釋
*/
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
// 從任何給定的候選注釋中确定建議值
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
// 如果有的話,傳回包裝好的MethodParameter
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
// 傳回與目标方法/構造函數本身相關聯的注解。
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}
加載BeanDefinition
- loadBeanDefinitions
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 為指定的beanFactory建立XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 對beanDefinitionReader進行環境變量設定
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 對beanDefinitionReader進行環境變量設定進行設定,可以覆寫
initBeanDefinitionReader(beanDefinitionReader);
// 使用給定的XmlBeanDefinitionReader加載bean定義
loadBeanDefinitions(beanDefinitionReader);
}
使用XmlBeanDefinitionReader的loadBeanDefinitions方法進行配置檔案的加載注冊相信大家已經不陌生了,這完全就是開始BeanFactory的套路。因為在XmlBeanDefinitionReader中已經将之前初始化的DefaultListableBeanFactory注冊進去了,是以XmlBeanDefinitionReader所讀取的BeanDefinitionHolder都會注冊到DefaultListableBeanFactory中,也就是經過此步驟,類型DefaultListableBeanFactory的變量beanFactory已經包含了所有解析好的配置。
8、功能擴充prepareBeanFactory源碼解析
-
prepareBeanFactory方法源碼
在進入prepareBeanFactory方法之前,spring已經完成了對配置的解析,而ApplicationContext在功能上的擴充也由此展開。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 1 設定beanFactory的classLoader為目前context的classLoader
beanFactory.setBeanClassLoader(getClassLoader());
// 2 設定beanFactory的表達式語言處理器,Spring3增加了表達式語言的支援。
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 3 為beanFactory增加了一個預設的propertyEditor,這個主要是對bean的屬性設定管理的一個工具
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
//4 添加一個新的BeanPostProcessor,它将被應用到這個工廠建立的bean中
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 5 設定了幾個忽略自動裝配的接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 6 設定了幾個自動裝配的特殊規則
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 7 将早期用于檢測内部bean的後處理器注冊為ApplicationListenerDetector
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// 8 增加AspectJ的支援
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()));
}
// 9 添加預設的系統環境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());
}
}
-
屬性注冊編輯器案例
UserManager類
package com.test.bean;
import lombok.Data;
import java.util.Date;
/**
* @Description: user manager
* @Author: Janson
* @Date: 2020/4/13 23:16
**/
@Data
public class UserManager {
private Date dateValue;
}
resources目錄下的bean.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.xsd">
<bean id="userManager" class="com.test.bean.UserManager">
<property name="dateValue" value="2020-04-13"></property>
</bean>
</beans>
屬性編輯器測試類
package com.test;
import com.test.bean.UserManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Description: propertyEditor測試類
* @Author: Janson
* @Date: 2020/4/13 23:44
**/
@Slf4j
public class PropertyEditorTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
UserManager userManager = (UserManager) applicationContext.getBean("userManager");
log.info("dateValue:{}", userManager.getDateValue());
}
}
測試類輸出結果
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Date' for property 'dateValue': no matching editors or conversion strategy found
結論:這樣使用會直接抛出如上異常,類型轉換不成功。因為UserManager中的dataValue的屬性是Date類型的,而XML中配置的卻是String類型的,是以當然會報異常。
Spring針對此問題提供了解決辦法
定義屬性編輯器DatePropertyEditorRegistrar
package com.test.support;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description: Date PropertyEditorRegistrar
* @Author: Janson
* @Date: 2020/4/14 22:49
**/
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
}
注冊到Spring中
<!--注冊Spring自帶編輯器-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="com.test.support.DatePropertyEditorRegistrar"></bean>
</list>
</property>
</bean>
啟動測試類,輸出結果如下:
- 注冊預設編輯器doRegisterEditor
/**
- 如果可能的話,覆寫預設編輯器(因為這是我們在這裡真正想做的);否則注冊為自定義編輯器。
*/
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
else {
registry.registerCustomEditor(requiredType, editor);
}
}
-
ApplicationContextAwareProcessor處理器
對于postProcessAfterInitialization方法,在ApplicationContextAwareProcessor沒有做過多的邏輯處理,重點看一下postProcessBeforeInitialization方法。
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
// 調用aware相關接口擷取資源
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
// 調用aware相關接口擷取資源
invokeAwareInterfaces(bean);
}
return bean;
}
invokeAwareInterfaces方法
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
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 ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
postProcessBeforeInitialization方法中調用了invokeAwareInterfaces。從invokeAwareInterfaces方法來看,我們已經了解Spring的用意,實作這些Aware接口的bean在被初始化之後,可以取得一些對應的資源。
-
設定忽略依賴
當Spring将ApplicationContextAwareProcessor注冊後,那麼在invokeAwareInterfaces方法中間調用的Aware類已經不是普通的bean了,如ResourceLoaderAware、ApplicationEventPublisherAware、ApplicationContextAware等,那麼當然需要在Spring做bean的依賴注入的時候忽略他們,而ignoreDependencyInterface的作用正是在此。
// 5 設定了幾個忽略自動裝配的接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
-
注冊依賴
Spring中有了忽略依賴的功能,當然也必不可少地會有注冊依賴功能。
當注冊了依賴解析後,例如當注冊了對BeanFactory.class的解析依賴後,當bean的屬性注入的時候,一旦檢測到屬性為BeanFactory類型便會将beanFactory的執行個體注入進去。
// 6 設定了幾個自動裝配的特殊規則
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
9、 模闆方法postProcessBeanFactory
子類覆寫方法做額外的處理
// 子類覆寫方法做額外的處理
postProcessBeanFactory(beanFactory);
10、激活的BeanFactoryPostProcessor
在正式介紹之前,我們先了解下BeanFactoryPostProcessor的用法。
BeanFactoryPostProcessor接口和BeanPostProcessor類似,可以對bean的定義(配置中繼資料)進行處理。也就是說,Spring的IOC容器允許BeanFactoryPostProcessor在容器實際執行個體化任何其他的bean之前讀取配置中繼資料,并有可能修改它。如果你願意,你可以配置多個BeanFactoryPostProcessor并可以通過order屬性(需要實作Ordered接口)來控制BeanFactoryPostProcessor的執行順序。如果你想改變實際的bean執行個體(例如從配置中繼資料建立的對象),那麼你們最好使用BeanPostProcessor。同樣地,BeanFactoryPostProcessor的作用域範圍是容器級的。它隻和你所使用的容器有關。如果你在容器中定義一個BeanFactoryPostProcessor不會對定義在另一個容器中的bean進行後置處理,即使這兩個容器都是在同一個層級上。在Spring中存在對于BeanFactoryPostProcessor的典型應用,比如PropertyPlaceholderConfigurer。
-
自定義BeanFactoryPostProcessor案例
我們實作一個 BeanFactoryPostProcessor,去除敏感屬性值的功能來展示自定義的BeanFactoryPostProcessor的建立及使用。
配置檔案beanFactory.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.xsd">
<bean id="customBeanFactoryPostProcessor" class="com.test.factory.CustomBeanFactoryPostProcessor">
<property name="sensitives">
<set>
<value>bollocks</value>
<value>inky</value>
<value>bum</value>
<value>Microsoft</value>
</set>
</property>
</bean>
<bean id="simpleBean" class="com.test.factory.SimplePostProcessor">
<property name="connectionString" value="bollocks"></property>
<property name="username" value="Microsoft"></property>
<property name="password" value="imaginecup"></property>
</bean>
</beans>
自定義CustomBeanFactoryPostProcessor
package com.test.factory;
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;
/**
* @Description: Custom BeanFactoryPostProcessor
* @Author: Janson
* @Date: 2020/4/16 22:50
**/
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private Set<String> sensitives;
public CustomBeanFactoryPostProcessor() {
this.sensitives = new HashSet<>();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
StringValueResolver valueResolver = new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
if (isSensitive(strVal)) {
return "******";
}
return strVal;
}
};
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
visitor.visitBeanDefinition(bd);
}
}
private boolean isSensitive(Object value) {
if (value != null) {
String upValue = value.toString().toUpperCase();
return this.sensitives.contains(upValue);
}
return false;
}
public void setSensitives(Set<String> sensitives) {
this.sensitives.clear();
for (String sensitive : sensitives) {
this.sensitives.add(sensitive.toUpperCase());
}
}
}
普通的bean:SimplePostProcessor
package com.test.factory;
import lombok.Data;
/**
* @Description: SimplePostProcessor
* @Author: Janson
* @Date: 2020/4/16 23:15
**/
@Data
public class SimplePostProcessor {
private String connectionString;
private String username;
private String password;
}
測試類BeanFactoryPostProcessorTest
package com.test;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/**
* @Description: BeanFactoryPostProcessor測試類
* @Author: Janson
* @Date: 2020/4/16 23:09
**/
@Slf4j
public class BeanFactoryPostProcessorTest {
public static void main(String[] args) {
Resource resource = new ClassPathResource("beanFactory.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
beanDefinitionReader.loadBeanDefinitions(resource);
BeanFactoryPostProcessor bfpp = (BeanFactoryPostProcessor) factory.getBean("customBeanFactoryPostProcessor");
bfpp.postProcessBeanFactory(factory);
Object simpleBean = factory.getBean("simpleBean");
log.info("simpleBean:{}", simpleBean);
}
}
測試輸出結果
- BeanFactoryPostProcessor源碼解析
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 對BeanDefinitionRegistryPostProcessors類型的處理
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
// 定義擴充類BeanFactoryPostProcessor的BeanDefinitionRegistryPostProcessor集合
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 寫死注冊的後處理器
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 對于BeanDefinitionRegistryPostProcessor類型,在BeanFactoryPostProcessor的基礎上還有自己定義的方法,需要先調用。
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
// 記錄正常的BeanFactoryPostProcessor集合
regularPostProcessors.add(postProcessor);
}
}
// 不要在這裡初始化factorybean:我們需要保留所有正常bean,未初始化,讓bean工廠的後置處理器應用于它們!
// 将實作的beandefinitionregistrypostprocessor分開優先順序,順序,等等。
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 首先,調用實作PriorityOrdered的BeanDefinitionRegistryPostProcessors
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 接下來,調用實作Ordered的BeanDefinitionRegistryPostProcessors
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);
currentRegistryProcessors.clear();
// 最後,調用所有其他的BeanDefinitionRegistryPostProcessors,直到沒有其他的出現。
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);
currentRegistryProcessors.clear();
}
// 現在,調用到目前為止處理的所有處理器的postProcessBeanFactory回調。
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// 調用用上下文執行個體注冊的工廠處理器
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 不要在這裡初始化FactoryBeans:我們需要保留所有正常bean,未初始化,以讓bean工廠的後處理器應用于它們!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// 在實作PriorityOrdered、Ordered和其他的BeanFactoryPostProcessors之間進行分離,
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// 跳過-已經在第一階段處理了
}
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);
}
}
// 首先,調用實作PriorityOrdered.的BeanFactoryPostProcessors
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 接下來,調用實作Ordered的BeanFactoryPostProcessors
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 最後,調用所有其他BeanFactoryPostProcessors
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除緩存的合并bean定義,因為後處理器可能有修改了原始的中繼資料,例如替換了值中的占位符…
beanFactory.clearMetadataCache();
}
從上面的源碼解析可以看到,對于BeanFactoryPostProcessor的處理主要分兩種情況進行,一個是對于BeanDefinitionRegistry類的特殊處理,另一種是對普通的BeanFactoryPostProcessor進行處理。
-
注冊BeanPostProcessor案例
Spring中大部分功能都是通過後處理器的方式進行擴充的,這是Spring架構的一個特性,但是在BeanFactory中其實并沒有實作後處理器的自定注冊,是以在調用的時候如果沒有進行手動注冊其實是不能使用的。但是在ApplicationContext中增加自動注冊功能。
自定義後處理器CustomInstantiationAwareBeanPostProcessor
package com.test.processor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
/**
* @Description: Custom InstantiationAwareBeanPostProcessor
* @Author: Janson
* @Date: 2020/4/18 12:29
**/
@Slf4j
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.info("postProcessBeforeInstantiation:====");
return null;
}
}
bean.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.xsd">
<bean id="dog" class="com.test.bean.Dog">
<property name="name" value="Hello Dog"></property>
<property name="age" value="1"></property>
</bean>
<bean id="customInstantiationAwareBeanPostProcessor" class="com.test.processor.CustomInstantiationAwareBeanPostProcessor"></bean>
</beans>
普通的bean:Dog
package com.test.bean;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@Data
@Slf4j
public class Dog {
private String name;
private Integer age;
public void print() {
log.info("dog name:{} age:{}", name, age);
}
}
測試類BeanPostProcessorTest
package com.test;
import com.test.bean.Dog;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Description: BeanPostProcessor測試類
* @Author: Janson
* @Date: 2020/4/6 15:40
**/
public class BeanPostProcessorTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
Dog dog = (Dog) applicationContext.getBean("dog");
}
}
輸出結果
11、注冊的BeanPostProcessor
- 注冊的BeanPostProcessor的源碼分析
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// BeanPostProcessorChecker是一個普通的資訊列印,可能會有些情況,當Spring的配置中後處理器還沒有被注冊就已經開始了bean
// 初始化時便會列印出BeanPostProcessorChecker中設定的資訊。
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
//使用PriorityOrdered、Ordered和無序BeanPostProcessor來保證順序
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);
}
}
// 第1步,注冊所有實作PriorityOrdered的BeanPostProcessors
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// 第2步,注冊所有實作Ordered的BeanPostProcessors
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
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);
//第3步,注冊所有無序的BeanPostProcessors
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// 第4步,注冊所有内部BeanPostProcessors
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// 第5步,将檢測内部bean的後處理器重新注冊為ApplicationListenerDetector,将它移動到處理器鍊的末端(用于擷取代理等)
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
-
注冊的BeanPostProcessor的源碼分析總結說明
從以上源碼分析和注釋,我們做一下總結。首先我們會發現,對于BeanPostProcessor的處理與BeanFactoryPostProcessor的處理極為相似,但是似乎有有些不一樣的地方。經過反複的對比發現,對于BeanFactoryPostProcessor的處理區分兩種情況,一種方式是通過寫死的方式的處理,另外一種是通過配置檔案的處理。那麼為什麼在BeanPostProcessor的進行中隻考慮了配置檔案的方式而不考慮寫死的方式呢?提出這個問題,還是因為讀者沒有完全了解兩者實作的功能。對于BeanFactoryPostProcessor的處理,不但要實作注冊功能,而且還要實作對後處理的激活操作,是以需要載入配置中的定義并進行激活;而對于BeanPostProcessor并不需要馬上調用,再說,寫死的方式實作的功能是将後處理器提起并調用,這裡不需要調用,當然不需要考慮寫死的方式了,這裡的功能隻需要将配置檔案的BeanPostProcessor提出出來并注入beanFactory就可以了。
對于beanFactory的注冊,也不是直接注冊就可以的。在Spring中支援對于BeanPostProcessor的排序,比如根據PriorityOrdered進行排序、根據Ordered進行排序或者無序,而Spring在BeanPostProcessor的激活順序的時候也會考慮對于順序的問題而先進行排序。
12、初始化消息資源
“國際化資訊”也被稱為“本地化資訊”,一般需要兩個條件才可以确定一個特定類型的本地化資訊,它們分别是“語言類型”和“國家/地區的類型”。
-
國際資訊化接口MessageSource
ResourceBundleMessageSource和ReloadableResourceBundleMessageSource與HierarchicalMessageSource的依賴關系如下圖所示:
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource HierarchicalMessageSource接口最重要的兩個實作類是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它們是基于Java的ResourceBundle基礎類實作的,允許通過資源名加載國際化資源。ReloadableResourceBundleMessageSource提供了定時順序功能,允許在不重新開機系統的情況下,更新資源的資訊。StaticMessageSource主要用于程式測試,他允許聽你剛剛程式設計的方式提供國際化資訊。而DelegatingMessageSource是為友善操作父類MessageSource而提供的代理類。 - initMessageSource源碼解析
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
// 如果在配置中心已經配置了messageSource,那麼将messageSource提前并記錄在this.messageSource
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// 讓MessageSource知道父消息源
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// 如果沒有父消息源,則隻将父上下文設定為父消息源注冊了。 hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// 使用空的MessageSource來接受getMessage調用。
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}
通過讀取并将自定義資源檔案配置記錄在容器中,那麼就可以在擷取資源檔案的時候直接使用了 。例如在AbstractApplicationContext中擷取資源檔案屬性的方法
public String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
return getMessageSource().getMessage(code, args, locale);
}
其中getMessageSoure()方法正是擷取了之前定義的自定義資源配置。
13、初始化ApplicationEventMulticaster
在講解Spring的時間傳播器之前,我們還是先來看一下Spring的事件監聽的簡單用法。
- Spring事件監聽的案例
定義監聽事件MyEvent
package com.test.event;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import java.io.Serializable;
/**
* @Description: 定義監聽事件
* @Author: Janson
* @Date: 2020/4/19 9:32
**/
@Slf4j
public class MyEvent extends ApplicationEvent implements Serializable {
private static final long serialVersionUID = -2846915828284386885L;
private String msg;
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public MyEvent(Object source) {
super(source);
}
public MyEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
public void print() {
log.info("msg:{}", msg);
}
}
定義監聽器MyListener
package com.test.listener;
import com.test.event.MyEvent;
import org.springframework.context.ApplicationListener;
/**
* @Description: 定義監聽器
* @Author: Janson
* @Date: 2020/4/19 9:36
**/
public class MyListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
event.print();
}
}
配置資源檔案event.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.xsd">
<bean id="myListener" class="com.test.listener.MyListener"></bean>
</beans>
事件測試類MyEventTest
package com.test;
import com.test.event.MyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Description: 事件測試類
* @Author: Janson
* @Date: 2020/4/19 9:41
**/
public class MyEventTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:event.xml");
MyEvent myEvent = new MyEvent("hello", "this is a event msg");
applicationContext.publishEvent(myEvent);
}
}
事件測試類輸出結果
10:00:02.586 [main] INFO com.test.event.MyEvent - msg:this is a event msg
事件總結:
當程式運作時,Spring會将發出的MyEvent事件轉發給我們自定義 的MyListener進行進一步處理。這就是設計模式中的觀察者模式。
-
initApplicationEventMulticaster源碼分析
initApplicationEventMulticaster方式比較簡單,無非考慮兩種情況
1、如果使用者自定義了事件廣播器,那麼使用使用者自定義的事件廣播器。
ApplicationEventMulticaster。
2、如果使用者沒有自定義了事件廣播器,那麼使用預設的
ApplicationEventMulticaster。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
- 預設廣播器SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
可以推斷,當産生Spring事件的時候會預設使用SimpleApplicationEventMulticaster的multicastEvent方法來廣播事件,周遊所有監聽器,并使用監聽器中的onApplicationEvent方法來進行監聽器的處理。而對于每個監聽器來說其實都可以擷取到産生的事件,但是是否進行處理則由事件監聽器來決定。
14、注冊監聽器
之前在介紹Spring的廣播器時反複提到了事件監聽器,那麼在Spring注冊監聽器的時候,又做了哪些操作呢?
- registerListeners源碼分析
protected void registerListeners() {
// 首先注冊靜态指定的監聽器。
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 不要在這裡初始化FactoryBeans:我們需要保留所有正常bean,未初始化,以讓後置處理器應用于它們!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 釋出早期的應用程式事件,現在我們終于有一個ApplicationEventMulticaster
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
15、初始化非延遲加載單例
- finishBeanFactoryInitialization源碼分析
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 這個上下文的初始化轉換服務。
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));
}
// 如果沒有bean後處理器,注冊一個預設的嵌入式值解析器
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);
}
// 停止使用臨時類加載器進行類型比對
beanFactory.setTempClassLoader(null);
// 允許緩存所有的bean定義中繼資料,不期望有進一步的更改。
beanFactory.freezeConfiguration();
// 執行個體化所有剩餘的(非惰性初始化)單例
beanFactory.preInstantiateSingletons();
}
- ConversionService使用案例
定義轉換器
package com.test.converter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description: 自定義日期轉換器
* @Author: Janson
* @Date: 2020/4/19 10:49
**/
@Slf4j
public class String2DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
log.info("convert before source:{}", source);
Date parse = simpleDateFormat.parse(source);
log.info("convert after parse:{}", parse);
return parse;
} catch (ParseException e) {
log.info("parse String to date error", e);
return null;
}
}
}
資源配置檔案bean.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.xsd">
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.test.converter.String2DateConverter"></bean>
</list>
</property>
</bean>
</beans>
轉換器測試類
package com.test;
import com.test.converter.String2DateConverter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.Assert;
import java.util.Date;
/**
* @Description: 自定義日期轉換器測試類
* @Author: Janson
* @Date: 2020/4/19 10:57
**/
public class String2DateConverterTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new String2DateConverter());
Date convert = conversionService.convert("2020-04-19 11:03:11", Date.class);
Assert.isAssignable(convert.getClass(),Date.class);
}
}
轉換器測試類輸出
11:22:45.040 [main] INFO com.test.converter.String2DateConverter - convert before source:2020-04-19 11:03:11
11:22:45.048 [main] INFO com.test.converter.String2DateConverter - convert after parse:Sun Apr 19 11:03:11 CST 2020
- preInstantiateSingletons源碼分析
public void preInstantiateSingletons() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-instantiating singletons in " + this);
}
// 周遊一個副本以允許init方法,而init方法反過來注冊新的bean定義。雖然這可能不是正常的工廠引導的一部分,但它在其他方面也可以正常工作
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 觸發所有非惰性單例bean的初始化
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
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);
}
}
}
// 為所有适用的bean觸發初始化後回調
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
16、完成重新整理finishRefresh
在Spring中還提供了Lifecycle接口,Lifecycle中包含了start/stop方法,實作此接口後Spring會保證在啟動的時候調用其start方法開始生命周期,并在Spring關閉的時候調用stop方法來結束生命周期,通常用來配置背景程式,在啟動後一直運作(如對MQ進行輪詢等)。而ApplicationContext的初始化最後正是保證了這一功能的實作。
- finishRefresh源碼分析
protected void finishRefresh() {
// 清除上下文級的資源緩存(例如來自掃描的ASM中繼資料)
clearResourceCaches();
// 為此上下文初始化生命周期處理器。
initLifecycleProcessor();
// 首先将refresh傳播到生命周期處理器
getLifecycleProcessor().onRefresh();
// 釋出最終事件
publishEvent(new ContextRefreshedEvent(this));
// 如果活躍,請參與LiveBeansView MBean
LiveBeansView.registerApplicationContext(this);
}
initLifecycleProcessor
當ApplicationContext啟動或停止時,它會通過LifecycleProcessor來與所有聲明的bean的周期做狀态更新,而在LifecycleProcessor使用前首先需要初始化。
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
}
else {
DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
defaultProcessor.setBeanFactory(beanFactory);
this.lifecycleProcessor = defaultProcessor;
beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LifecycleProcessor with name '" +
LIFECYCLE_PROCESSOR_BEAN_NAME +
"': using default [" + this.lifecycleProcessor + "]");
}
}
}
onRefresh
啟動所有實作了Lifecycle接口的bean
public void onRefresh() {
startBeans(true);
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
group.add(beanName, bean);
}
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
phases.get(key).start();
}
}
}
publishEvent
當完成ApplicationContext初始化的時候,要通過Spring中的事件釋出機制來發出ContextRefreshedEvent事件,以保證對應的監聽器可以做進一步的邏輯處理。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// 必要時将事件裝飾為ApplicationEvent
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 如果可能的話,現在就多點傳播——或者在初始化多點傳播之後延遲多點傳播
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 也可以通過父上下文釋出事件。
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
17、總結
到這裡,Spring5源碼之ApplicationContext分析的初始化和重新整理過程就已經講完了,文章中總共分了16個目錄來重點分析ApplicationContext的核心功能邏輯和開發過程中常用的案例來重新認識源碼的執行過程。如果各位讀者在看文章的過程中有什麼問題或者文章中有分析不到位的地方,歡迎評論留言,互相學習,多謝。
參考文獻
Spring源碼深度解析
如果您覺得有幫助,歡迎點贊哦 ~ ~ 多謝~