1. spring5源碼講解-bean的加載過程(一)

spring-core源碼解析-(1)

基本

本部分從最基本的Spring開始铆农。配置文件:

<?xml version="1.0" encoding="UTF-8"?>    
<beans>    
    <bean class="base.SimpleBean"></bean>
</beans>

啟動代碼:

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
    SimpleBean bean = context.getBean(SimpleBean.class);
    bean.send();
    context.close();
}

SimpleBean:

public class SimpleBean {
    public void send() {
        System.out.println("I am send method from SimpleBean!");
    }
}

ClassPathXmlApplicationContext

整個繼承體系如下:

ClassPathXmlApplicationContext繼承體系

ResourceLoader代表了加載資源的一種方式伸蚯,正是策略模式的實現(xiàn)

構造器源碼:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {
    super(parent);
    setConfigLocations(configLocations);
    //默認true
    if (refresh) {
        refresh();
    }
}

構造器

首先看父類構造器晾嘶,沿著繼承體系一直向上調(diào)用熟吏,直到AbstractApplicationContext:

public AbstractApplicationContext(ApplicationContext parent) {
    this();
    setParent(parent);
}
public AbstractApplicationContext() {
    this.resourcePatternResolver = getResourcePatternResolver();
}

getResourcePatternResolver:

protected ResourcePatternResolver getResourcePatternResolver() {
    return new PathMatchingResourcePatternResolver(this);
}

PathMatchingResourcePatternResolver支持Ant風格的路徑解析距糖。

設置配置文件路徑

即AbstractRefreshableConfigApplicationContext.setConfigLocations:

public void setConfigLocations(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;
    }
}

resolvePath:

protected String resolvePath(String path) {
    return getEnvironment().resolveRequiredPlaceholders(path);
}

此方法的目的在于將占位符(placeholder)解析成實際的地址玄窝。比如可以這么寫: new ClassPathXmlApplicationContext("classpath:config.xml");那么classpath:就是需要被解析的。

getEnvironment方法來自于ConfigurableApplicationContext接口悍引,源碼很簡單恩脂,如果為空就調(diào)用createEnvironment創(chuàng)建一個。AbstractApplicationContext.createEnvironment:

protected ConfigurableEnvironment createEnvironment() {
    return new StandardEnvironment();
}

Environment接口

StandardEnvironment繼承體系:

StandardEnvironment繼承體系

Environmen接口代表了當前應用所處的環(huán)境趣斤。從此接口的方法可以看出俩块,其主要和profile、Property相關浓领。

Profile

Spring Profile特性是從3.1開始的玉凯,其主要是為了解決這樣一種問題: 線上環(huán)境和測試環(huán)境使用不同的配置或是數(shù)據(jù)庫或是其它。有了Profile便可以在 不同環(huán)境之間無縫切換联贩。Spring容器管理的所有bean都是和一個profile綁定在一起的壮啊。使用了Profile的配置文件示例:

<beans profile="develop">  
    <context:property-placeholder location="classpath*:jdbc-develop.properties"/>  
</beans>  
<beans profile="production">  
    <context:property-placeholder location="classpath*:jdbc-production.properties"/>  
</beans>  
<beans profile="test">  
    <context:property-placeholder location="classpath*:jdbc-test.properties"/>  
</beans>

在啟動代碼中可以用如下代碼設置活躍(當前使用的)Profile:

context.getEnvironment().setActiveProfiles("dev");

當然使用的方式還有很多(比如注解),參考:

spring3.1 profile 配置不同的環(huán)境

Spring Profiles example

Property

這里的Property指的是程序運行時的一些參數(shù)撑蒜,引用注釋:

properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,Maps, and so on.

Environment構造器

private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
public AbstractEnvironment() {
    customizePropertySources(this.propertySources);
}
PropertySources接口

繼承體系:

PropertySources繼承體系

此接口實際上是PropertySource的容器歹啼,默認的MutablePropertySources實現(xiàn)內(nèi)部含有一個CopyOnWriteArrayList作為存儲載體。

StandardEnvironment.customizePropertySources:

