先说一下可以使用的场景
项目中有一些功能类使用了@Value
修饰, 这种属性取值通常要么是读取 yml 的配置文件,要么是读取配置中心.
在我们在本地调试的时候Controller时, 如果 如果Service层用到了 @Value
修饰 的属性时, 但是需要根据这个请求去 动态去读取值 , 那么我们可以给这个请求增加一个标识 (如下面案例 请求头中加了个 env ) ,
如果请求头是 env有值,那就让这个请求去调用 uat 环境的接口 (这个可以自己配置) , 如果没值, 那就让它读取 读取 yml 的配置文件,要么或者配置中心的接口
这样,就能比较方便的,相当于不用重新运行项目也能动态修改 配置 (比较通常运行项目时,就会先指定 当前是什么环境, 然后读取什么配置 , 比如 dev 读取 dev.yml
, test 读取 test.yml
, 但这样配置好后,就不用这么麻烦重启项目了 )
当然,您还可以在此基础上进行优化,因为这里仅仅方便调试而已,所以就先这样咯
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.*;
@Component
@Configuration
public class EnvironmentPropertiesProcessor implements BeanPostProcessor , HandlerInterceptor, ApplicationContextAware, WebMvcConfigurer {
private final Map<String, Map<String, Object>> envPropertiesMap = new HashMap<>();
private final List<String> beanNameList = new ArrayList<>();
private final Map<String, Object> initPropertiesMap = new HashMap<>();//配置文件的值
//调试配置
private final static Map<String, Object> uatValueMap = new HashMap<String, Object>(){{
//todo 把要替换的直接粘贴到这就行了
put("${http-request.conn-timeout:5000}","10000");
put("${nebula.secret.key}","1234567812345678");
put("${fund-trade.host}","sss-uat.xxx.online"););
}};
private static final String ENV_KEY = "env";
private static ApplicationContext context;
@Override
public void addInterceptors(InterceptorRegistry registry) {
EnvironmentPropertiesProcessor bean = context.getBean(EnvironmentPropertiesProcessor.class);
System.out.println("读取本地项目用@Value的配置文件:" + context.getBean(EnvironmentPropertiesProcessor.class).initPropertiesMap);
System.out.println("转换后");
for (Map.Entry<String, Object> entry : context.getBean(EnvironmentPropertiesProcessor.class).initPropertiesMap.entrySet()) {
try {
// 这里打印出来方便复制放到 上面粘贴 , put("xxx","xxx")
String value = entry.getValue().toString();
System.out.println("put(" + "\"" + entry.getKey() + "\"," + "\"" + value.replaceAll("dev|test","uat") + "\"" + ");");
}catch (Exception e){
//System.out.println( "\"" + entry.getValue() + "\"" + entry.getValue());
}
}
System.out.println();
registry.addInterceptor(bean);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 扫描bean中带有@Value注解的属性,并将其加入到envPropertiesMap中
Class<?> clazz = bean.getClass();
Map<String, Object> propertiesMap = new HashMap<>();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
Value valueAnnotation = AnnotationUtils.getAnnotation(field, Value.class);
if (valueAnnotation != null) {
String key = valueAnnotation.value();
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, bean);
propertiesMap.put(key, value);
initPropertiesMap.put(key, value);
}
}
if (!propertiesMap.isEmpty()) {
envPropertiesMap.put(beanName, propertiesMap);
beanNameList.add(beanName);
}
return bean;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String env = request.getHeader(ENV_KEY);
if (env != null && !env.isEmpty()) {
for (String name : beanNameList) {
Object bean = context.getBean(name);
try {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
// 找到需要设置的属性,并将其值注入
if (field.isAnnotationPresent(Value.class)) {
Value valueAnnotation = field.getAnnotation(Value.class);
String expression = valueAnnotation.value();
field.setAccessible(true);
Object uatValue = uatValueMap.get(expression);
if (!StringUtils.isEmpty(uatValue)) {
//todo 这里还可以自己完善,但一般来说大多配置都是字符串
if (field.getType().getName().equals("java.lang.String")) {
field.set(bean, uatValue);
}
//...
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求结束后,还原
String env = request.getHeader(ENV_KEY);
if (env != null && !env.isEmpty()) {
for (String name : beanNameList) {
Object bean = context.getBean(name);
try {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
// 找到需要设置的属性,并将其值注入
if (field.isAnnotationPresent(Value.class)) {
Value valueAnnotation = field.getAnnotation(Value.class);
String expression = valueAnnotation.value();
field.setAccessible(true);
Object str = initPropertiesMap.get(expression);
if (!StringUtils.isEmpty(str)) {
field.set(bean, str);
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}