【Spring 笔记】BeanFactoryPostProcessor 相关整理

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

1. 概述

  • BeanPostProcessor 是 Spring 提供一种扩展机制,该机制允许在 Bean 实例化之后初始化之际对 Bean 进行增强处理(前、后置处理)。具体可查看 【Spring 笔记】BeanPostProcessor 相关整理。
  • 在 Spring 容器启动 阶段,Spring 还提供了一种容器扩展机制 BeanFactoryPostProcessor,该机制作用于容器启动阶段,允许在容器实例化 Bean 之前对注册到该容器的 BeanDefinition 做出修改。

2. 原理

BeanFactoryPostProcessor

  • BeanFactoryPostProcessor 机制,提供在 Bean 实例化之前最后一次修改 BeanDefinition 的机会,可以对 BeanDefinition 进行一些额外的操作,比如更改某些 Bean 的一些属性,给某些 Bean 增加一些其他的信息等操作。
  • org.springframework.beans.factory.config.BeanFactoryPostProcessor 接口。
public interface BeanFactoryPostProcessor {

 /**
  * 1、Modify the application context's internal bean factory after its standard initialization.
  *
  * 2、All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.
  *
  * @param beanFactory the bean factory used by the application context
  * @throws org.springframework.beans.BeansException in case of errors
  */
 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
  • 仅有一个 postProcessBeanFactory() 方法,该方法接收一个 ConfigurableListableBeanFactory 类型的 beanFactory 参数。
    • 该方法的作用是在 standard initialization 之后(完成 BeanDefinition 的加载)对 bean factory 容器进行修改。
    • 其中的参数 beanFactory 就是已经完成了 standard initialization 的 BeanFactory 。
    • 作用的时机是所有的 BeanDefinition 已经完成了加载即加载至 BeanFactory 中,但是还没有完成初始化。
    • postProcessBeanFactory() 方法中,不能进行 Bean 的实例化工作,这样会导致 Bean 过早实例化,导致严重后果。
      • BeanFactoryPostProcessor 是与 BeanDefinition 打交道,想要与 Bean 打交道,可以使用 BeanPostProcessor
  • BeanPostProcessor 一样,BeanFactoryPostProcessor 同样支持排序,一个容器可以同时拥有多个 BeanFactoryPostProcessor,如果比较在乎顺序,可以实现 Ordered 接口。
  • 自定义 BeanFactoryPostProcessor,直接实现该接口即可。

2.1 用例

  • 配置


    
        
        
    
    

  • 用例
public class TestObj {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class Test implements BeanFactoryPostProcessor, Ordered {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("调用 postProcessBeanFactory...");
        System.out.println("容器中有 BeanDefinition 的个数:" + configurableListableBeanFactory.getBeanDefinitionCount());
        // 获取指定的 BeanDefinition
        BeanDefinition bd = configurableListableBeanFactory.getBeanDefinition("testObj");
        MutablePropertyValues pvs = bd.getPropertyValues();
        pvs.addPropertyValue("name", "testObj2");
        pvs.addPropertyValue("age", 20);
    }

    @Override
    public int getOrder() {
        return 1;
    }

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        TestObj testobj = (TestObj) context.getBean("testObj");
        System.out.println("name:" + testobj.getName() + ",age:" + testobj.getAge());
    }
}

/**
print
调用 postProcessBeanFactory...
容器中有 BeanDefinition 的个数:2
name:testObj2,age:20
**/

  • 一般情况下是不会主动去自定义 BeanFactoryPostProcessor,Spring 提供了几个常用的 BeanFactoryPostProcessor
    • PropertyPlaceholderConfigurer 允许在 XML 配置文件中使用占位符并将这些占位符所代表的资源单独配置到简单的 properties 文件中加载。
    • PropertyOverrideConfigurer 允许使用占位符来明确表明 bean 定义中的 property 与 properties 文件中的各配置项之间的对应关系。

2.2 PropertyPlaceholderConfigurer

  • 允许用 Properties 文件中的属性,来定义应用上下文(配置文件或者注解)。
