在Spring容器(BeanFactory)建立後,就須要開始了Bean建立。Bean建立又分爲兩個階段:html
1) 準備Bean定義java
在全部的Bean建立以前,由BeanFactoryPostProcessor建立一個PlaceholderConfiguer(3.1以前是PropertyPlaceholderConfiguer,以後是PropertySourcesPlaceholderConfigurer)。這個PlaceholderConfiguer用於將Bean定義中引用的屬性${}進行替換。spring
這個過程是在BeanFactoryPostProcessor#postProcessBeforeInstantation()中完成。express
2)建立Bean並使用BeanPostProcessor對Bean進行注入app
2.1在此階段,由於須要對每個新建立的Bean進行屬性注入,因此 要先建立BeanPostProcessor對象,由於Spring要支持自動注入,因此最終建立的是一個AutowiredAnnotationBeanPostProcessor對象。ide
2.2接下來根據Bean定義一個一個的建立Bean。post
2.2.1 在Bean對象建立完後,就使用AutowiredAnnotationBeanPostProcessor # postProcessPropertyValues() 進行注入。this
這個過程實際上是根據類中的@Autowired,@Value,@Inject,@Resource 進行注入。lua
注入的時候,必然會從BeanFactory中獲取依賴,獲取屬性配置等。特別是在使用@Value的注入,還會觸發Spring EL 解析,取值操做。spa
獲取依賴值後,使用反射方式注入。
2.2.2 BeanPostProcessor#postProcessBeforeInitialization()
該過程調用的是 @PostConstruct
2.2.2 InitializingBean#afterProperties()
2.2.3 XML(init-method)
在Bean定義的準備階段,會使用PlaceholderConfigurer對Bean定義中的佔位符進行替換。
PlaceholderConfigurer目前有兩個版本:Spring 3.1以前使用的是PropertyPlaceholderConfiguer,以後使用的是PropertySourcesPlaceholderConfigurer,後者比前者更具擴展性。
當你在applicationContext.xml中使用了<context:property-placeholder>時,Spring就會自動的根據applicationContext.xml中引用的spring-context-VERSION.xsd版原本自動建立實現。此外,不論使用哪一種實現,都只會建立一個實例,因此配置多個會無效的。
PropertyPlaceholderConfigurer postProcessBeanFactory: // 把多個配置文件合併到一個Properties集合中。 Properties mergedProps = mergeProperties(); // Convert the merged properties, if necessary. convertProperties(mergedProps); // Let the subclass process the properties. processProperties(beanFactory, mergedProps): replace bean define 's placeholder 當替換placeholder時,會從mergedProps 和(或)systemProperties中找對應的值。 雖然默認是會從systemProperties找的,可是從systemProperties中找並非必須的, 若是不想從systemProperties中找,須要調用PropertyPlaceholderConfigurer#setSearchSystemEnvironment(false) 若是會從systemProperties中找的話, 順序是: protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) { String propVal = null; if (systemPropertiesMode == 2) { propVal = resolveSystemProperty(placeholder); } if (propVal == null) { propVal = resolvePlaceholder(placeholder, props); } if (propVal == null && systemPropertiesMode == 1) { propVal = resolveSystemProperty(placeholder); // 從系統屬性找,不包含操做系統的環境變量 } return propVal; } systemPropertiesMode 的默認值是1,即默認是 配置文件優先,而後纔是systemProperties
支持:指定location, system properties, OS environment variables。
從3.1開始,在applicationContext.xml中指定<context:property-placeholder>,不會再生成PropertyPlaceholderConfigurer對象了,而是生成
PropertySourcesPlaceholderConfigurer對象。
這兩個類的做用是同樣的,都是用來替換 bean define中的佔位符的。而PropertySourcesPlaceholderConfigurer的大概結構是:
class PropertySourcesPlaceholderConfigurer { private Properties localProperties; private Resource[] locations; private boolean localOverride = false; // 默認是false private Environment environment; private PropertySources propertySources; public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (this.propertySources == null) { // 建立propertySources this.propertySources = new MutablePropertySources(); // 把Environment添加到 propertySources // Environment 默認包括:command line arguments, systemProperties, environment variables if (this.environment != null) { this.propertySources.addLast( new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { @Override public String getProperty(String key) { return this.source.getProperty(key); } } ); } try { // localPropertySource 是 localProperties 與 locations 合併的產物,具體參見mergeProperties() // 若是localOverride ==true, 則 localPropertySource 優先於 Environment , // 不然 Environment 優先於 localPropertySource, PropertySource<?> localPropertySource = new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties()); if (this.localOverride) { this.propertySources.addFirst(localPropertySource); } else { this.propertySources.addLast(localPropertySource); } } catch (IOException ex) { throw new BeanInitializationException("Could not load properties", ex); } } // 替換佔位符 processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources)); } protected Properties mergeProperties() throws IOException { Properties result = new Properties(); if (this.localOverride) { // Load properties from file upfront, to let local properties override. loadProperties(result); } if (this.localProperties != null) { for (Properties localProp : this.localProperties) { CollectionUtils.mergePropertiesIntoMap(localProp, result); } } if (!this.localOverride) { // Load properties from file afterwards, to let those properties override. loadProperties(result); } return result; } }
引入了PropertySource, Environment的概念。一個PropertySource能夠是一個Properties對象,能夠是一個.properties文件等,java的系統屬性、操做系統的環境變量、Java程序的command line 參數等,均可以視爲一個PropertSource。
每個PropertySource都有一個名字,做爲整個PropertySource的標識。
Environment提供了一個運行環境的概念,特別是在Spring Boot中,將其運用的出神入化。它其實又是多個PropertySource的集合。
StandardEnvironment中,會將系統屬性和操做系統環境分別轉換爲一個PropertySource,做爲內置。
其中系統屬性被轉爲名爲systemProperties的PropertySource,環境變量被轉爲systemEnvironment的PropertySource。
Spring中的表達式能夠分爲兩大類:\${}, #{}。其中 \${}就是上面經過PlaceholerConfiger進行替換的。而#{}則是Spring的EL系統,它相似於OGNL等。
SpringEL是創建在Spring容器之上的,因此在解析SpringEL時,必然會支持要對容器、環境配置的訪問。
一個Spring EL的解析是由StandardBeanExpressionResolver來完成的:
StandardBeanExpressionResolver#evaluate : 它內部由一堆的PropertyAccessor, 由這些屬性訪問器來從對象導航讀取值 取值順序: 1)ReflectivePropertyAccessor ,且它是優先使用getter, setter, 找不到纔是 經過field反射。能夠對對象的字段進行read-write 2)BeanExpressionContextAccessor 從Spring EL的 context(也就是BeanExpressionContext)獲取, read-only 3)BeanFactoryAccessor 從BeanFactory(Spring Bean 容器)獲取依賴等 4)MapAccessor 訪問Java.util.Map 對象,基於key來讀取值 5)EnvironmentAccessor 訪問Spring Environment ,從Environment讀取值
和${}同樣,#{}既能夠在xml中使用,也能夠在@Value中使用。
上面已經說了@Value會在Bean建立後注入屬性時使用到,那麼也就是說在注入屬性時會觸發Spring EL的解析。
想要了解更多SpEL,參見:
https://docs.spring.io/spring/docs/4.3.19.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#expressions