/** System environment property source name: {@value} */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value} */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(new MapPropertySource
        (SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    propertySources.addLast(new SystemEnvironmentPropertySource
        (SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
PropertySource接口

PropertySource接口代表了鍵值對的Property來源座菠。繼承體系:

PropertySource繼承體系

AbstractEnvironment.getSystemProperties:

@Override
public Map<String, Object> getSystemProperties() {
    try {
        return (Map) System.getProperties();
    }
    catch (AccessControlException ex) {
        return (Map) new ReadOnlySystemAttributesMap() {
            @Override
            protected String getSystemAttribute(String attributeName) {
                try {
                    return System.getProperty(attributeName);
                }
                catch (AccessControlException ex) {
                    if (logger.isInfoEnabled()) {
                        logger.info(format("Caught AccessControlException when accessing system " +
                                "property [%s]; its value will be returned [null]. Reason: %s",
                                attributeName, ex.getMessage()));
                    }
                    return null;
                }
            }
        };
    }
}

這里的實現(xiàn)很有意思狸眼,如果安全管理器阻止獲取全部的系統(tǒng)屬性,那么會嘗試獲取單個屬性的可能性浴滴,如果還不行就拋異常了拓萌。

getSystemEnvironment方法也是一個套路,不過最終調(diào)用的是System.getenv升略,可以獲取jvm和OS的一些版本信息微王。

路徑Placeholder處理

AbstractEnvironment.resolveRequiredPlaceholders:

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    //text即配置文件路徑,比如classpath:config.xml
    return this.propertyResolver.resolveRequiredPlaceholders(text);
}

propertyResolver是一個PropertySourcesPropertyResolver對象:

private final ConfigurablePropertyResolver propertyResolver =
            new PropertySourcesPropertyResolver(this.propertySources);
PropertyResolver接口

PropertySourcesPropertyResolver繼承體系(排除Environment分支):

PropertySourcesPropertyResolver繼承關系

此接口正是用來解析PropertyResource品嚣。

解析

AbstractPropertyResolver.resolveRequiredPlaceholders:

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    if (this.strictHelper == null) {
        this.strictHelper = createPlaceholderHelper(false);
    }
    return doResolvePlaceholders(text, this.strictHelper);
}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
    //三個參數(shù)分別是${, }, :
    return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
        this.valueSeparator, ignoreUnresolvablePlaceholders);
}

doResolvePlaceholders:

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
    //PlaceholderResolver接口依然是策略模式的體現(xiàn)
    return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
        @Override
        public String resolvePlaceholder(String placeholderName) {
            return getPropertyAsRawString(placeholderName);
        }
    });
}

其實代碼執(zhí)行到這里的時候還沒有進行xml配置文件的解析炕倘,那么這里的解析placeHolder是什么意思呢,原因在于可以這么寫:

System.setProperty("spring", "classpath");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("${spring}:config.xml");
SimpleBean bean = context.getBean(SimpleBean.class);

這樣就可以正確解析翰撑。placeholder的替換其實就是字符串操作罩旋,這里只說一下正確的屬性是怎么來的。實現(xiàn)的關鍵在于PropertySourcesPropertyResolver.getProperty:

@Override
protected String getPropertyAsRawString(String key) {
    return getProperty(key, String.class, false);
}
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        for (PropertySource<?> propertySource : this.propertySources) {
            Object value = propertySource.getProperty(key);
            return value;
        }
    }
    return null;
}

很明顯了眶诈,就是從System.getProperty和System.getenv獲取涨醋,但是由于環(huán)境變量是無法自定義的,所以其實此處只能通過System.setProperty指定逝撬。

注意浴骂,classpath:XXX這種寫法的classpath前綴到目前為止還沒有被處理。

關注公眾號: 太上老君007
spring源碼分析第一時間通知你

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宪潮,一起剝皮案震驚了整個濱河市溯警,隨后出現(xiàn)的幾起案子趣苏,更是在濱河造成了極大的恐慌,老刑警劉巖愧膀,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谣光,居然都是意外死亡檩淋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門萄金,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蟀悦,“玉大人,你說我怎么就攤上這事氧敢∪崭辏” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵孙乖,是天一觀的道長浙炼。 經(jīng)常有香客問我,道長唯袄,這世上最難降的妖魔是什么弯屈? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮恋拷,結果婚禮上资厉,老公的妹妹穿的比我還像新娘。我一直安慰自己蔬顾,他們只是感情好宴偿,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诀豁,像睡著了一般窄刘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舷胜,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天都哭,我揣著相機與錄音,去河邊找鬼逞带。 笑死欺矫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的展氓。 我是一名探鬼主播穆趴,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遇汞!你這毒婦竟也來了未妹?” 一聲冷哼從身側響起簿废,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎络它,沒想到半個月后族檬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡化戳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年单料,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片点楼。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡扫尖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掠廓,到底是詐尸還是另有隱情换怖,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布蟀瞧,位于F島的核電站沉颂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏悦污。R本人自食惡果不足惜兆览,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塞关。 院中可真熱鬧抬探,春花似錦、人聲如沸帆赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椰于。三九已至怠益,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘾婿,已是汗流浹背蜻牢。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偏陪,地道東北人抢呆。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像笛谦,于是被迫代替她去往敵國和親抱虐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345