Spring占位符Placeholder的實(shí)現(xiàn)原理解析
占位符Placeholder的使用
xml中的配置:
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-lazy-init="false"> <context:property-placeholder location="classpath:application.properties"/> <bean id="user" class="com.morris.spring.entity.Author"> <property name="name" value="${author.name}" /> </bean> </beans>
實(shí)現(xiàn)原理
前面在Spring中自定義標(biāo)簽的解析中分析到context:property-placeholder
這種自定義標(biāo)簽的解析流程如下:
- 基于SPI機(jī)制,掃描所有類路徑下jar中/META-INFO/spring.handlers文件,并將這些文件讀取為一個(gè)key為namespace,value為具體NameSpaceHandler的Map結(jié)構(gòu)。
- 根據(jù)bean標(biāo)簽名獲得xml上方的namespace,然后根據(jù)namespace從第一步中的map中獲得具體的NameSpaceHandler。
- 調(diào)用NameSpaceHandler的init()方法進(jìn)行初始化,此方法一般會(huì)將負(fù)責(zé)解析各種localName的BeanDefinitionParser解析器注冊(cè)到一個(gè)map中。
- 根據(jù)localName=property-placeholder從上一步中獲得具體的BeanDefinitionParser解析器,并調(diào)用其parse()方法進(jìn)行解析。
在這里NameSpaceHandler為ContextNamespaceHandler,而BeanDefinitionParser解析器為PropertyPlaceholderBeanDefinitionParser,所以我們觀察的重點(diǎn)為PropertyPlaceholderBeanDefinitionParser的parse()方法。
注冊(cè)PropertySourcesPlaceholderConfigurer
parse()方法位于父類AbstractBeanDefinitionParser,先來看下繼承關(guān)系,后面的代碼使用了大量的模板方法模式,將會(huì)在這幾個(gè)類中來回切換:
org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse
public final BeanDefinition parse(Element element, ParserContext parserContext) { // 調(diào)用子類org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser.parseInternal AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { // 生成一個(gè)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; if (shouldParseNameAsAliases()) { String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); // 注冊(cè)BD registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { String msg = ex.getMessage(); parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element); return null; } } return definition; }
org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if (parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } // 獲取子類PropertyPlaceholderBeanDefinitionParser返回的PropertySourcesPlaceholderConfigurer 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)); BeanDefinition containingBd = parserContext.getContainingBeanDefinition(); if (containingBd != null) { // Inner bean definition must receive same scope as containing bean. builder.setScope(containingBd.getScope()); } if (parserContext.isDefaultLazyInit()) { // Default-lazy-init applies to custom bean definitions as well. builder.setLazyInit(true); } // 又是一個(gè)模板方法模式 /** * @see org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder) */ doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser#getBeanClass
protected Class<?> getBeanClass(Element element) { if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) { return PropertySourcesPlaceholderConfigurer.class; // 新版返回這個(gè) } return org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.class; }
org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser#doParse
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { // 調(diào)用父類的doParse super.doParse(element, parserContext, builder); builder.addPropertyValue("ignoreUnresolvablePlaceholders", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE); if (StringUtils.hasLength(systemPropertiesModeName) && !systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) { builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName); } if (element.hasAttribute("value-separator")) { builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator")); } if (element.hasAttribute("trim-values")) { builder.addPropertyValue("trimValues", element.getAttribute("trim-values")); } if (element.hasAttribute("null-value")) { builder.addPropertyValue("nullValue", element.getAttribute("null-value")); } }
org.springframework.context.config.AbstractPropertyLoadingBeanDefinitionParser#doParse
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { // 解析<context:property-placeholder>標(biāo)簽的各種屬性 String location = element.getAttribute("location"); if (StringUtils.hasLength(location)) { location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location); String[] locations = StringUtils.commaDelimitedListToStringArray(location); builder.addPropertyValue("locations", locations); } 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)); } builder.addPropertyValue("ignoreResourceNotFound", Boolean.valueOf(element.getAttribute("ignore-resource-not-found"))); builder.addPropertyValue("localOverride", Boolean.valueOf(element.getAttribute("local-override"))); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); }
總結(jié)一下,其實(shí)上面這么多代碼調(diào)來調(diào)去,只有一個(gè)目的,就是向spring容器中注入一個(gè)BeanDefinition,這個(gè)BeanDefinition有兩個(gè)最重要的屬性:
- BeanClass為PropertySourcesPlaceholderConfigurer。
- 有一個(gè)屬性為location,對(duì)應(yīng)properties文件的位置。
PropertySourcesPlaceholderConfigurer的調(diào)用
上面向spring容器中注入一個(gè)PropertySourcesPlaceholderConfigurer類型BeanDefinition,先來看下這個(gè)類的繼承關(guān)系:
從上圖的繼承關(guān)系可以看出PropertySourcesPlaceholderConfigurer實(shí)現(xiàn)了BeanFactoryPostProcessor,所以這個(gè)類的核心方法為postProcessBeanFactory()。
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (this.propertySources == null) { // null this.propertySources = new MutablePropertySources(); if (this.environment != null) { // environment中存儲(chǔ)的是系統(tǒng)屬性和環(huán)境變量 this.propertySources.addLast( new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { @Override @Nullable public String getProperty(String key) { return this.source.getProperty(key); } } ); } try { // 加載application.properties為Properties,包裝為PropertySource 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)); this.appliedPropertySources = this.propertySources; }
上面的方法的前面一大截的主要作用為將系統(tǒng)屬性、環(huán)境變量以及properties文件中的屬性整合到MutablePropertySources中,這樣就可以直接調(diào)用MutablePropertySources.getProperties()方法根據(jù)屬性名拿到對(duì)應(yīng)的屬性值了。MutablePropertySources里面其實(shí)是一個(gè)Map的鏈表,這樣就可以先遍歷鏈表,然后再根據(jù)屬性名從Map中找到對(duì)應(yīng)的屬性值。
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException { propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); // ${ propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); // } propertyResolver.setValueSeparator(this.valueSeparator); // : // 下面的doProcessProperties會(huì)回調(diào)這個(gè)lambda表達(dá)式 // 真正的解析邏輯在resolveRequiredPlaceholders /** * @see AbstractPropertyResolver#resolveRequiredPlaceholders(java.lang.String) */ StringValueResolver valueResolver = strVal -> { String resolved = (this.ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal)); if (this.trimValues) { resolved = resolved.trim(); } return (resolved.equals(this.nullValue) ? null : resolved); }; // 這里會(huì)遍歷所有的BD,挨個(gè)處理占位符 doProcessProperties(beanFactoryToProcess, valueResolver); }
org.springframework.beans.factory.config.PlaceholderConfigurerSupport#doProcessProperties
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) { BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); for (String curName : beanNames) { // Check that we're not parsing our own bean definition, // to avoid failing on unresolvable placeholders in properties file locations. if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); try { // 遍歷BD visitor.visitBeanDefinition(bd); } catch (Exception ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex); } } } // New in Spring 2.5: resolve placeholders in alias target names and aliases as well. beanFactoryToProcess.resolveAliases(valueResolver); // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes. beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); }
org.springframework.beans.factory.config.BeanDefinitionVisitor#visitBeanDefinition
public void visitBeanDefinition(BeanDefinition beanDefinition) { visitParentName(beanDefinition); visitBeanClassName(beanDefinition); visitFactoryBeanName(beanDefinition); visitFactoryMethodName(beanDefinition); visitScope(beanDefinition); if (beanDefinition.hasPropertyValues()) { // 遍歷所有的屬性 visitPropertyValues(beanDefinition.getPropertyValues()); } if (beanDefinition.hasConstructorArgumentValues()) { ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); visitIndexedArgumentValues(cas.getIndexedArgumentValues()); visitGenericArgumentValues(cas.getGenericArgumentValues()); } }
org.springframework.beans.factory.config.BeanDefinitionVisitor#visitPropertyValues
protected void visitPropertyValues(MutablePropertyValues pvs) { PropertyValue[] pvArray = pvs.getPropertyValues(); for (PropertyValue pv : pvArray) { // 解析占位符 Object newVal = resolveValue(pv.getValue()); if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) { // 將新的value替換BD中舊的 pvs.add(pv.getName(), newVal); } } }
resolveValue()方法中會(huì)回調(diào)到之前的lambda表達(dá)式StringValueResolv真正開始解析,也就是根據(jù)屬性名從PropertySources中取值。
總結(jié)一下PropertySourcesPlaceholderConfigurer#postProcessBeanFactory()方法:這個(gè)方法會(huì)在Bean實(shí)例化之前完成對(duì)Spring容器中所有BeanDefinition中帶有占位符的屬性進(jìn)行解析,這樣在Bean實(shí)例化后就能被賦予正確的屬性了。
到此這篇關(guān)于Spring占位符Placeholder的實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Spring占位符Placeholder內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot2.3 整合mybatis-plus 高級(jí)功能(圖文詳解)
這篇文章主要介紹了springboot2.3 整合mybatis-plus 高級(jí)功能,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Java服務(wù)如何調(diào)用系統(tǒng)指令、Bat腳本記錄
這篇文章主要介紹了Java服務(wù)如何調(diào)用系統(tǒng)指令、Bat腳本記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯的方案
最近項(xiàng)目要求部署到其他公司的服務(wù)器上,但是又不想將源碼泄露出去,要求對(duì)正式環(huán)境的啟動(dòng)包進(jìn)行安全性處理,防止客戶直接通過反編譯工具將代碼反編譯出來,本文介紹了如何對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯,需要的朋友可以參考下2024-08-08SpringBoot整合Scala構(gòu)建Web服務(wù)的方法
這篇文章主要介紹了SpringBoot整合Scala構(gòu)建Web服務(wù)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03