【Spring 笔记】BeanFactoryPostProcessor 相关整理_第1张图片
PropertyPlaceholderConfigurer 体系
  • PropertyResourceConfigurer 中实现了 BeanFactoryPostProcessor 接口的 postProcessBeanFactory() 方法,实现了属性资源的配置。
// PropertyResourceConfigurer.java
// extends PropertiesLoaderSupport
// implements BeanFactoryPostProcessor, PriorityOrdered
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
        // 1. 返回合并的 Properties 实例
        Properties mergedProps = mergeProperties();

        // Convert the merged properties, if necessary.
        // 2. 转换合并属性
        convertProperties(mergedProps);

        // Let the subclass process the properties.
        // 3. 子类处理
        processProperties(beanFactory, mergedProps);
    } catch (IOException ex) {
        throw new BeanInitializationException("Could not load properties", ex);
    }
}
  • 方法的调用流程。
    • 步骤 1,调用 mergeProperties() 方法,返回合并的 Properties 实例。
      • Properties 实例维护着一组 key-value ,也就是 Properties 配置文件中的内容。
    • 步骤 2,调用 convertProperties() 方法,转换合并的值,将原始值替换为真正的值。
    • 步骤 3,调用 processProperties() 方法,前面两个步骤将配置文件中的值进行了处理,该方法是真正的替换过程,该方法由子类实现。
// PropertyResourceConfigurer.java
protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
        throws BeansException;
  • PropertyPlaceholderConfigurer 中,重写了 processProperties() 方法。
// PropertyPlaceholderConfigurer.java
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
        throws BeansException {
    // 1. 创建 StringValueResolver 对象
    StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
    // 2. 处理
    doProcessProperties(beanFactoryToProcess, valueResolver);
}
  • 方法的调用流程。
    • 步骤 1,构造一个 PlaceholderResolvingStringValueResolver 类型的 StringValueResolver 实例。
      • StringValueResolver 为一个解析 String 类型值的策略接口,该接口提供了 resolveStringValue() 方法,用于解析 String 值。
    • 步骤 2,得到 String 解析器的实例 valueResolver 后,则会调用 doProcessProperties() 方法,进行真值的替换操作。

步骤 1 中的 PlaceholderResolvingStringValueResolver

// PropertyPlaceholderConfigurer.java
private class PlaceholderResolvingStringValueResolver implements StringValueResolver {
    
    private final PropertyPlaceholderHelper helper;
    
    private final PlaceholderResolver resolver;
    
    public PlaceholderResolvingStringValueResolver(Properties props) {
        this.helper = new PropertyPlaceholderHelper(
                placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
        this.resolver = new PropertyPlaceholderConfigurerResolver(props);
    }
}
  • 在构造 String 值解析器 StringValueResolver 时,将已经解析的 Properties 实例对象封装在 PlaceholderResolver 实例 resolver 中。
  • PlaceholderResolver 是一个用于解析字符串中包含占位符的替换值的策略接口,该接口有一个 resolvePlaceholder() 方法,用于返回占位符的替换值。
  • PropertyPlaceholderHelper 工具 helper ,是进行替换的工具类。

步骤 2 中的 doProcessProperties

// PlaceholderConfigurerSupport.java
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
        StringValueResolver valueResolver) {
    // 2.1. 创建 BeanDefinitionVisitor 对象
    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) // 当前实例 PlaceholderConfigurerSupport 不在解析范围内
                && beanFactoryToProcess.equals(this.beanFactory))) { // 同一个 Spring 容器
            BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
            try {
                //2.2.
                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.
    // 2.3. 别名的占位符
    beanFactoryToProcess.resolveAliases(valueResolver);
    
    // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
    // 2.4. 解析嵌入值的占位符,例如注释属性
    beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
  • 方法的调用流程。
    • 步骤 2.1,根据 String 值解析策略 valueResolver 得到 BeanDefinitionVisitor 实例。
      • BeanDefinitionVisitorBeanDefinition 的访问者,通过它可以实现对 BeanDefinition 内容的访问,例如 Scope、PropertyValues、FactoryMethodName 等等。
    • 步骤 2.2,得到该容器的所有 BeanName,然后对其进行访问( visitBeanDefinition() 方法)。
    • 步骤 2.3,解析别名的占位符。
    • 步骤 2.4,解析嵌入值的占位符,例如注释属性。

visitBeanDefinition

  • 该方法访问了 BeanDefinition 中所有值得访问的东西,包括 parent 、class 、factory-bean 、factory-method 、scope 、property 、constructor-arg 。
// BeanDefinitionVisitor.java
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());
    }
}

