Java业务系统配置管理设计方案

  • 数据库配合项集合管理实现
    • Properties配置工具类
    • 配置集合方案DDL
    • Entity类
    • DAO类
    • 动态配置集合工具类
    • 测试
    • 后记

代码和配置都是程序的一部分,而一个完整、灵活的系统架构对于配置的管理是不可或缺的。根据程序实现业务需求的过程中不可避免的要使用一些可调换的配置项来控制代码在特定场景下的不同反应的状态,在不同的团队、不同的项目中大家用到的配置管理方案各有不同。

管理配置的技术大体有properties、spring xml配置文件配置,还有用于微服务的spring cloud
config技术等等。Java生态圈大家很多的配置方案都是建立在以上技术上的。

把系统架构中的配置可分为两类:支持系统运行的基础配置,这些配置往往不会经常变动调整,至少不会在系统“运行”中更改,尽量不让开发人员修改的配置。比如:系统缓存配置,事务、一些基础支撑的中间件的配置。还有一类是业务配置这是比基础配置更高一级的配置项集合:这些配置项的特点是:跟业务流程代码相关性高但是又不合适做在特定的功能模块中的配置。比如说一个抽奖的功能,在用户获奖之后需要通知另一个在远程系统的方法(向指定URL发起请求),在开发、测试、生产各个过程的环境下调用的URL不一样,这样的情况下可以将此种配置定义为,业务配置。
这些配置放到properties或者spring xml配置文件中比较合适。 这种配置尽量要与代码离得近一些,结合的紧密一些。

对于一个要用于项目中业务配置的管理我认为至少要满足一些要求:集中化、动态、灵活。要与代码隔离。

基础配置集合和业务配置集合要分开管理。因为大家在将基础配置与代码强关联之后,修改都需要重启生效,业务的配置要避免这样,减少开发调试的时间。

由以上需求做一个以数据库存放配置的实现方案。

把配置项放到数据库里可以避免修改配置重启程序才能生效的弊端,但是每次读取数据库中间消耗的时间也是很可观的,所以我们将配置集合放到一个Java集合中,每次应用配置项从程序内存读取减少对数据库的访问来提高效率,而在修改了某个配置项中我们需要动态的重载配置项,同时为了防止线程间的同步和安全问题,我们需要使用单例工厂模式实现管理一个线程间安全的集合。这个时候对于我们的这个工厂类必要的几个功能接口有,载入配置、数据库配置更改之后激发重载配置、读取单个配置项,读取所以配置项,当然,为了过于频繁(不正确的使用配置集合)的重载配置列表我们还要一个重载配置集合的事件间隔机制。

有了具体的需求剩下的就是实现了。

数据库配合项集合管理实现

依赖
Java 1.8 +
Spring 5
Lombok
Mybatis / Mybatis Plus / JPA

项目代码仓库 - >

Properties配置工具类

import java.util.Properties;
// Properties工具接口
public interface PropertiesUtil {public Properties getProperties();public String getProperty(String key);public Object setProperty(String key, String value);public Long getPropertyByLong(String key);
}
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;/*** Properties配置工具类*/
@Slf4j
public class ApplicationPropertiesUtils implements PropertiesUtil {private Properties properties;/*** 指定配置文件*/private static final String APPLICATIONCONFIGURATIONPROPERTIESFILENAME = "application.properties";public ApplicationPropertiesUtils() {InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(APPLICATIONCONFIGURATIONPROPERTIESFILENAME);properties = new Properties();try {properties.load(inputStream);} catch (IOException e) {e.printStackTrace();}}public ApplicationPropertiesUtils(String propertyPath) {InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(propertyPath);properties = new Properties();try {properties.load(inputStream);} catch (IOException e) {e.printStackTrace();}}@Overridepublic Properties getProperties() {InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(APPLICATIONCONFIGURATIONPROPERTIESFILENAME);properties = new Properties();try {properties.load(inputStream);} catch (IOException e) {e.printStackTrace();}return properties;}/*** 读取配置项 - 字符串* @param key* @return*/@Overridepublic String getProperty(String key) {return this.properties.getProperty(key);}@Overridepublic Object setProperty(String key, String value) {return this.properties.setProperty(key, value);}/*** 读取配置 - 长整形* @param key* @return*/@Overridepublic Long getPropertyByLong(String key) {String temp = this.properties.getProperty(key);for (int i = 0; i < temp.length()-1; i++) {if ( ! Character.isDigit(temp.charAt(i))) {log.error("getPropertyByLong 配置项的value出现非数字:"+key);return null;}}return Long.parseLong(temp);}
}

配置集合方案DDL

