在上一篇解析自定義命名空間的標籤 中,咱們已經知道解析自定義命名空間的標籤須要用到NamespaceHandler接口的實現類,而且知道spring是如何獲取命名空間對應的命名空間處理器對象的。所以咱們很容易就能在spring-context包下的META-INF/spring.handlers文件中找到http://www.springframework.org/schema/context命名空間(即本文說的context命名空間)的處理器org.springframework.context.config.ContextNamespaceHandler,下面是ContextNamespaceHandler類的源碼。java
public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }
ContextNamespaceHandler 實現了NamespaceHandler接口的init方法來爲context命名空間下的標籤註冊解析器BeanDefinitionParser對象。spring
Spring context命名空間有property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export和mbean-server 8個標籤。這8個標籤都有一個BeanDefinitionParser實現類與之對應。這一節咱們分別探討property-placeholder和property-override標籤的解析。markdown
property-placeholder標籤用於加載property屬性文件。若是bean的<property>value值與屬性文件中的某個值相同,那麼,默認狀況下,定義<property>的value值可使用表達式「${key}」,其中key爲屬性文件中=左邊的字符串。ide
property-placeholder標籤對應的BeanDefinitionParser實現類是PropertyPlaceholderBeanDefinitionParser,下面是這個類的繼承結構。
post
AbstractBeanDefinitionParser是PropertyPlaceholderBeanDefinitionParser類的一個抽象父類,它實現了BeanDefinitionParser接口的parse(Element element, ParserContext parserContext)方法,代碼以下。ui
@Override public final BeanDefinition parse(Element element, ParserContext parserContext) { // 調用抽象方法parseInternal(Element element, ParserContext parserContext) // 這個方法有子類實現,把解析指定Element對象的任務交給子類完成 AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { // 解析並未bean生成一個id值 String id = resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error( "Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element); } String[] aliases = null; // 檢測是否應該把name屬性做爲bean的別名,默認爲true // 子類能夠重寫shouldParseNameAsAliases()來決定 if (shouldParseNameAsAliases()) { // 獲取bean的別名 String name = element.getAttribute("name"); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } // 建立BeanDefinitionHolder對象 BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); // 註冊BeanDefintion registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { parserContext.getReaderContext().error(ex.getMessage(), element); return null; } } return definition; }
AbstractBeanDefinitionParser的parse方法,首先把解析節點的任務交給子類來完成,子類須要實現parseInternal(Element element, ParserContext parserContext)方法並返回一個AbstractBeanDefinition 對象;而後根據須要設置bean的id和別名;最後建立並註冊BeanDefinitionHolder對象。下面咱們重點看parseInternal方法,在PropertyPlaceholderBeanDefinitionParser的繼承體系中,parseInternal方法的實如今AbstractSingleBeanDefinitionParser類中,代碼以下。編碼
@Override protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { // 建立BeanDefinitionBuilder對象,這個對象只是代理了一個GenericBeanDefinition對象 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if (parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } // 獲取要實例化bean的類對象,默認爲null,通常由子類提供。 Class<?> beanClass = getBeanClass(element); if (beanClass != null) { builder.getRawBeanDefinition().setBeanClass(beanClass); } else { String beanClassName = getBeanClassName(element); if (beanClassName != null) { builder.getRawBeanDefinition().setBeanClassName(beanClassName); } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); if (parserContext.isNested()) { // 嵌套的bean定義,必須和外層的bean在同一個做用域 builder.setScope(parserContext.getContainingBeanDefinition().getScope()); } if (parserContext.isDefaultLazyInit()) { // 默認爲延遲加載 builder.setLazyInit(true); } // 把繼續解析標籤的任務交給子類 doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
AbstractSingleBeanDefinitionParser的parseInternal方法建立了一個BeanDefinitionBuilder對象,這個對象只是代理了GenericBeanDefinition對象,以簡化對GenericBeanDefinition對象的操做,而後就是一些基本的配置,這在代碼中已經體現了。parseInternal也並未對節點作實質性的操做,它只調用爲子類建立的一些鉤子方法,這些方法有:spa
用於獲取要實例化bean的類(Class)對象或者類全名稱的方法:.net
protected Class<?> getBeanClass(Element element) { return null; } protected String getBeanClassName(Element element) { return null; }
以及用於進一步解析節點的方法代理
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { doParse(element, builder); } protected void doParse(Element element, BeanDefinitionBuilder builder) { }
property-placeholder標籤的解析器PropertyPlaceholderBeanDefinitionParser類重寫了上面2組方法中的getBeanClass(Element element)方法和doParse(Element element, BeanDefinitionBuilder builder) 方法。
下面是getBeanClass(Element element)方法的源碼。
@Override protected Class<?> getBeanClass(Element element) { // 從spring3.1開始system-properties-mode屬性的默認值就爲ENVIRONMENT,再也不是FALLBACK if ("ENVIRONMENT".equals(element.getAttribute("system-properties-mode"))) { // PropertySourcesPlaceholderConfigurer對象能夠從上下文的環境對象中獲取屬性值 return PropertySourcesPlaceholderConfigurer.class; } return PropertyPlaceholderConfigurer.class; }
說明:PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer都是抽象類PlaceholderConfigurerSupport的直接子類,見總結。
下面是doParse(Element element, BeanDefinitionBuilder builder)方法的源碼。
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { super.doParse(element, builder); builder.addPropertyValue("ignoreUnresolvablePlaceholders", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); String systemPropertiesModeName = element.getAttribute("system-properties-mode"); if (StringUtils.hasLength(systemPropertiesModeName) && !systemPropertiesModeName.equals("system-properties-mode")) { builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName); } // 指定一個分隔符用於分隔默認值,默認爲英文冒號: // 好比${user.name:chyohn},若是user.name沒有在屬性文件中定義,則使用默認值chyohn // 假設設置的分隔符爲英文?,則上面的定義應該爲${name?choyhn} if (element.hasAttribute("value-separator")) { builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator")); } // 設置是否容許trim獲取到的屬性值,默認爲false if (element.hasAttribute("trim-values")) { builder.addPropertyValue("trimValues", element.getAttribute("trim-values")); } // 指定一個值用於表示null,好比指定的爲hasNull, // 若是某一個屬性值定義的時候爲haveNull,那麼生成的bean的這個屬性真正值就是null if (element.hasAttribute("null-value")) { builder.addPropertyValue("nullValue", element.getAttribute("null-value")); } }
PropertyPlaceholderBeanDefinitionParser類的doParse方法首先調用父類AbstractPropertyLoadingBeanDefinitionParser的doParse方法,而後根據節點配置的屬性值來修改PlaceholderConfigurerSupport的屬性值。這裏咱們在繼續看看AbstractPropertyLoadingBeanDefinitionParser的doParse方法源代碼。
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { // 獲取屬性文件的地址參數 String location = element.getAttribute("location"); if (StringUtils.hasLength(location)) { // 若是有多個屬性文件,每一個屬性文件可使用英文逗號隔開。 String[] locations = StringUtils.commaDelimitedListToStringArray(location); builder.addPropertyValue("locations", locations); } // 獲取指定的Properties對象的bean名稱 // 若是local-override屬性爲true,這裏設置的properties將覆蓋屬性文件中的內容 String propertiesRef = element.getAttribute("properties-ref"); if (StringUtils.hasLength(propertiesRef)) { builder.addPropertyReference("properties", propertiesRef); } // 獲取屬性文件編碼 String fileEncoding = element.getAttribute("file-encoding"); if (StringUtils.hasLength(fileEncoding)) { builder.addPropertyValue("fileEncoding", fileEncoding); } // 設置排序,即優先級 String order = element.getAttribute("order"); if (StringUtils.hasLength(order)) { builder.addPropertyValue("order", Integer.valueOf(order)); } // 設置是否忽略指定的屬性文件不存在的錯誤,true爲是 builder.addPropertyValue("ignoreResourceNotFound", Boolean.valueOf(element.getAttribute("ignore-resource-not-found"))); // 通常來講,屬性文件的內容更後加載。 // 若是localOverride爲true,那麼PropertiesLoaderSupport的localProperties內容就會覆蓋屬性文件中相同key的的內容。 // 若是標籤是property-placeholder且localOverride爲true,上下文的環境對象中的數據也會覆蓋屬性文件中相同key的內容。 // 默認爲false builder.addPropertyValue("localOverride", Boolean.valueOf(element.getAttribute("local-override"))); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); }
property-override標籤的做用是爲xml配置文件中的bean的屬性指定最終結果。
property-override標籤的解析器類爲PropertyOverrideBeanDefinitionParser,這個類和property-placeholder標籤的解析器類PropertyPlaceholderBeanDefinitionParser同樣是AbstractPropertyLoadingBeanDefinitionParser類的直接子類,而且PropertyOverrideBeanDefinitionParser重寫了getBeanClass(Element element)和doParse(Element element, BeanDefinitionBuilder builder)方法,代碼以下。
@Override protected Class<?> getBeanClass(Element element) { return PropertyOverrideConfigurer.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { super.doParse(element, builder); // 設置忽略不正確的鍵,也就是忽略格式不正確的鍵,或者是.左邊的bean名稱不存在的鍵 // 默認爲false,表示不忽略 builder.addPropertyValue("ignoreInvalidKeys", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); }
關於AbstractPropertyLoadingBeanDefinitionParser類在前面已經探討過了,關於這段代碼也就沒有什麼要多說的。
(1)property-override和property-placeholder的不一樣點。
功能不同,property-override標籤的做用是爲xml配置文件中的bean的屬性指定最終結果;而property-placeholder標籤的做用是把xml配置文件中bean 的<property>標籤的value值替換成正真的值,並且<property>標籤的value值必須符合特定的表達式格式,默認爲「${key}」,其中key爲屬性文件中的key。
屬性文件內容要求不同,property-override標籤加載的properties文件中的key的格式有嚴格的要求,必須爲「bean名稱.bean屬性」。若是屬性ignore-unresolvable的值爲false,那麼屬性文件中的bean名稱必須在當前容器中能找到對應的bean。
(2)property-override和property-placeholder的共同點。
二者都是以properties文件做爲數據來源。
二者的解析器BeanDefinitionParser類都繼承自AbstractPropertyLoadingBeanDefinitionParser類。所以它們共有AbstractPropertyLoadingBeanDefinitionParser及其父類中所處理的標籤屬性,而且這些屬性在兩個標籤中具備相同的做用。這其實都歸於它們所表明的工廠後處理器都繼承了PropertiesLoaderSupport類,具體看下面的繼承結構圖,其中第一個是property-override的,後面兩個是property-placeholder的。