visitPropertyValues

  • 对属性数组进行遍历,调用 resolveValue() 方法,对属性进行解析获取最新值,如果新值和旧值不等,则用新值替换旧值。
// BeanDefinitionVisitor.java
protected void visitPropertyValues(MutablePropertyValues pvs) {
    PropertyValue[] pvArray = pvs.getPropertyValues();
    // 遍历 PropertyValue 数组
    for (PropertyValue pv : pvArray) {
        // 解析真值
        Object newVal = resolveValue(pv.getValue());
        if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
            // 设置到 PropertyValue 中
            pvs.add(pv.getName(), newVal);
        }
    }
}

resolveValue

// BeanDefinitionVisitor.java
@Nullable
protected Object resolveValue(@Nullable Object value) {
    if (value instanceof BeanDefinition) {
        visitBeanDefinition((BeanDefinition) value);
    } else if (value instanceof BeanDefinitionHolder) {
        visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
    } else if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        String newBeanName = resolveStringValue(ref.getBeanName());
        if (newBeanName == null) {
            return null;
        }
        if (!newBeanName.equals(ref.getBeanName())) {
            return new RuntimeBeanReference(newBeanName);
        }
    } else if (value instanceof RuntimeBeanNameReference) {
        RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value;
        String newBeanName = resolveStringValue(ref.getBeanName());
        if (newBeanName == null) {
            return null;
        }
        if (!newBeanName.equals(ref.getBeanName())) {
            return new RuntimeBeanNameReference(newBeanName);
        }
    } else if (value instanceof Object[]) {
        visitArray((Object[]) value);
    } else if (value instanceof List) {
        visitList((List) value);
    } else if (value instanceof Set) {
        visitSet((Set) value);
    } else if (value instanceof Map) {
        visitMap((Map) value);
    } else if (value instanceof TypedStringValue) {
        TypedStringValue typedStringValue = (TypedStringValue) value;
        String stringValue = typedStringValue.getValue();
        if (stringValue != null) {
            String visitedString = resolveStringValue(stringValue);
            typedStringValue.setValue(visitedString);
        }
    // 由于 Properties 中的是 String,所以重点在此处
    } else if (value instanceof String) {
        return resolveStringValue((String) value);
    }
    return value;
}

// BeanDefinitionVisitor.java
@Nullable
protected String resolveStringValue(String strVal) {
    if (this.valueResolver == null) {
        throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
                "object into the constructor or override the 'resolveStringValue' method");
    }
    // 解析真值
    String resolvedValue = this.valueResolver.resolveStringValue(strVal);
    // Return original String if not modified.
    return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
}
  • valueResolver 是在构造 BeanDefinitionVisitor 实例时传入的 String 类型解析器 PlaceholderResolvingStringValueResolver,调用其 resolveStringValue() 方法。
// PropertyPlaceholderConfigurer.java
// 内部类 PlaceholderResolvingStringValueResolver.java
@Override
@Nullable
public String resolveStringValue(String strVal) throws BeansException {
    // 解析真值
    String resolved = this.helper.replacePlaceholders(strVal, this.resolver);
    // trim
    if (trimValues) {
        resolved = resolved.trim();
    }
    // 返回真值
    return (resolved.equals(nullValue) ? null : resolved);
}
  • helper 为 PropertyPlaceholderHelper 实例对象,而 PropertyPlaceholderHelper 则是处理应用程序中包含占位符的字符串工具类。
    • 在构造 helper 实例对象时需要传入了几个参数:placeholderPrefix、placeholderSuffix、valueSeparator,这些值在 PlaceholderConfigurerSupport 中定义。
// PlaceholderConfigurerSupport.java
/** Default placeholder prefix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/** Default placeholder suffix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/** Default value separator: {@value}. */
public static final String DEFAULT_VALUE_SEPARATOR = ":";

/** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
/** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX}. */
protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
/** Defaults to {@value #DEFAULT_VALUE_SEPARATOR}. */
@Nullable
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;

replacePlaceholders

  • 调用 PropertyPlaceholderHelperreplacePlaceholders() 方法,进行占位符替换。
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
    Assert.notNull(value, "'value' must not be null");
    return parseStringValue(value, placeholderResolver, new HashSet<>());
}

// PropertyPlaceholderHelper.java
protected String parseStringValue(String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders) {
    StringBuilder result = new StringBuilder(value);
    // 1. 获取前缀 "${" 的索引位置
    int startIndex = value.indexOf(this.placeholderPrefix);
    while (startIndex != -1) {
        // 2. 获取 后缀 "}" 的索引位置
        int endIndex = findPlaceholderEndIndex(result, startIndex);
        if (endIndex != -1) {
            // 3. 截取 "${" 和 "}" 中间的内容,这也就是我们在配置文件中对应的值
            String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
            String originalPlaceholder = placeholder;
            if (!visitedPlaceholders.add(originalPlaceholder)) {
                throw new IllegalArgumentException(
                        "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
            }
            // Recursive invocation, parsing placeholders contained in the placeholder key.
            // 解析占位符键中包含的占位符,真正的值
            placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
            // Now obtain the value for the fully resolved key...
            // 4. 从 Properties 中获取 placeHolder 对应的值 propVal
            String propVal = placeholderResolver.resolvePlaceholder(placeholder);
            // 如果不存在
            if (propVal == null && this.valueSeparator != null) {
                // 5. 查询 : 的位置
                int separatorIndex = placeholder.indexOf(this.valueSeparator);
                // 如果存在 :
                if (separatorIndex != -1) {
                    // 获取 : 前面部分 actualPlaceholder
                    String actualPlaceholder = placeholder.substring(0, separatorIndex);
                    // 获取 : 后面部分 defaultValue
                    String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                    // 从 Properties 中获取 actualPlaceholder 对应的值
                    propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                    // 如果不存在 则返回 defaultValue
                    if (propVal == null) {
                        propVal = defaultValue;
                    }
                }
            }
            if (propVal != null) {
                // Recursive invocation, parsing placeholders contained in the
                // previously resolved placeholder value.
                propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                if (logger.isTraceEnabled()) {
                    logger.trace("Resolved placeholder '" + placeholder + "'");
                }
                startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
            } else if (this.ignoreUnresolvablePlaceholders) {
                // Proceed with unprocessed value.
                // 忽略值
                startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
            } else {
                throw new IllegalArgumentException("Could not resolve placeholder '" +
                        placeholder + "'" + " in value \"" + value + "\"");
            }
            visitedPlaceholders.remove(originalPlaceholder);
        } else {
            startIndex = -1;
        }
    }
    // 6. 返回propVal,就是替换之后的值
    return result.toString();
}
  • 方法的调用流程。
    • 步骤 1,获取占位符前缀 " ${ " 的索引位置 startIndex 。
    • 步骤 2,如果前缀 " ${ " 存在,则从 " { " 后面开始获取占位符后缀 " } " 的索引位置 endIndex 。
    • 步骤 3,如果前缀 " ${ " 和后缀 " } " 都存在,则截取中间部分 placeholder。
    • 步骤 4,从 Properties 中获取 placeHolder 对应的值 propVal。
    • 步骤 5,如果 propVal 为空,则判断占位符中是否存在 " : "。
      • 如果存在则对占位符进行分割处理,前面部分为 actualPlaceholder,后面部分 defaultValue。
      • 尝试从 Properties 中获取 actualPlaceholder 对应的值 propVal。
      • 如果不存在,则将 defaultValue 的值赋值给 propVal。
    • 步骤 6,返回 propVal,也就是 Properties 中对应的值。

2.2.1 应用

  • PropertyPlaceholderConfigurer 允许在 XML 配置文件中使用占位符并将这些占位符所代表的资源单独配置到简单的 properties 文件中来加载。
  • 主要的使用场景有。
    • 动态加载配置文件,进行多环境切换。
    • 属性加解密。
  • 在项目开发过程中,会存在多个环境,如 dev 、test 、prod 等等,各个环境的配置都会不一样。可以利用 PropertyPlaceholderConfigurer 的特性来动态加载配置文件,实现多环境切换

配置文件

  • application-dev.properties
testObj.name=dev
  • application-test.properties
testObj.name=test
  • application-prod.properties
testObj.name=prod
  • spring.xml


    
        
            
                classpath:application-dev.properties
                classpath:application-test.properties
                classpath:application-prod.properties
            
        
    
    
        
    

用例

public class Test extends PropertyPlaceholderConfigurer {

    private Resource[] locations;

    private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();

    @Override
    public void setLocations(Resource[] locations) {
        this.locations = locations;
    }

    @Override
    public void setLocalOverride(boolean localOverride) {
        this.localOverride = localOverride;
    }

    /**
     * 覆盖这个方法,根据启动参数,动态读取配置文件
     *
     * @param props
     * @throws IOException
     */
    @Override
    protected void loadProperties(Properties props) throws IOException {
        if (locations != null) {
            // locations 里面已经包含了三个自定义文件
            for (Resource location : this.locations) {
                InputStream is = null;
                try {
                    String filename = location.getFilename();
                    String env = "application-" + System.getProperty("spring.profiles.active", "dev") + ".properties";

                    // 找到需要的文件,加载
                    if (filename.contains(env)) {
                        logger.info("Loading properties file from " + location);
                        is = location.getInputStream();
                        this.propertiesPersister.load(props, is);

                    }
                } catch (IOException ex) {
                    logger.info("读取配置文件失败.....");
                    throw ex;
                } finally {
                    if (is != null) {
                        is.close();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        TestObj testobj = (TestObj) context.getBean("testObj");
        System.out.println("name:" + testobj.getName());
    }
}

/**
name:dev
**/
  • -Dspring.profiles.active 调整为 test,则打印结果是 test,这样就实现了根据不同的环境加载不同的配置。

2.3 PropertyOverrideConfigurer

  • PropertyOverrideConfigurer 允许对 Spring 容器中配置的任何 bean 定义的 property 信息进行覆盖替换。

2.3.1 应用

示例 1

  • application.properties
testObj.name=default
  • spring.xml


    
        
            
                classpath:application.properties
            
        
    
    
        
    

  • 用例
public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        TestObj testobj = (TestObj) context.getBean("testObj");
        System.out.println("name:" + testobj.getName());
    }
}

/**
name:default
**/
  • PropertyOverrideConfigurer 定义的文件取代了 bean 中的值。

示例 2

  • application.properties
testObj.name=PropertyOverrideConfigurer
  • application-dev.properties
testObj.name=PropertyPlaceholderConfigurer
  • spring.xml


    
        
            
                classpath:application.properties
            
        
    
    
        
            
                classpath:application-dev.properties
            
        
    
    
        
    

  • 用例
public class Test extends PropertyPlaceholderConfigurer {

    private Resource[] locations;

    private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();

    @Override
    public void setLocations(Resource[] locations) {
        this.locations = locations;
    }

    @Override
    public void setLocalOverride(boolean localOverride) {
        this.localOverride = localOverride;
    }

    /**
     * 覆盖这个方法,根据启动参数,动态读取配置文件
     *
     * @param props
     * @throws IOException
     */
    @Override
    protected void loadProperties(Properties props) throws IOException {
        if (locations != null) {
            // locations 里面已经包含了三个自定义文件
            for (Resource location : this.locations) {
                InputStream is = null;
                try {
                    String filename = location.getFilename();
                    String env = "application-" + System.getProperty("spring.profiles.active", "dev") + ".properties";

                    // 找到需要的文件,加载
                    if (filename.contains(env)) {
                        logger.info("Loading properties file from " + location);
                        is = location.getInputStream();
                        this.propertiesPersister.load(props, is);

                    }
                } catch (IOException ex) {
                    logger.info("读取配置文件失败.....");
                    throw ex;
                } finally {
                    if (is != null) {
                        is.close();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        TestObj testobj = (TestObj) context.getBean("testObj");
        System.out.println("name:" + testobj.getName());
    }
}

/**
name:PropertyOverrideConfigurer
**/
  • PropertyOverrideConfigurer 后作用,会直接取代 PropertyPlaceholderConfigurer 定义的值,无论如何都会显示 PropertyOverrideConfigurer 定义的值。

2.3.2 原理

【Spring 笔记】BeanFactoryPostProcessor 相关整理_第2张图片
PropertyOverrideConfigurer 体系
  • PropertyOverrideConfigurer 继承 PropertyResourceConfigurer 类,并实现了 processProperties() 方法。
// PropertyOverrideConfigurer.java
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
        throws BeansException {
    // 迭代配置文件中的内容
    for (Enumeration names = props.propertyNames(); names.hasMoreElements();) {
        String key = (String) names.nextElement();
        try {
            processKey(beanFactory, key, props.getProperty(key));
        } catch (BeansException ex) {
            String msg = "Could not process key '" + key + "' in PropertyOverrideConfigurer";
            if (!this.ignoreInvalidKeys) {
                throw new BeanInitializationException(msg, ex);
            }
            if (logger.isDebugEnabled()) {
                logger.debug(msg, ex);
            }
        }
    }
}
  • 迭代 props 数组,依次调用 processKey() 方法。
// PropertyOverrideConfigurer.java
/**
 * The default bean name separator.
 */
public static final String DEFAULT_BEAN_NAME_SEPARATOR = ".";
/**
 * Bean 名字的分隔符
 */
private String beanNameSeparator = DEFAULT_BEAN_NAME_SEPARATOR;
/**
 * Contains names of beans that have overrides.
 */
private final Set beanNames = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
protected void processKey(ConfigurableListableBeanFactory factory, String key, String value)
        throws BeansException {
    // 判断是否存在 ".",即获取其索引位置
    int separatorIndex = key.indexOf(this.beanNameSeparator);
    if (separatorIndex == -1) {
        throw new BeanInitializationException("Invalid key '" + key +
                "': expected 'beanName" + this.beanNameSeparator + "property'");
    }
    // 得到 beanName
    String beanName = key.substring(0, separatorIndex);
    // 得到属性值
    String beanProperty = key.substring(separatorIndex+1);
    this.beanNames.add(beanName);
    // 替换
    applyPropertyValue(factory, beanName, beanProperty, value);
    if (logger.isDebugEnabled()) {
        logger.debug("Property '" + key + "' set to value [" + value + "]");
    }
}
  • 获取分割符 " . " 的索引位置,得到 beanName 以及相应的属性,然后调用 applyPropertyValue() 方法。
// PropertyOverrideConfigurer.java
protected void applyPropertyValue(ConfigurableListableBeanFactory factory, String beanName, String property, String value) {
    // 获得 BeanDefinition 对象
    BeanDefinition bd = factory.getBeanDefinition(beanName);
    BeanDefinition bdToUse = bd;
    while (bd != null) {
        bdToUse = bd;
        bd = bd.getOriginatingBeanDefinition();
    }
    // 设置 PropertyValue 到 BeanDefinition 中
    PropertyValue pv = new PropertyValue(property, value);
    pv.setOptional(this.ignoreInvalidKeys);
    bdToUse.getPropertyValues().addPropertyValue(pv);
}
  • 从容器中获取 BeanDefinition,然后根据属性 property 和其值 value 构造成一个 PropertyValue 对象,最后调用 addPropertyValue() 方法。
    • PropertyValue 是用于保存一组 bean 属性的信息和值的对像。
// MutablePropertyValues.java
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
    for (int i = 0; i < this.propertyValueList.size(); i++) {
        PropertyValue currentPv = this.propertyValueList.get(i);
        // 匹配
        if (currentPv.getName().equals(pv.getName())) {
            // 合并属性
            pv = mergeIfRequired(pv, currentPv);
            // 覆盖属性
            setPropertyValueAt(pv, i);
            return this;
        }
    }
    // 未匹配到,添加到 propertyValueList 中
    this.propertyValueList.add(pv);
    return this;
}
  • 添加 PropertyValue 对象,替换或者合并相同的属性值。

你可能感兴趣的:(【Spring 笔记】BeanFactoryPostProcessor 相关整理)