Mysql

create table if not exists applicationconfiguration
(updateTimeAC datetime not null comment '配置更新时间',keyAC varchar(128) charset utf8 not null comment '配置项键',valueAC varchar(256) charset utf8 not null comment '配置项值',authorAC varchar(16) charset utf8 not null comment '更新/创建人',descriptionAC varchar(256) charset utf8 not null comment '配置说明',constraint ApplicationConfiguration_keyAC_uindexunique (keyAC)
)
comment '程序配置表' charset=utf8mb4;alter table applicationconfigurationadd primary key (keyAC);

Entity类

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.JdbcType;import javax.persistence.Entity;
import java.io.Serializable;
import java.util.Date;@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@TableName("applicationconfiguration")
public class ApplicationConfiguration implements Serializable {@TableId(value = "keyAC" )String keyAC;@TableField(value = "valueAC" ,jdbcType = JdbcType.NVARCHAR)String valueAC;@TableField(value = "AuthorAC")String authorAC;@TableField(value = "descriptionAC")String descriptionAC;@TableField(value = "updateTimeAC")Date updateTimeAC;
}

DAO类

@Repository
public interface ApplicationConfigurationDao extends BaseMapper<ApplicationConfiguration> {List<ApplicationConfiguration> findAll();
}

因为是MyBatis实现要加一个Mapper配置。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--    映射的DAO接口    --><mapper namespace="cn.Dao.ApplicationConfigurationDao"><!--    查询所有记录-按name字段降序排序  --><select id="findAll"resultType="cn.Entity.ApplicationConfiguration" useCache="true">select *from applicationconfiguration</select></mapper>

动态配置集合工具类


/*** 应用程序(项目)需要使用的配置单例工厂类* add by 2019年12月8日 星期日*/
@Slf4j
@Component
public class ApplicationConfigurationSingletonFactory {//  配置DAO对象@Autowiredprivate static ApplicationConfigurationDao applicationConfigurationDao;//    配置集合(表)private volatile static Hashtable hashtable = null;//    最后一次载入配置时间private volatile static Long lastLoadApplicationConfigurationTime ;private volatile static ApplicationPropertiesUtils applicationPropertiesUtils = new ApplicationPropertiesUtils();//    重载应用配置间隔时间private volatile static Long ReloadApplicationConfigurationIntervalTime = 0L;//    单例模式,将构造函数私有化private ApplicationConfigurationSingletonFactory() {}//    静态代码块static {lastLoadApplicationConfigurationTime = 0L;ReloadApplicationConfigurationIntervalTime = applicationPropertiesUtils.getPropertyByLong("ReloadApplicationConfigurationIntervalTime");}//    获取配置集合实例public synchronized static Map getInstance(){if (lastLoadApplicationConfigurationTime + ReloadApplicationConfigurationIntervalTime*1000 > System.currentTimeMillis()) {log.warn("调用载入applicationConfiguration配置过于频繁,已超过设置间隔时间:"+ReloadApplicationConfigurationIntervalTime + "currentTimeMillis:"+System.currentTimeMillis());throw new RuntimeException("调用载入applicationConfiguration配置过于频繁");}if (hashtable != null) {lastLoadApplicationConfigurationTime = System.currentTimeMillis();return hashtable;}else{if (applicationConfigurationDao != null) {List<ApplicationConfiguration> applicationConfigurationList = applicationConfigurationDao.selectList(null);hashtable = new Hashtable();applicationConfigurationList.forEach(i->{hashtable.put(i.getKeyAC(), i.getValueAC());});}else{WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();Object obj = wac.getBean(ApplicationConfigurationDao.class);if (obj != null) {applicationConfigurationDao = (ApplicationConfigurationDao) obj;List<ApplicationConfiguration> applicationConfigurationList = applicationConfigurationDao.selectList(null);hashtable = new Hashtable();applicationConfigurationList.forEach(i->{hashtable.put(i.getKeyAC(), i.getValueAC());});}else{throw new RuntimeException("初始化异常:没有拿到实例化的applicationConfigurationDao对象" + ApplicationConfigurationSingletonFactory.class.getCanonicalName());}}}lastLoadApplicationConfigurationTime = System.currentTimeMillis();return hashtable;}//    重载配置集合public synchronized static Map reloadApplicationConfiguration(){if (lastLoadApplicationConfigurationTime + ReloadApplicationConfigurationIntervalTime*1000 > System.currentTimeMillis()) {log.warn("调用载入applicationConfiguration配置过于频繁,已超过设置间隔时间:"+ReloadApplicationConfigurationIntervalTime + "currentTimeMillis:"+System.currentTimeMillis());throw new RuntimeException("调用载入applicationConfiguration配置过于频繁");}if (hashtable != null) {hashtable.clear();if (applicationConfigurationDao != null) {List<ApplicationConfiguration> applicationConfigurationList = applicationConfigurationDao.selectList(null);hashtable = new Hashtable();applicationConfigurationList.forEach(i->{hashtable.put(i.getKeyAC(), i.getValueAC());});}else{WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();Object obj = wac.getBean(ApplicationConfigurationDao.class);if (obj != null) {applicationConfigurationDao = (ApplicationConfigurationDao) obj;List<ApplicationConfiguration> applicationConfigurationList = applicationConfigurationDao.selectList(null);hashtable = new Hashtable();applicationConfigurationList.forEach(i->{hashtable.put(i.getKeyAC(), i.getValueAC());});}else{throw new RuntimeException("初始化异常:没有拿到实例化的applicationConfigurationDao对象" + ApplicationConfigurationSingletonFactory.class.getCanonicalName());}}}else{log.warn("在重新载入应用程序配置之前没有使用getInstance():不推荐这样用。");log.warn("尝试调用getInstance载入配置。。。");Map result = ApplicationConfigurationSingletonFactory.getInstance();lastLoadApplicationConfigurationTime = System.currentTimeMillis();return result;}lastLoadApplicationConfigurationTime = System.currentTimeMillis();return hashtable;}//    读取配置项(从单例的配置集合中) - 字符串public static String getValueOfConfiguration(String key) {Object temp = hashtable.get(key);if (temp != null) {return temp.toString();}else{log.error("没有找到配置项:"+key);}return null;}}

测试

1、新建基础配置文件

这里我们使用application.properties

# 应用框架配置# 重载一次applicationConfiguration的时间间隔 - 单位 / 秒
# add by 2019年12月8日 星期日
ReloadApplicationConfigurationIntervalTime=30

2、执行准备好的DDL建造表结构并插入一些测试数据

3、启动程序

测试Controller类,为了方便测试我们来写个Controller类

