1. 程式人生 > >SpringBoot之淺析配置項解析(四)

SpringBoot之淺析配置項解析(四)

我們在之前的文章中簡單的說了一下SpringBoot對於預設的配置檔案的解析過程,在這一篇文章中我們再簡單的分析一下SpringBoot是怎麼將解析到的配置屬性資訊設定到相應的Bean上的。既然是用SpringBoot的屬性配置方式,那麼我們在這裡會在對應的類上加上ConfigurationProperties和Component(或是和Component相同功能的)註解。我們定義的Bean如下:

@Component
@ConfigurationProperties(prefix ="person.info")
public class PersonInfoDomain {
    /**
     * 使用者名稱
     */
private String userName; }

配置檔案如下:
application
其內容依次如下:

person.info.user-name=lisi

person:
  info:
    user-name: zhangzhang

person.info.user-name=zhangsan

person:
  info:
    user-name: lilisisi

首先問一個問題,如果是你來實現這樣的功能的話,你會在什麼時機來進行屬性值設定的功能呢?在建立Bean的時候進行屬性的設定應該是一個比較合適的時機吧?Spring也是這樣做的。在Spring中提供了各種不同功能的介面來讓我們在Bean建立的過程中做一些不同功能的擴充套件,我們先稱為”生命週期”的介面(可以在這裡進行檢視:

Spring Bean的生命週期小析(一) Spring Bean的生命週期小析(二)),在Spring在有這樣一個類:AbstractAutowireCapableBeanFactory這個類主要的一個功能是串聯Spring的生命週期。我們先看一下這個類中的這個方法:applyBeanPostProcessorsBeforeInitialization(不要問我為什麼知道這個方法。。。放一下呼叫鏈的截圖)
applyBeanPostProcessorsBeforeInitialization

    @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; }

getBeanPostProcessors

    @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[快速給容器中匯入一個