SpringBoot动态更新外部属性文件

2020-04-18  本文已影响0人  飘逸峰

摘要

思路

SpringCloud可以通过config组件实现配置的动态加载,我们也可以将数据存在数据库或者缓存中,可是如果只是一个小项目,不想依赖任何中间件,那么就可以通过如下的方式实现。

代码

这个类要注册到spring上下文,并在需要的地方调用该对象的refresh方法即可重新加载所有外部属性文件。

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * <p>动态加载外部属性处理类</p>
 */
@Slf4j
@Component
public class ExternalPropertiesRefresh {

    @Autowired
    private ConfigurableListableBeanFactory configurableListableBeanFactory;

    /**
     * <p>根据属性名获取属性值</p>
     *
     * @param fieldName bean的属性名称
     * @param object    bean对象
     * @return java.lang.Object get方法返回值
     * @author hanqf
     */
    private Object getFieldValueByName(String fieldName, Object object) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = object.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(object, new Object[]{});
            return value;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }


    /**
     * <p>根据属性名设置属性值</p>
     *
     * @param fieldName  bean的属性名称
     * @param object     bean对象
     * @param paramTypes set方法参数类型
     * @param params     set方法参数值
     * @author hanqf
     */
    private void setFieldValueByName(String fieldName, Object object, Class[] paramTypes, Object[] params) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String setter = "set" + firstLetter + fieldName.substring(1);
            Method method = object.getClass().getMethod(setter, paramTypes);
            method.invoke(object, params);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    /**
     * <p>获取属性名称,去除前缀</p>
     *
     * @param key   属性key
     * @param prefix 属性key前缀
     * @return java.lang.String
     * @author hanqf
     */
    private String fieldName(String key, String prefix) {
        if (StringUtils.hasText(prefix)) {
            return key.replace(prefix + ".", "");
        }
        return key;
    }

    /**
     * <p>将属性文件值绑定到bean对象</p>
     *
     * @param bean
     * @param properties
     * @param prefix
     * @author hanqf
     */
    private Object bind(Object bean, Properties[] properties, String prefix) {
        String fieldName = "";//属性名称
        String pValue = "";//属性值
        String[] sp = null; //map属性分割key和value
        for (Properties pro : properties) {
            Map<String, Map<String, String>> fidleMap = new HashMap<>();
            Map<String, Set<String>> fidleSet = new HashMap<>();
            Map<String, List<String>> fidleList = new HashMap<>();
            //遍历属性
            for (Object key : pro.keySet()) {
                pValue = (String) (pro.get(key));
                fieldName = fieldName((String) key, prefix);

                //map
                sp = fieldName.split("\\.");
                if (sp.length == 2) {
                    fieldName = sp[0];
                }

                //list&&set
                if (fieldName.indexOf("[") > 0) {
                    fieldName = fieldName.substring(0, fieldName.indexOf("["));
                }

                //属性类型
                Object object = getFieldValueByName(fieldName, bean);

                //类型匹配
                if (object instanceof Map) {
                    if (fidleMap.get(fieldName) != null) {
                        object = fidleMap.get(fieldName);
                    } else {
                        object = new HashMap<String, String>();
                    }
                    if (sp.length == 2) {
                        ((Map) object).put(sp[1], pValue);
                        fidleMap.put(fieldName, (Map<String, String>) object);
                    }
                } else if (object instanceof Set) {
                    if (fidleSet.get(fieldName) != null) {
                        object = fidleSet.get(fieldName);
                    } else {
                        object = new HashSet<String>();
                    }
                    ((Set) object).add(pValue);
                    fidleSet.put(fieldName, (Set<String>) object);
                } else if (object instanceof List) {
                    if (fidleList.get(fieldName) != null) {
                        object = fidleList.get(fieldName);
                    } else {
                        object = new ArrayList<String>();
                    }
                    ((List) object).add(pValue);
                    fidleList.put(fieldName, (List<String>) object);
                } else if (object instanceof String) {
                    setFieldValueByName(fieldName, bean, new Class[]{String.class}, new Object[]{pValue});
                } else if (object instanceof Integer) {
                    setFieldValueByName(fieldName, bean, new Class[]{Integer.class}, new Object[]{Integer.valueOf(pValue)});
                } else if (object instanceof Long) {
                    setFieldValueByName(fieldName, bean, new Class[]{Long.class}, new Object[]{Long.valueOf(pValue)});
                } else if (object instanceof Double) {
                    setFieldValueByName(fieldName, bean, new Class[]{Double.class}, new Object[]{Double.valueOf(pValue)});
                } else if (object instanceof Float) {
                    setFieldValueByName(fieldName, bean, new Class[]{Float.class}, new Object[]{Float.valueOf(pValue)});
                }
            }

            //map类型赋值
            if (fidleMap.size() > 0) {
                for (String fname : fidleMap.keySet()) {
                    setFieldValueByName(fname, bean, new Class[]{Map.class}, new Object[]{fidleMap.get(fname)});
                }
            }

            //set类型赋值
            if (fidleSet.size() > 0) {
                for (String fname : fidleSet.keySet()) {
                    setFieldValueByName(fname, bean, new Class[]{Set.class}, new Object[]{fidleSet.get(fname)});
                }
            }

            //list类型赋值
            if (fidleList.size() > 0) {
                for (String fname : fidleList.keySet()) {
                    setFieldValueByName(fname, bean, new Class[]{List.class}, new Object[]{fidleList.get(fname)});
                }
            }

        }

        return bean;
    }


    /**
     * <p>刷新指定属性类</p>
     * @author hanqf
     * @param beanName bean的注册名称,默认类名称首字母小写
     */
    @SneakyThrows
    public void refresh(String beanName){
        Class<?> cls = configurableListableBeanFactory.getType(beanName);
        Object bean = configurableListableBeanFactory.getBean(cls);
        Properties[] propertiesArray = null;
        String prefix = "";
        if (cls.getAnnotations() != null && cls.getAnnotations().length > 0) {
            for (Annotation annotation : cls.getAnnotations()) {

                if (annotation instanceof PropertySource) {
                    PropertySource propertySource = (PropertySource) annotation;
                    String[] values = propertySource.value();
                    if (values.length > 0) {
                        propertiesArray = new Properties[values.length];
                        for (int i = 0; i < values.length; i++) {
                            //如果引用的是外部文件,则重新加载
                            if (values[i].startsWith("file:")) {
                                String path = values[i].replace("file:", "");
                                Properties properties = PropertiesLoaderUtils.loadProperties(new FileSystemResource(path));
                                propertiesArray[i] = properties;
                            }
                        }
                    }
                }

                if (annotation instanceof ConfigurationProperties) {
                    ConfigurationProperties configurationProperties = (ConfigurationProperties) annotation;
                    prefix = configurationProperties.prefix();
                }

            }
        }

        if (propertiesArray != null && propertiesArray.length > 0) {
            //将属性绑定到对象
            bind(bean, propertiesArray, prefix);

        }
    }


    /**
     * <p>重新加载属性文件</p>
     *
     * @author hanqf
     */
    @SneakyThrows
    public void refresh() {
        String[] ary = configurableListableBeanFactory.getBeanNamesForAnnotation(PropertySource.class);
        if (ary != null && ary.length > 0) {
            for (String beanName : ary) {
                //通过Spring的beanName获取bean的类型
                refresh(beanName);
            }
        }
    }
}