    private String resultStr = new String();/*** 获取配置集合* @return*/@RequestMapping("/getconfig")@ResponseBodypublic String getConfigProc(){resultStr = "";Map<String,String> list = ApplicationConfigurationSingletonFactory.getInstance();list.forEach( (k,v)->{resultStr +=v;});return resultStr;}/*** 重载配置集合* @return*/@RequestMapping("/reloadconfig")@ResponseBodypublic String getConfigProc2(){resultStr = "";Map<String,String> list = ApplicationConfigurationSingletonFactory.reloadApplicationConfiguration();list.forEach( (k,v)->{resultStr +=v;});return resultStr;}/*** 读取指定配置项 - restful api* @param key* @return*/@RequestMapping("/getOneConfig/{key}")@ResponseBodypublic String getOneConfig(@PathVariable String key) {String value = ApplicationConfigurationSingletonFactory.getValueOfConfiguration(key);return value;}

读取单个配置(此时还没有加载配置集合)

初始化配置集合(第一次读取配置到内存)

然后再次读取指定配置

修改一个配置

第三次读取配置(因为没有重载配置集合,所以在内存中的配置集合还是修改前的)

重载配置集合(此操作会刷新内存中的配置集合)

第四次读取指定配置项(内存中的配置已经被刷新)

可以看到已经把修改过的配置加载过来了

因为我们设定了加载配置集合的间隔是30秒,如果在30秒内再次加载配置就会

这样可以避免不规范的配置集合操作。

后记

以上实现给提供了一个,动态的,数据库集中管理配置的基础实现。如果大家有客制化需求可以方便的在此上实现,比如在spring mvc实例化bean时自动加载一次配置集合,后台任务定时刷新配置项等等功能。
程序启动运行代码,自动任务可以参考博主的其它文章哦:
Web应用启动自动带起代码的操作 ->

quartz任务框架基本原理及使用->

Java业务系统配置管理设计方案实现相关推荐

  1. Java业务系统是怎么和MySQL交互的?

    很多crud boy眼中的数据库: 但使用MySQL时,总会遇到各种烦人问题,什么偶尔死锁.性能丢人.各种异常报错.一般人都会Google博客,尝试解决问题,最后虽然是解决了问题,但可能也没搞懂背后原 ...

  2. 不同业务场景Cas客户端(Java业务系统)接入

    概述 场景一.网站部分文章需要Cas认证(登录平台)通过后才能访问 1.通过拦截器,把文章访问路径进行拦截,判断当前文章是否需要cas认证 2.如果需要认证,那么调用CAS服务端进行认证,成功后回调地 ...

