Spring 注入

  • Spring注入
  1. Spring Bean生命週期
  2. PlaceholderConfigurer
  3. SpringEL

Spring Bean生命週期

 

 

在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)

 

PlaceholderConfigurer

        在Bean定義的準備階段,會使用PlaceholderConfigurer對Bean定義中的佔位符進行替換。

PlaceholderConfigurer目前有兩個版本:Spring 3.1以前使用的是PropertyPlaceholderConfiguer,以後使用的是PropertySourcesPlaceholderConfigurer,後者比前者更具擴展性。

   當你在applicationContext.xml中使用了<context:property-placeholder>時,Spring就會自動的根據applicationContext.xml中引用的spring-context-VERSION.xsd版原本自動建立實現。此外,不論使用哪一種實現,都只會建立一個實例,因此配置多個會無效的。

 

 

 

PropertyPlaceholderConfigurer 

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。  

 

PropertySourcePlaceholderConfigurer

 

從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。

 

SpringEL訪問

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

相關文章
相關標籤/搜索