最近工作上遇到了一类需求,就是要对接大量外部系统,而需要对接的外部系统又存在各种环境,比如开发,测试,正式环境,他们的配置不尽相同。
一般情况下,我们可以通过配置application-dev.yml、application-test.yml、application-prod.yml等springboot配置文件来配置这些参数。
但是由于我们对接外部系统数量极多,而且不停的在增加,所以如果考虑将外部系统的地址,账户密码配置进yml文件,会导致yml文件爆炸且不太好管控,所以需要思考一个更快捷的办法。
中间我们考虑将这样的配置写进数据库,但是几个环境的数据库都要分别配置,也挺麻烦的。
经过思考,我们想出了一个代码配置的办法,这种办法可以让程序员在代码级别自由发挥,不受制于运维,仅供大家参考。
1.首先增加一个接口,定义若干环境
/** * 代码配置参数神器 * * @author xiaokek * @since 2020年5月13日 下午4:01:47 */ public interface CoreConfig { default Map<String, String> getLocal(){ return Collections.EMPTY_MAP;}; default Map<String, String> getTest(){ return getLocal();}; default Map<String, String> getProd(){ return getLocal();}; }
2.增加一个配置获取器
/** * 各环境配置参数 * * @author xiaokek * @since 2020年5月13日 下午4:02:01 */ public class CoreConfigs { private final Map<String, String> c; public CoreConfigs(Map<String, String> c) { this.c = c; } public String getConfig(String key) { if(!c.containsKey(key)) { throw BizException.error("配置项不存在:"+key); } return c.get(key); } }
3.增加一个自动配置
/** * 基于不同环境的配置项 * * @author xiaokek * @since 2020年2月27日 下午12:35:27 */ @Configuration @ConditionalOnBean(CoreConfig.class) public class EnvAutoConfiguration implements InitializingBean{ @Bean @Profile({ "local", "my", "dev" }) public CoreConfigs localConfig(List<CoreConfig> cs) { Map<String, String> r = new LinkedCaseInsensitiveMap<>(); for(CoreConfig cc : cs) { r.putAll(cc.getLocal()); } return new CoreConfigs(r); } @Bean @Profile({ "test" }) public CoreConfigs testConfig(List<CoreConfig> cs) { Map<String, String> r = new LinkedCaseInsensitiveMap<>(); for(CoreConfig cc : cs) { r.putAll(cc.getTest()); } return new CoreConfigs(r); } @Bean @Profile({ "prod" }) public CoreConfigs prodConfig(List<CoreConfig> cs) { Map<String, String> r = new LinkedCaseInsensitiveMap<>(); for(CoreConfig cc : cs) { r.putAll(cc.getProd()); } return new CoreConfigs(r); } @Autowired(required=false)List<CoreConfig> cs; @Override public void afterPropertiesSet() throws Exception { if(CollectionUtils.isEmpty(cs)) { return; } Set<String> all = null; for(CoreConfig cc : cs) { check(cc); all = check(all, cc.getLocal().keySet(),"local", cc); } all = null; for(CoreConfig cc : cs) { all = check(all, cc.getTest().keySet(),"test", cc); } all = null; for(CoreConfig cc : cs) { all = check(all, cc.getProd().keySet(),"prod", cc); } }
/**
以开发环境为准,防止参数漏配检查
*/ private void check(CoreConfig cc) { int size = (cc.getLocal() == null ? 0: cc.getLocal().size()); if(size != (cc.getSit() == null ? 0: cc.getSit().size())) { throw BizException.error(cc.getClass().getName()+" -test的配置参数缺失, 请检查"); }if(size != (cc.getProd() == null ? 0: cc.getProd().size())) { throw BizException.error(cc.getClass().getName()+" -prod的配置参数缺失, 请检查"); } } private Set<String> check(Set<String> all, Set<String> next, String env, CoreConfig cc) { if(all == null) { all = Sets.newHashSet(); } SetView<String> in = Sets.intersection(all, next); if(in != null && in.size() > 0) { throw BizException.error("发现" +cc.getClass().getName() +" - " +env+"的重复配置键: "+in); } all.addAll(next); return all; } }
4.上面3个是放在一个配置基础jar包,其他微服务小组可以依赖上述jar包,在自己的工程做自己的配置
关键的一点来了,这样的配置类,可以有无限个,适合拆分在各自的包里
@Component public class JobParamConfig implements CoreConfig{ @Override public Map<String, String> getLocal() { Map<String, String> c = Maps.newHashMap(); //YY接口 c.put("xxx.webservice.url", "http://xxxx:9040/xxx.asmx?WSDL"); //XX接口 c.put("350100.url", "http:/xxxx:4321/ylxwjk/"); c.put("350100.username", "1_1"); c.put("350100.password", "2"); return c; } @Override public Map<String, String> getProd() { Map<String, String> c = Maps.newHashMap(); //YY接口 c.put("xxx.webservice.url", "http://220.xx.xx.xx:9040/xxx.asmx?WSDL"); //XX接口 c.put("350100.url", "http://10.xx.xxx.xx:8089/ylxwjk/"); c.put("350100.username", "222"); c.put("350100.password", "222"); return c; } }
5.接下来可以快乐使用参数拉!
@Resource CoreConfigs c; private String initToken() { String body = "{"username":""+c.getConfig("350100.username")+"","password":""+c.getConfig("350100.password")+""}"; String token = null; String json = ""; try { ResponseEntity<String> en = rt.exchange( RequestEntity.post(URI.create(c.getConfig("350100.url")+"/user/login")).contentType(MediaType.APPLICATION_JSON).body(body), String.class); json = en.getBody(); Result<String> r = JsonUtil.fromJson(json, Result.class); if(StringUtils.equals(r.getCode(),"200")) { token = r.getData(); }else { throw BizException.error("身份验证获取token失败!:"+r.getMessage()); } }catch (BizException e) { throw e; }return token; }