  3. JAVA oa 系统模块设计方案

    1.模型管理 :web在线流程设计器.预览流程xml.导出xml.部署流程2.流程管理 :导入导出流程资源文件.查看流程图.根据流程实例反射出流程模型.激活挂起 .自由跳转3.运行中流程:查看流程信息 ...

  4. java 军工_为什么军工行业不用java而是选择继续用c(对于业务系统Java是非常合适的而不带操作系统的板子甚至可以做到微秒级别的实时控制)...

    军工,即军事工业,是工业领域的一个分支.工业控制,必然是C/C++的主战场.工业控制,讲究的是极高的稳定性,稳定压倒一切.同时,还讲究实时性.在控制过程中,一旦出现不稳定或大的延时,后果可能就是致命的 ...

  5. java计算机毕业设计信用卡增值业务系统小程序用户端源码+mysql数据库+lw文档+系统+调试部署

    java计算机毕业设计信用卡增值业务系统小程序用户端源码+mysql数据库+lw文档+系统+调试部署 java计算机毕业设计信用卡增值业务系统小程序用户端源码+mysql数据库+lw文档+系统+调试部 ...

  6. 银行客户业务系统(JAVA,JSP,SQLSERVER)

    银行客户业务系统(JAVA,JSP,SQLSERVER) 银行客户业务系统(JAVA,JSP,SQLSERVER)(毕业论文13620字以上,共23页,程序代码,MySQL数据库) [运行环境]  M ...

  7. 计算机毕业设计Java信用卡增值业务系统小程序管理端(源码+系统+mysql数据库+lw文档

    计算机毕业设计Java信用卡增值业务系统小程序管理端(源码+系统+mysql数据库+lw文档 计算机毕业设计Java信用卡增值业务系统小程序管理端(源码+系统+mysql数据库+lw文档) 本源码技术 ...

  8. java毕业生设计信用卡增值业务系统小程序用户端计算机源码+系统+mysql+调试部署+lw

    java毕业生设计信用卡增值业务系统小程序用户端计算机源码+系统+mysql+调试部署+lw java毕业生设计信用卡增值业务系统小程序用户端计算机源码+系统+mysql+调试部署+lw 本源码技术栈 ...

  9. java计算机毕业设计校园代办业务系统源码+数据库+系统+lw文档+mybatis+运行部署

    java计算机毕业设计校园代办业务系统源码+数据库+系统+lw文档+mybatis+运行部署 java计算机毕业设计校园代办业务系统源码+数据库+系统+lw文档+mybatis+运行部署 开发软件:i ...

最新文章

  1. linu逻辑分区动态调整大小
  2. 怎么在html中加判断,css样式里面如何做判断
  3. 韩国讨论到 2020 年拥抱开源操作系统
  4. 国二c语言操作题评分标准,全国计算机二级C语言操作题题库.doc
  5. mysql galera 安装_MySQL Galera 集群的安装过程
  6. 开场 Live,分享点干货——「深入了解 Node.js 包与模块机制」
  7. 多项式乘法:练习总结
  8. 设计模式(八)组合模式 Composite
  9. 成为最差前端开发的10个建议,唉!传递负能量了
  10. SQLi LABS Less-12 联合注入+报错注入
  11. DBA和开发同事的一些代沟(一)
  12. vista中如何解决金山词霸延时问题
  13. Jsoup 爬虫之百度贴吧
  14. win10 系统把装在c盘的软件移到其他盘的方法
  15. 用以太坊区块链和jwt token保证Asp.Net Core的API交互安全(上)
  16. vga转HDMI与hdmi转VGA区别
  17. mysql的ace什么概念_ACE(03):努力了,总会有收获
  18. mysql中的/、div的区别
  19. Linux之V4L2驱动框架
  20. JAVA从网络下载文件到本地

热门文章

  1. 【r语言plot报错】Error in plot.window(...) : ‘xlim‘值不能是无限的/ need finite ‘xlim’ values
  2. 做人呢,一定要谨记下面两个原则
  3. c++类成员函数中调用多线程函数_beginthreadex()
  4. 语法分析:自上而下分析(递归下降分析法+预测分析法)
  5. 方便好用计算机,电脑系统哪个最好用 电脑系统对比介绍【图文】
  6. android nfc驱动,移植NFC驱动到android系统
  7. springboot对条件接口Condition的扩展和使用----1
  8. ClickHouse的入门、使用和优化
  9. Apple PUSH Notication Service (APNS) 配置攻略
  10. 中断服务子程序c语言格式,中断服务子程序是如何被执行的 ?