介绍
spring的IOC容器提供了BeanFactoryPostProcessor接口在容器实际实例化任何其他的bean之前读取配置元数据并且还可以修改它。也可以控制执行顺序,通过实现Order接口。
BeanFactoryPostProcessor接口的核心方法就是postProcessBeanFactory方法,将配置封装到BeanFactory中。
加载自定义配置文件主要就是PropertyPlaceholderConfigurer,其实现了PropertyPlaceholderConfigurer类实现了BeanFactoryPostProcessor接口,完成了Properties文件的获取,类型转换,加载到BeanFactory。
PropertyPlaceholderConfigurer关键源码
PropertyPlaceholderConfigurer主要是从抽象父类PropertiesLoaderSupport继承得到的父类的模板方法
## 设置本地的Properties文件
public void setProperties(Properties properties) {
this.localProperties = new Properties[] {properties};
}
## 将Properties文件解析,并加入到BeanFactory中
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
// 根据配置路径获取Properties实例
Properties mergedProps = mergeProperties();
// 将属性值转换并替换
convertProperties(mergedProps);
// 访问bean工厂中bean定义,替换${....}配置
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
## 根据配置路径获取Properties实例
/**
* Return a merged Properties instance containing both the
* loaded properties and properties set on this FactoryBean.
*/
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;
}
## 将属性值转换并替换
protected void convertProperties(Properties props) {
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
String convertedValue = convertProperty(propertyName, propertyValue);
if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) {
props.setProperty(propertyName, convertedValue);
}
}
}
## 访问bean工厂中bean定义,替换${....}配置
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
this.doProcessProperties(beanFactoryToProcess, valueResolver);
}
自定义配置
思路:
我们可以扩展加载配置文件的方法,其中可以重写原来的方法,或者在原来的方法上面包装一层。
- Properties文件加载工具类
public class SunpyPropertyUtils {
private static Logger log = Logger.getLogger(SunpyPropertyUtils.class);
private static final String classpath_header = "classpath:";
private static final String root_classpath_header = "classpath*:";
/**
* 根据spring配置的路径加载Properties文件
* 优先级:类路径文件 > 文件系统空间文件
* @param propertyPath
* @return
*/
public final static Properties getProperties(String propertyPath) {
if (StringUtils.isEmpty(propertyPath)) {
log.error("Properties File Path is null!");
return null;
}
if (propertyPath.startsWith(root_classpath_header)) {
Properties prop = getClasspathProperties(propertyPath.substring(11));
if (prop == null) {
getFileSystemProperties(classpath_header+propertyPath.substring(11));
}
}
if (propertyPath.startsWith(classpath_header)) {
return getFileSystemProperties(propertyPath);
}
return null;
}
/**
* 加载类路径上面的Properties文件
* @param propertyPath
* @return
*/
public final static Properties getClasspathProperties(String propertyPath) {
InputStream is = ClassLoader.getSystemResourceAsStream(propertyPath);
Properties prop = new Properties();
if (is == null) {
return null;
}
try {
prop.load(is);
return prop;
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
log.error(e);
}
}
return null;
}
/**
* 加载 文件系统路径上面的Properties文件
* @param propertyPath(格式 classpath:xxx.properties)
* @return
*/
public final static Properties getFileSystemProperties(String propertyPath) {
Properties prop = new Properties();
FileInputStream fis = null;
try {
File file = ResourceUtils.getFile(propertyPath);
fis = new FileInputStream(file);
prop.load(fis);
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
log.error(e);
}
}
return prop;
}
}
- 自定义PropertyPlaceholderConfigurer加载类
public class SunpyPropertyPlaceholder extends PropertyPlaceholderConfigurer{
// spring动态配置资源文件路径
private List<String> resourcePath;
// 当spring实例化的时候,使用setter方式注入
public void setResourcePath(List<String> resourcePath) {
this.resourcePath = resourcePath;
}
// 获得配置
@Override
protected Properties mergeProperties() throws IOException {
Properties props = new Properties();
if (CollectionUtils.isEmpty(resourcePath)) {
return null;
}
for (String path : resourcePath) {
Properties prop = SunpyPropertyUtils.getProperties(path);
if (prop != null) {
props.putAll(prop);
}
}
this.setProperties(props);
return props;
}
}
- 配置XML文件
<beans:bean id="sunpyPropertyPlaceholder" class="cn.spy.single.common.SunpyPropertyPlaceholder">
<beans:property name="resourcePath">
<beans:list>
<beans:value>classpath:config.properties</beans:value>
</beans:list>
</beans:property>
</beans:bean>
-
启动调用成功
网友评论