下面通过http请求刷新配置文件

启动服务器后,任意修改属性文件的值,然后请求/refresh,即可重新加载全部属性文件,然后请求/demo查看是否生效,也可以请求/propertiesDemo/refresh,指定要刷新的对象。

@RestController
public class DemoController {
    @Autowired
    private ExternalPropertiesRefresh externalPropertiesRefresh;

    @Autowired
    private PropertiesDemo propertiesDemo;


    @RequestMapping("/refresh")
    public String refreshpro() {
        externalPropertiesRefresh.refresh();
        return "refresh properties success";
    }

    @RequestMapping("/{beanName}/refresh")
    public String refreshProByBeanName(@PathVariable String beanName) {
        externalPropertiesRefresh.refresh(beanName);
        return "refresh properties success for " + beanName;
    }

    @RequestMapping("/demo")
    public PropertiesDemo demo() {
        return propertiesDemo;
    }

}

PropertiesDemo.java

@Component
@PropertySource(value = "file:demo.properties",encoding = "utf-8")
@ConfigurationProperties(prefix = "demo.data")
@Data
public class PropertiesDemo {
    private Map<String, String> map = new HashMap<>();
    private Map<String, String> map2 = new HashMap<>();
    private Set<String> set = new HashSet<>();
    private Set<String> set2 = new HashSet<>();
    private List<String> list = new ArrayList<>();
    private List<String> list2 = new ArrayList<>();

    private String name;
    private Integer age;
    private Double salary;
}

demo.properties

demo.data.map.client=client
demo.data.map.token=token

demo.data.map2.client=client
demo.data.map2.token=token

demo.data.name=张三
demo.data.age=20
demo.data.salary=12345.67

demo.data.set[0]=beijing
demo.data.set[1]=shanghai
demo.data.set[2]=tianjin

demo.data.set2[0]=guangzhou
demo.data.set2[1]=shenzheng
demo.data.set2[2]=hangzhou


demo.data.list[0]=南极
demo.data.list[1]=北极
demo.data.list[2]=赤道

demo.data.list2[0]=喜马拉雅山
demo.data.list2[1]=噶麦斯山
demo.data.list2[2]=阿尔卑斯山
上一篇下一篇

猜你喜欢

热点阅读