回顧總結
到目前為止,Spring
原始碼中AbstractApplicationContext#refresh
方法的已經解讀到第11個方法finishBeanFactoryInitialization
,前10個方法介紹了:
BeanFactory
的準備,建立,重新整理,個性化BeanFactory
的擴充套件點,自定義屬性解析;- 環境資訊
Environment
的載入(包括環境變數、系統變數等); BeanDefinition
的載入,解析,自定義xml
的方式;BeanFactoryPostProcessor
的註冊與執行流程,BeanDefinitionRegistryPostProcessor
的解析,ConfigurationClassPostProcessor
對Spring
註解的解析過程(@Component、@PropertySources、@PropertySource、@ComponentScans、@ComponentScan、@Import
等註解的解析),Spring Boot
是如何通過@Configuration+@Import + ImportSelector
進行自動裝配的等;BeanPostProcessor
的註冊流程;- 國際化,
Spring
事件驅動的載入執行過程;
finishBeanFactoryInitialization 解析過程
接下來開始解析Spring
對Bean
的建立過程,上原始碼:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
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));
}
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
// 如果之前沒有註冊過任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean),
// 則註冊一個預設的嵌入值解析器:此時,主要用於解析註釋屬性值。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
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);
// Allow for caching all bean definition metadata, not expecting further changes.
// 允許快取所有 bean 定義後設資料,而不是期望進一步的更改
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 例項化所有剩餘的(非惰性初始化)單例
beanFactory.preInstantiateSingletons();
}
- 判斷是否存在轉換服務,有就設定
- 判斷是否有內建的值解析器,沒有就建立一個處理佔位符的解析器
- 例項化LoadTimeWeaverAware,進行早期的Bean的建立
- 停止使用臨時的類載入器
- 凍結BeanDefinition的後設資料資訊,防止被修改
- 開始例項化所有的單例bean物件
除了beanFactory.preInstantiateSingletons()
方法,其他都是Bean
建立的準備,接下來一個一個分析,首先是轉換服務的設定。
轉換服務ConversionService的初始化
方法一開始設定了一個轉換服務,這個轉換服務在Spring
中還是非常的重要的,比如我們xml
中配置一個String
型別的屬性值,但是在Bean
的定義中是一個Integer
型別的,這時Spring
就會自動幫我們轉出來,他是怎麼做的呢?
在Spring
中有幾個比較重要的介面:
Converer
用於將物件S
轉換為物件T
ConverterFactory
一個轉換工廠,能夠將物件S
轉成一類物件R
的子集T
,比如可以將字串S
轉換為T
(Integer、Long
等)Number
型別R
的子集GenericConverter
支援多種型別之間互相轉換。
Spring
轉換器介面ConversionService
的預設實現是DefaultConversionService
,這個預設的轉換器實現中,內建了很多的轉換器,比如:
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
// 陣列轉集合
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
// 集合轉陣列
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
// 陣列轉字串
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
converterRegistry.addConverter(new StreamConverter(conversionService));
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
可以說是非常的豐富的,基本上常見都是Spring提供了,非常貼心。
那麼怎麼使用呢?
不懂當然是上官網:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#core-convert ,這裡可以看到我們只需要將ConversionServiceFactoryBean
配置到Spring容器中就可以了,Spring
內建的轉換器就可以工作了,非常方便。
ConversionServiceFactoryBean
實現了FactoryBean
介面和InitializingBean
介面,而InitializingBean#afterPropertiesSet
是初始化Bean
過程中需要執行的。ConversionServiceFactoryBean
原始碼中:
@Override
public void afterPropertiesSet() {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
// 建構函式
public DefaultConversionService() {
// 新增預設的轉換器
addDefaultConverters(this);
}
可以看到這個ConversionServiceFactroyBean
就是用來初始化轉換器的,並且這個類還提供了擴充套件,可以自定義轉換器加入到轉換器集合中。
自定義轉換器
自定義String轉Integer型別的轉換器:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class StringToIntegerConverter implements Converter<String,Integer> , ConditionalConverter {
@Override
public Integer convert(String source) {
return NumberUtils.parseNumber(source,Integer.class);
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
System.out.println(sourceType.getType());
System.out.println(targetType.getType());
return true;
}
}
邏輯非常簡單,直接呼叫Spring
提供的工具類進行轉換。
配置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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.redwinter.selfconverter"/>
<!--配置轉化器-->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.redwinter.selfconverter.StringToIntegerConverter"/>
</set>
</property>
</bean>
</beans>
建立轉換器客戶端:
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
@Service
public class MyConverter {
private final ConversionService conversionService;
public MyConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void test(String source){
System.out.println(conversionService.convert(source, Integer.class));
}
}
建立測試:
/**
* @author <a href="2360564660@qq.com">redwinter</a>
* @since 1.0
**/
public class FactoryBeanTest {
@Test
public void test(){
MyClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("spring-factory.xml");
MyConverter myConverter = context.getBean(MyConverter.class);
myConverter.test("12345");
}
}
輸出:
class java.lang.String
class java.lang.Integer
12345
分析完轉換服務,接下來分析 值解析器的新增。
預設的值解析器
// 省略程式碼.....
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
// 如果之前沒有註冊過任何 BeanFactoryPostProcessor(例如 PropertySourcesPlaceholderConfigurer bean),
// 則註冊一個預設的嵌入值解析器:此時,主要用於解析註釋屬性值。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 省略程式碼.....
首先判斷了容器中是否存在嵌入的值解析器,如果沒有就新增一個進去,這裡新增進去的是StringValueResolver
,點選resolvePlaceHolders
方法進去,最終會在AbstractPropertyResolver#resolvePlaceholders
中建立一個PropertyPlaceholderHelper
類
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
// 字首為 ${ ,字尾為 },值的分隔符為 : ,比如 ${username:zhansan} username沒有的話,後面的為預設的值
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
如果已經註冊過一個BFPP
的佔位符解析器的話,就不需要在註冊了,BFPP
的佔位符解析器就是PropertySourcesPlaceholderConfigurer
,專門用於解析佔位符的,比如在xml
中或者yaml
中,配置類似於${jdbc.username}
這種格式的,就會被解析器解析。PropertySourcesPlaceholderConfigurer
這個解析器實現了BeanFactoryPostProcessor
介面,能夠對BeanDefinition
進行處理,當然也可以對屬性值進行處理。
分析完值解析器,繼續往下分析。
Bean建立前的其他準備
// 省略程式碼.....
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
// 在prepareBeanFactory 準備BeanFactory時設定進去的,如果存在,則開始早期Bean的建立
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
// 停止使用臨時的類載入器,這裡也是在準備BeanFactory時設定進去的
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// 允許快取所有 bean 定義後設資料,而不是期望進一步的更改
beanFactory.freezeConfiguration();
// 省略程式碼.....
這裡從容器中獲取了AOP
的織入,如果有的話就開始進行早期的Bean
的建立;然後停止了臨時的類載入器;然後就是凍結BeanDefinition
的後設資料資訊。
public void freezeConfiguration() {
this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}
點選進來,其實就是設定了標識,防止後期對BeanDefinition
的修改。
這前面的幾個判斷和方法實際上都是Bean
建立的準備工作,接下來開始分析preInstantiateSingletons
預例項化所有的單例Bean
。