一、实现方式
通过spring的environmentPostProcessor扩展点将配置文件的配置项注入到准备环境中
二、具体实现
@Order(Ordered.LOWEST_PRECEDENCE)
@Data
public class AutoconfigEnvironmentPostProcessor implements EnvironmentPostProcessor, ApplicationListener<ApplicationEvent>, Ordered {
private static final DeferredLog log = new DeferredLog();
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
log.replayTo(AutoconfigEnvironmentPostProcessor.class);
}
// 要获取的资源名称
private static final String PROPERTY_SOURCE_NAME_OA = "applicationConfig: [classpath:/application-dev.yml]";
private static final String PROPERTY_SOURCE_NAME_PREFIX = "applicationConfig: [classpath:/application";
private static final String PROPERTY_SOURCE_NAME_SUFIX = ".yml]";
private static final String BOTOS_TRAP = "applicationConfig: [classpath:/bootstrap.yml]";
private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";
//默认项目配置文件
private static final String PROPERTY_SOURCE_NAME_DEFAULT = "applicationConfig: [classpath:/application.yml]";
private static final String CENTER = "applicationConfig: [file:cfg/application.yml]";
// A
private static final String APOLLO = "ApolloBootstrapPropertySources";
// yml资源加载器
private static final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
//关键方法,重写方法将全局属性塞入容器,由于这种重写方法是最先加载的,无法通过spring容器装载配置文件值,因此需要手动比对
//具体逻辑是先将配置文件读出来,然后将启动器中配置的默认值读出来,然后循环比对
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try {
PropertySource<?> propertySource = null;
log.info("【开始接管配置项】");
Map<String, Object> resultMap = getFieldsFromClass(new OaDefaultProperties(), "");
// String configName = checkBootsTrap(environment);
// 通过环境后置处理器获取加载完的环境变量(加载后处理)
String configName = getApplicationFileName(environment);
if (environment.getPropertySources().contains(configName)) {
propertySource = environment.getPropertySources().get(configName);
replaceConfigs(environment,configName,resultMap);
} else if (environment.getPropertySources().contains(CENTER)) {
propertySource = environment.getPropertySources().get(CENTER);
replaceConfigs(environment,CENTER,resultMap);
}else {
// 初始化
propertySource = new PropertySource(PROPERTY_SOURCE_NAME_OA, resultMap) {
@Override
public Object getProperty(String name) {
Map<String, Object> map = (Map<String, Object>) source;
return map.containsKey(name) ? map.get(name) : null;
}
};
// 资源排序优先加载
environment.getPropertySources().addFirst(propertySource);
}
} catch (Exception e) {
e.printStackTrace();
log.error("【配置项处理异常=>】" + e.getMessage());
}
}
/**
* 将类的属性反射递归方法抽取出来,将properties下的写的默认配置类属性读入map中
*
* @param targetObj 需要解析的实体类的实例
* @param fatherClassName 父类的名称,用来拼接层级配置
* @return
*/
private static Map<String, Object> getFieldsFromClass(Object targetObj, String fatherClassName) {
Map<String, Object> resultMap = new HashMap();
Class targetClass = targetObj.getClass();
Field[] fields = targetClass.getDeclaredFields();
Class[] innerClasses = targetClass.getDeclaredClasses();
//循环将类的属性塞入结果集
for (int i = 0; i < fields.length; i++) {
try {
Field field = fields[i];
// 打开私有访问
field.setAccessible(true);
// 获取属性
String name = field.getName();
//处理特殊字符
if (!StringUtils.isEmpty(name) && name.contains("$") ) {
name = name.replace("$$"," ").trim().replace("$", "-");
}
// 获取属性值
Object value = field.get(targetObj);
if (fatherClassName.equals("")) {
resultMap.put(name, value);
} else {
if (!StringUtils.isEmpty(fatherClassName) && fatherClassName.contains("$") ) {
fatherClassName = fatherClassName.replace("$$"," ").trim().replace("$", "-");
}
resultMap.put(fatherClassName + "." + name, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
log.error("【属性反射异常,原因】" + e.getMessage());
}
}
//处理类下的子类
for (Class claszInner : innerClasses) {
log.info("正在处理配置模块【" + claszInner.getSimpleName() + "】");
Map<String, Object> sonclassResultMap = new HashMap();
try {
String fatherconfigname = "";
if (fatherClassName.equals("")) {
fatherconfigname = claszInner.getSimpleName();
} else {
fatherconfigname = fatherClassName + "." + claszInner.getSimpleName();
}
//递归将子类下的属性放入容器
sonclassResultMap = getFieldsFromClass(claszInner.newInstance(), fatherconfigname);
resultMap.putAll(sonclassResultMap);
} catch (Exception e) {
e.printStackTrace();
log.info("【自动配置的Properties中,内部类构造器有问题】");
}
}
return resultMap;
}
private void replaceConfigs(ConfigurableEnvironment environment,String cfgName,Map<String,Object>resultMap) {
PropertySource<?> propertySource = environment.getPropertySources().get(cfgName);
Map<String, Object> source = (Map<String, Object>) propertySource.getSource();
for (String key : resultMap.keySet()) {
// 循环读取到的自定义配置如果环境变量里不存在默认配置则由配置类来接管
if (!source.containsKey(key) || source.get(key) == null || source.get(key).toString().isEmpty()) {
log.info("已将【" + key + "】替换为【" + resultMap.get(key) + "】");
source.put(key, resultMap.get(key));
}
}
}
private String getApplicationFileName(ConfigurableEnvironment environment){
String name = "";
for (PropertySource<?> propertySource : environment.getPropertySources()) {
log.info("【获取到配置文件=>"+ propertySource.getName() +"】");
if(propertySource.getName().indexOf(PROPERTY_SOURCE_NAME_PREFIX) >=0){
name = propertySource.getName();
break;
}
}
return StringUtils.isEmpty(name)?PROPERTY_SOURCE_NAME_OA:name;
}
通过反射读取配置类信息将配置加载到环境中,如果配置文件没有配置相关配置信息则自动加载配置类中的配置项,从而实现配置文件的简化。