SpringBoot之淺析配置項解析(四)
我們在之前的文章中簡單的說了一下SpringBoot對於預設的配置檔案的解析過程,在這一篇文章中我們再簡單的分析一下SpringBoot是怎麼將解析到的配置屬性資訊設定到相應的Bean上的。既然是用SpringBoot的屬性配置方式,那麼我們在這裡會在對應的類上加上ConfigurationProperties和Component(或是和Component相同功能的)註解。我們定義的Bean如下:
@Component
@ConfigurationProperties(prefix ="person.info")
public class PersonInfoDomain {
/**
* 使用者名稱
*/
private String userName;
}
配置檔案如下:
其內容依次如下:
person.info.user-name=lisi
person:
info:
user-name: zhangzhang
person.info.user-name=zhangsan
person:
info:
user-name: lilisisi
首先問一個問題,如果是你來實現這樣的功能的話,你會在什麼時機來進行屬性值設定的功能呢?在建立Bean的時候進行屬性的設定應該是一個比較合適的時機吧?Spring也是這樣做的。在Spring中提供了各種不同功能的介面來讓我們在Bean建立的過程中做一些不同功能的擴充套件,我們先稱為”生命週期”的介面(可以在這裡進行檢視:
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
//這裡的getBeanPostProcessors()這個方法獲取到的是Spring 容器中實現BeanPostProcessor介面的Bean 在這個Bean中有一個Bean叫ConfigurationPropertiesBindingPostProcessor 從這個Bean的名字我們可以感覺這個Bean應該是和ConfigurationProperties相關的類,而事實也確是如此 其他的我們先不說了,放一下SpringBoot中內建的一些BeanPostProcessor 的Bean,我們直接進入到 ConfigurationPropertiesBindingPostProcessor 這個類中
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//獲取類上的ConfigurationProperties註解 用AnnotationUtils來對類上的註解進行處理是很方便,有興趣的可以看一下AnnotationUtils中的方法的原始碼實現
ConfigurationProperties annotation = AnnotationUtils
.findAnnotation(bean.getClass(), ConfigurationProperties.class);
//如果類上有ConfigurationProperties 註解
if (annotation != null) {
//主要處理方法
postProcessBeforeInitialization(bean, beanName, annotation);
}
//方法上的ConfigurationProperties註解
annotation = this.beans.findFactoryAnnotation(beanName,
ConfigurationProperties.class);
if (annotation != null) {
postProcessBeforeInitialization(bean, beanName, annotation);
}
return bean;
}
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
private void postProcessBeforeInitialization(Object bean, String beanName,
ConfigurationProperties annotation) {
//我們需要設定屬性的目標bean
Object target = bean;
//新建一個PropertiesConfigurationFactory類 這個類來完成屬性的賦值的工作
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
//propertySources 1)
factory.setPropertySources(this.propertySources);
//驗證器
factory.setValidator(determineValidator(bean));
//轉換服務
factory.setConversionService(this.conversionService == null
? getDefaultConversionService() : this.conversionService);
if (annotation != null) {
//類上 ConfigurationProperties註解的資訊
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
//屬性字首
if (StringUtils.hasLength(annotation.prefix())) {
factory.setTargetName(annotation.prefix());
}
}
try {
//主要方法
factory.bindPropertiesToTarget();
}
}
對於1)處的PropertySources我們應該不陌生了,前面我們一直在提到這個類。我們看一下這個PropertySources是在什麼時候進行賦值的。在ConfigurationPropertiesBindingPostProcessor中有一個這樣的方法afterPropertiesSet:
public void afterPropertiesSet() throws Exception {
if (this.propertySources == null) {
//尋找PropertySources
this.propertySources = deducePropertySources();
}
//validator的賦值
if (this.validator == null) {
this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
}
//轉換服務
if (this.conversionService == null) {
this.conversionService = getOptionalBean(
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
ConversionService.class);
}
}
private PropertySources deducePropertySources() {
//從Spring 容器中獲取 PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
if (configurer != null) {
//configurer.getAppliedPropertySources() 這裡獲取到的就是我們之前一直提到的MutablePropertySources
return new FlatPropertySources(configurer.getAppliedPropertySources());
}
//如果Spring 容器中 沒有PropertySourcesPlaceholderConfigurer的話 則從ConfigurableEnvironment中獲取
if (this.environment instanceof ConfigurableEnvironment) {
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
.getPropertySources();
return new FlatPropertySources(propertySources);
}
//還獲取不到的話,則新建一個MutablePropertySources
return new MutablePropertySources();
}
private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
if (this.beanFactory instanceof ListableBeanFactory) {
//從Spring 容器中獲取 PropertySourcesPlaceholderConfigurer
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
Map<String, PropertySourcesPlaceholderConfigurer> beans = listableBeanFactory
.getBeansOfType(PropertySourcesPlaceholderConfigurer.class, false,
false);
if (beans.size() == 1) {
return beans.values().iterator().next();
}
//如果有多於一個存在的話,則返回null
if (beans.size() > 1 && logger.isWarnEnabled()) {
}
}
return null;
}
OK,我們進入到PropertiesConfigurationFactory看一下factory.bindPropertiesToTarget();這個方法的內容:
public void bindPropertiesToTarget() throws BindException {
//propertySources不能為null
Assert.state(this.propertySources != null, "PropertySources should not be null");
try {
this.hasBeenBound = true;
doBindPropertiesToTarget();
}
}
private void doBindPropertiesToTarget() throws BindException {
//targetName PropertiesConfigurationFactory註解上的字首值
//target 目標bean
RelaxedDataBinder dataBinder = (this.targetName != null
? new RelaxedDataBinder(this.target, this.targetName)
: new RelaxedDataBinder(this.target));
//校驗器
if (this.validator != null
&& this.validator.supports(dataBinder.getTarget().getClass())) {
dataBinder.setValidator(this.validator);
}
//轉換服務
if (this.conversionService != null) {
dataBinder.setConversionService(this.conversionService);
}
//集合的最大值
dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE);
dataBinder.setIgnoreNestedProperties(this.ignoreNestedProperties);
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
//自定義Binder 這裡是一個空實現
customizeBinder(dataBinder);
//下面這兩段程式碼是獲取 Spring支援的配置的名字 支援格式超出你的想象 可以除錯自己看一下
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
Set<String> names = getNames(relaxedTargetNames);
//獲取PropertyValues 重點要分析的
PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
relaxedTargetNames);
//屬性值的繫結工作
dataBinder.bind(propertyValues);
if (this.validator != null) {
//屬性值的校驗
dataBinder.validate();
}
checkForBindingErrors(dataBinder);
}
相關推薦
SpringBoot之淺析配置項解析(四)
我們在之前的文章中簡單的說了一下SpringBoot對於預設的配置檔案的解析過程,在這一篇文章中我們再簡單的分析一下SpringBoot是怎麼將解析到的配置屬性資訊設定到相應的Bean上的。既然是用SpringBoot的屬性配置方式,那麼我們在這裡會在對應的類上
mybatis源碼-解析配置文件(四)之配置文件Mapper解析
als cif fragments etc add contex csdn chm element 在 mybatis源碼-解析配置文件(三)之配置文件Configuration解析 中, 講解了 Configuration 是如何解析的。 其中, mappers作為con
saltstack主機管理項目:編寫插件基類-獲取主機列表-提取yaml配置文件(四)
技術分享 req bubuko shell cep error bin self ttr 一、編寫插件基類 1、目錄結構 1、我是如何獲知我有多少種系統? 當客戶端第一連接過來的時候,我就已經把這些文件存下來了 ,存在到哪裏了?存到數據庫了 每次對主機發送命令的動作時,
bkt項目 (四)搭建環境並測試之3添加日誌和 Thymeleaf 模板
model nal -- contex port tro XML 測試 fan 簡介:這個項目是為了學習SpringBoot以及學習SpringCloud用的,如果對你有什麽幫助,還是非常高興的。 GitHub : https://github.com/fankf/bk
第X屆智慧車攝像頭組程式碼全解析------(四)按鍵等其他初始化配置
作者:Sumjess 本次部落格內容: 該初始化函式下有以下語句: 一、init_control_circle(); --- 初始化PIT1和 PIT2: PIT
Ethercat解析(四)之搭建RTAI實時核心(Ubuntu12.04)
一、環境 系統:Ubuntu12.04 i386(請注意,amd64暫時不支援該實時核心) 實時補丁:linux-image-3.4-9-rtai-686-pae 二、獲取 三、安裝搭建 (1) 更新Ubuntu源和包 sudo ap
FAT16檔案系統之目錄項分析(四)
FAT16檔案系統的FDT分析 1:FDT位置 FDT的含義為檔案目錄表,它在一個檔案系統中的具體位置是緊跟在FAT2之後。 定位過程: A:系統通過讀取該分割槽表資訊,定位到其DBR扇區 B:讀取DBR的保留扇區數(OEH –0FH). C:讀取每個FAT扇區數(16H
SpringMVC原始碼解析(四)之關於json,xml的自動轉換原理
關於json,xml的自動轉換原理的核心就在messageConvert,前一篇我們已經分析到通過messageConvert對請求引數進行解析讀取,那就續點分析。 本節就以json的轉換為例(xml類同只是不同的mess
Android FM模組學習之四原始碼解析(四)
我今天想分享的是FM模組的儲存方法,即FmSharedPreferences.java FmSharedPreferences(Context context)在構造方法中載入Load()方法, public void Load(){ Log.d(
Nginx配置項優化(轉載)
cpu strong 資源 大並發 網站流量統計 調優 傳遞 超時時間 ipv (1)nginx運行工作進程個數,一般設置cpu的核心或者核心數x2 如果不了解cpu的核數,可以top命令之後按1看出來,也可以查看/proc/cpuinfo文件 grep ^processo
UI組件之AdapterView及其子類(四)Gallery畫廊控件使用
convert cal instance ram scaletype 循環 reat targe 外觀 聽說 Gallery如今已經不使用了,API使用ViewPaper取代了,以後再學專研ViewPaper吧如今說說Gallery畫廊,就是不停顯示圖片的意思 Gall
MVC之排球比賽計分程序 ——(四)視圖的設計與實現
元素 role view logs image 技術 size 之前 log (view)視圖 視圖是用戶看到並與之交互的界面。對老式的Web應用程序來說,視圖就是由HTML元素組成的界面,在新式的Web應用程序中,HTML依舊在視圖中扮演著重要的角色,但一些新的技術
React 實踐項目 (四)
click browser itl ner ise 前端框架 export ive github React在Github上已經有接近70000的 star 數了,是目前最熱門的前端框架。而我學習React也有一段時間了,現在就開始用 React+Redux 進行實戰!
buildroot構建項目(四)--- u-boot 2017.11 適配開發板修改 2 ---- 系統啟動初始化之一
鏈接 extc 信號 分析 col clock -h 17.1 標誌位 一、代碼分析 上一節已經分析了鏈接文件,知道了首先代碼是從 _start 開始,之後設置了中斷向量表,然後從 start.s 開始運行。 _start:vectors.S (arch\arm\li
Formik官方應用案例解析(四)組件生命周期事件
ext discus side all trac mount prop use 新的 基礎 示例工程:formik-09x-component-lifecycle-events-example核心文件:index.js 示例說明 Formik大部分示例工程中都使用了極其方式
dva源碼解析(四)
delay put 進入 exe global 導出 數據處理 參數 text 轉載 原文:https://blog.csdn.net/zhangrui_web/article/details/79651812 dva.js 知識導圖 不知大家學 react 或 dva
0x10軟考|網路工程師經驗分享之網路互聯與網際網路(四)
目錄 一、傳輸層協議TCP 二、傳輸層協議UDP 三、常見應用層協議 一、傳輸層協議TCP TCP協議:傳輸控制協議,面向位元組流按順序、連線、可靠、全雙工,可變滑動視窗、緩衝累積傳送。協議號6 下面是TCP段(斷頭),TCP頭(傳輸頭),TCP包
機器學習之支持向量機(四)
應用 問題 計算過程 非線性 簡單 常熟 一段 約束 有關 引言: SVM是一種常見的分類器,在很長一段時間起到了統治地位。而目前來講SVM依然是一種非常好用的分類器,在處理少量數據的時候有非常出色的表現。SVM是一個非常常見的分類器,在真正了解他的原理之前我們多多少少
Spring原始碼解析(四)——元件註冊4
/** * 給容器中註冊元件; * 1)、包掃描+元件標註註解(@Controller/@Service/@Repository/@Component)[自己寫的類] * 2)、@Bean[匯入的第三方包裡面的元件] * 3)、@Import[快速給容器中匯入一個