zoukankan      html  css  js  c++  java
  • 使用系统参数表,提升系统的灵活性

    1、使用系统参数表的好处

    ​ Spring Boot项目中常有一些相对稳定的参数设置项,其作用范围是系统级的或模块级的,这些参数称为系统参数。这些变量以参数形式进行配置,从而提高变动和扩展的灵活性,保持代码的稳定性。

    ​ 以数据库表形式存储的系统参数表比配置文件(.properties文件或.yaml文件)要更灵活,因为无需重启系统就可以动态更新。

    ​ 系统参数表可用于存储下列数据:

    • 表字段枚举值,如下列字段:
    `question_type`   TINYINT(4)   NOT NULL DEFAULT 0 COMMENT '题型,1-单选题,2-多选题,3-问答题',
    

    ​ 这个字段现在有3种取值,但是难保将来有扩展的可能,如:是非题、计算题、应用题等。

    ​ 因此将取值的枚举值用系统参数表来配置,可以提高系统扩展灵活性。

    ​ 另一方面,对于前端而言,就可以通过查询系统参数表数据,用于UI呈现,而不必硬编码。如前端需要用下拉框来显示所有可能的”题型“,这个列表就可以查询系统参数表来获取。

    ​ 因此可以将所有字段枚举值纳入系统参数表管理。

    • 参数设置,如邮件参数,对接的第三方系统的URL等。

    2、系统参数表的表结构

    ​ 系统参数表的表结构如下:

    DROP TABLE IF EXISTS `sys_parameters`;
    CREATE TABLE `sys_parameters`
    (
      `class_id`      INT(11)      NOT NULL DEFAULT 0 COMMENT '参数大类id',
      `class_key`     VARCHAR(60)  NOT NULL DEFAULT '' COMMENT '参数大类key',
      `class_name`    VARCHAR(60)  NOT NULL DEFAULT '' COMMENT '参数大类名称',
      `item_id`       INT(11)      NOT NULL DEFAULT 0 COMMENT '参数大类下子项id',
      `item_key`      VARCHAR(200) NOT NULL DEFAULT '' COMMENT '子项key',
      `item_value`    VARCHAR(200) NOT NULL DEFAULT '' COMMENT '子项值',
      `item_desc`     VARCHAR(512) NOT NULL DEFAULT '' COMMENT '子项描述',
    
      -- 记录操作信息
      `login_name` VARCHAR(80)  NOT NULL DEFAULT '' COMMENT '操作人账号',
      `delete_flag`   TINYINT(4)   NOT NULL DEFAULT 0 COMMENT '记录删除标记,1-已删除',
      `create_time`   DATETIME  NOT NULL DEFAULT NOW() COMMENT '创建时间',
      `update_time`   DATETIME           DEFAULT NULL ON UPDATE NOW() COMMENT '更新时间',
      PRIMARY KEY (`class_id`, `item_id`)
    ) ENGINE = InnoDB DEFAULT CHARSET = utf8 COMMENT '系统参数表';
    

    ​ 说明:

    ​ class_id字段只要确保一个参数大类(如一个枚举字段名)使用唯一值。使用class_key和item_key自动,便于提高记录数据和代码的可读性。class_key一般可以取字段名,但如果发生同名时,需要修改,确保不同表的同名字段,使用不同的class_key。对于枚举值类型,item_key可以取item_id相同的值,只是数据类型不同,此item_key转换成整型数,就是对应字段的值。

    ​ 这个表的数据一般可以由开发人员提供,包括初始或变动的SQL脚本,由DBA执行,项目无需为此开发界面来维护。

    ​ 下面是初始脚本示例:

    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (11, 'receive_flag', '短信接收标志', 0, '0', '未接收', '');
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (11, 'receive_flag', '短信接收标志', 1, '1', '已接收', '');
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (11, 'receive_flag', '短信接收标志', 2, '2', '发送失败', '');
    
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (12, 'question_type', '题型', 1, '1', '单选题', '');
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (12, 'question_type', '题型', 2, '2', '多选题', '');
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (12, 'question_type', '题型', 3, '3', '问答题', '');
    
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (101, 'url_param', 'URL参数', 0, 'url_prefix', 'http://questinvest.abc.com:8880', 'url前缀部分');
    INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
    VALUES (101, 'url_param', 'URL参数', 1, 'url_action', '/questInvest/show', '请求接口方法');
    

    3、系统参数表在项目中的使用

    ​ 在Spring Boot项目中,系统参数表一般只需在应用启动时加载一次,并提供更新接口允许管理员来更新数据。下面详细说明使用方法。

    3.1、Entity类

    ​ 先定义系统参数表的实体类,实体类为SysParameter,代码如下:

    package com.abc.questInvest.entity;
    
    import javax.persistence.Column;
    
    import lombok.Data;
    
    /**
     * @className	: SysParameter
     * @description	: 系统参数信息对象类
     *
     */
    @Data
    public class SysParameter {
    	//参数大类id
    	@Column(name = "class_id")
    	private Integer classId;
    	
    	//参数大类key
    	@Column(name = "class_key")
    	private String classKey;
    
    	//参数大类名称
    	@Column(name = "class_name")
    	private String className;
    	
    	//子项id
    	@Column(name = "item_id")
    	private Integer itemId;	
    		
    	//子项key
    	@Column(name = "item_key")
    	private String itemKey;	
    	
    	//子项值
    	@Column(name = "item_value")
    	private String itemValue;	
    
    	//子项描述
    	@Column(name = "item_desc")
    	private String itemDesc;	
    
    	//========记录操作信息================
        // 操作人姓名
        @Column(name = "login_name")
        private String loginName;   
        
        // 记录删除标记,保留
        @Column(name = "delete_flag")
        private Byte deleteFlag;    
    
        // 创建时间
        @Column(name = "create_time")
        private Date createTime;
    
        // 更新时间
        @Column(name = "update_time")
        private Date updateTime;	
    }
    

    3.2、Dao类

    ​ 数据访问类为SysParameterDao,代码如下:

    package com.abc.questInvest.dao;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    
    import com.abc.questInvest.entity.SysParameter;
    
    /**
     * @className	: SysParameterDao
     * @description	: sys_parameters表数据访问类
     *
     */
    @Mapper
    public interface SysParameterDao {
    
    	//查询所有系统参数,按class_id,item_id排序
    	@Select("SELECT class_id,class_key,class_name,item_id,item_key,item_value,item_desc"
    			+ " FROM sys_parameters WHERE delete_flag = 0" 
    			+ " ORDER BY class_id,item_id")
        List<SysParameter> selectAll();
    }
    

    ​ SysParameterDao类,使用Mybatis,只需提供查询接口就行了,因为修改在数据库后台执行了。当然如果项目方认为有必要提供界面来维护该表,则可增加相应CRUD的接口。

    3.3、Service类

    ​ 服务接口类为SysParameterService,代码如下:

    package com.abc.questInvest.service;
    
    import java.util.List;
    
    import com.abc.questInvest.entity.SysParameter;
    
    /**
     * @className	: SysParameterService
     * @description	: 系统参数数据服务
     *
     */
    public interface SysParameterService {
    
    	/**
    	 * 
    	 * @methodName		: loadData
    	 * @description		: 加载数据库中数据,允许重复调用
    	 * @return			: 成功返回true,否则返回false。
    	 *
    	 */	
    	public boolean loadData();
    	
    	/**
    	 * 
    	 * @methodName		: getParameterClass
    	 * @description		: 获取指定classKey的参数类别的子项列表
    	 * @param classKey	: 参数类别key
    	 * @return			: 指定classKey的参数类别的子项列表
    	 * @history		:
    	 * ------------------------------------------------------------------------------
    	 * date			version		modifier		remarks                   
    	 * ------------------------------------------------------------------------------
    	 * 2021/06/02	1.0.0		sheng.zheng		初版
    	 *
    	 */
    	public List<SysParameter> getParameterClass(String classKey);
    	
    	/**
    	 * 
    	 * @methodName		: getParameterItemByKey
    	 * @description		: 根据classKey和itemKey获取参数子项
    	 * @param classKey	: 参数类别key
    	 * @param itemKey	: 子项key
    	 * @return			: SysParameter对象
    	 *
    	 */
    	public SysParameter getParameterItemByKey(String classKey,String itemKey);
    	
    	/**
    	 * 
    	 * @methodName		: getParameterItemByValue
    	 * @description		: 根据classKey和itemValue获取参数子项
    	 * @param classKey	: 参数类别key	
    	 * @param itemValue	: 子项值
    	 * @return			: SysParameter对象
    	 *
    	 */
    	public SysParameter getParameterItemByValue(String classKey,String itemValue);
    }
    

    ​ SysParameterService类定义了下列接口方法:

    • loadData方法,用于初始加载数据和更新数据。
    • getParameterClass方法,获取指定classKey的类别的所有子项列表。此方法调用会非常频繁。
    • getParameterItemByKey方法,根据classKey和itemKey获取参数子项,用于根据枚举值显示物理含义。此方法调用会非常频繁。
    • getParameterItemByValue方法,根据classKey和itemValue获取参数子项,用于根据物理含义取得枚举值。此方法调用会非常频繁。

    3.4、ServiceImpl类

    ​ 服务实现类为SysParameterServiceImpl,代码如下:

    package com.abc.questInvest.service.impl;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.abc.questInvest.dao.SysParameterDao;
    import com.abc.questInvest.entity.SysParameter;
    import com.abc.questInvest.service.SysParameterService;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @className	: SysParameterServiceImpl
     * @description	: SysParameterService实现类
     * @summary		: 实现对系统参数的管理
     *
     */
    @Slf4j
    @Service
    public class SysParameterServiceImpl implements SysParameterService{
    	//sys_parameters表数据访问对象
    	@Autowired
    	private SysParameterDao sysParameterDao;
    	
    	//管理全部的SysParameter表记录
    	private Map<String,Map<String,SysParameter>> sysParameterMap = new HashMap<String,Map<String,SysParameter>>();
    	
    	/**
    	 * 
    	 * @methodName		: loadData
    	 * @description		: 加载数据库中数据 
    	 * @return			: 成功返回true,否则返回false。
    	 *
    	 */	
    	@Override
    	public boolean loadData() {
    		try
    		{
    			//查询sys_parameters表,获取全部数据
    			List<SysParameter> sysParameterList = sysParameterDao.selectAll();
    			
    			synchronized(sysParameterMap) {
    				//先清空map,便于刷新调用
    				sysParameterMap.clear();
    				//将查询结果放入map对象中,按每个类别组织
    				for(SysParameter item : sysParameterList) {
    					String classKey = item.getClassKey();
    					String itemKey = item.getItemKey();
    					Map<String,SysParameter> sysParameterClassMap = null;
    					if (sysParameterMap.containsKey(classKey)) {
    						//如果存在该类别,则获取对象
    						sysParameterClassMap = sysParameterMap.get(classKey);
    					}else {
    						//如果不存在该类别,则创建
    						sysParameterClassMap = new HashMap<String,SysParameter>();
    						//加入map中
    						sysParameterMap.put(classKey, sysParameterClassMap);
    					}
    					sysParameterClassMap.put(itemKey,item);
    				}
    			}
    		}catch(Exception e) {
    			log.error(e.getMessage());
    			e.printStackTrace();
    			return false;
    		}
    		return true;
    	}
    	
    	/**
    	 * 
    	 * @methodName		: getParameterClass
    	 * @description		: 获取指定classKey的参数类别的子项列表
    	 * @param classKey	: 参数类别key
    	 * @return			: 指定classKey的参数类别的子项列表
    	 *
    	 */
    	@Override
    	public List<SysParameter> getParameterClass(String classKey){
    		List<SysParameter> sysParameterList = new ArrayList<SysParameter>();
    		
    		//获取classKey对应的子map,将所有子项加入列表中
    		if (sysParameterMap.containsKey(classKey)) {
    			Map<String,SysParameter> sysParameterClassMap = sysParameterMap.get(classKey);
    			for(SysParameter item : sysParameterClassMap.values()) {
    				sysParameterList.add(item);
    			}
    		}
    		
    		return sysParameterList;
    	}
    	
    	/**
    	 * 
    	 * @methodName		: getParameterItemByKey
    	 * @description		: 根据classKey和itemKey获取参数子项
    	 * @param classKey	: 参数类别key
    	 * @param itemKey	: 子项key
    	 * @return			: SysParameter对象
    	 *
    	 */
    	@Override
    	public SysParameter getParameterItemByKey(String classKey,String itemKey) {
    		SysParameter sysParameter = null;
    		
    		if (sysParameterMap.containsKey(classKey)) {
    			//如果classKey存在
    			Map<String,SysParameter> sysParameterClassMap = sysParameterMap.get(classKey);
    			if (sysParameterClassMap.containsKey(itemKey)) {
    				//如果itemKey存在
    				sysParameter = sysParameterClassMap.get(itemKey);
    			}
    		}
    		
    		return sysParameter;
    	}
    	
    	/**
    	 * 
    	 * @methodName		: getParameterItemByValue
    	 * @description		: 根据classKey和itemValue获取参数子项
    	 * @param classKey	: 参数类别key	
    	 * @param itemValue	: 子项值
    	 * @return			: SysParameter对象
    	 *
    	 */
    	@Override
    	public SysParameter getParameterItemByValue(String classKey,String itemValue) {
    		SysParameter sysParameter = null;
    		
    		if (sysParameterMap.containsKey(classKey)) {
    			//如果classKey存在
    			Map<String,SysParameter> sysParameterClassMap = sysParameterMap.get(classKey);
    			//遍历
    			for (Map.Entry<String,SysParameter> item : sysParameterClassMap.entrySet()) {
    				if(item.getValue().getItemValue().equals(itemValue)) {
    					//如果匹配值
    					sysParameter = item.getValue();
    					break;
    				}
    			}
    		}
    		
    		return sysParameter;
    		
    	}
    }
    

    ​ SysParameterServiceImpl类使用了Map<String,Map<String,SysParameter>>类型的属性变量sysParameterMap来管理全部的系统参数,外层Map管理classKey到Map<String,SysParameter>的映射关系,每一项为一个参数类别,而里层Map<String,SysParameter>,用于管理itemKey与SysParameter之间的映射关系,每一项为该类别下的一个子项。使用sysParameterMap属性的目的,是将所有系统参数都加载到内存中,从而无需频繁访问数据库。

    ​ loadData方法,用于初始加载数据和更新时刷新数据,为了防止更新时脏读数据,加了同步锁。这个方法调用不频繁。

    3.5、全局配置服务类

    ​ 全局配置服务类用于管理全局配置参数,包括系统参数、权限树等。如果只有一种参数,可以不必有此类,因为这样加了一层壳。

    ​ 服务接口类为GlobalConfigService,代码如下:

    package com.abc.questInvest.service;
    
    /**
     * @className	: GlobalConfigService
     * @description	: 全局变量管理类
     *
     */
    public interface GlobalConfigService {
    	
    	/**
    	 * 
    	 * @methodName		: loadData
    	 * @description		: 加载数据 
    	 * @return			: 成功返回true,否则返回false
    	 *
    	 */
    	public boolean loadData();
    	
    	
    	//获取SysParameterService对象
    	public SysParameterService getSysParameterService();
    	
    	//获取其它配置数据服务对象
    	//public FunctionTreeService getFunctionTreeService();
    }
    

    ​ GlobalConfigService提供了下列接口方法:

    • loadData方法,加载配置对象数据,确定多个配置对象的加载次序。

    • getSysParameterService方法,获取系统参数服务类对象。

    • 获取其它可能的配置服务对象的方法。

    ​ 服务实现类为GlobalConfigServiceImpl,代码如下:

    package com.abc.questInvest.service.impl;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.abc.questInvest.service.FunctionTreeService;
    import com.abc.questInvest.service.GlobalConfigService;
    import com.abc.questInvest.service.RoleFuncRightsService;
    import com.abc.questInvest.service.SysParameterService;
    import com.abc.questInvest.service.TableCodeConfigService;
    
    /**
     * @className	: GlobalConfigServiceImpl
     * @description	: GlobalConfigService实现类
     *
     */
    @Service
    public class GlobalConfigServiceImpl implements GlobalConfigService{
    		
    	//系统参数表数据服务对象
    	@Autowired
    	private SysParameterService sysParameterService;
    	
    	//其它配置数据服务对象
    	
    	/**
    	 * 
    	 * @methodName		: loadData
    	 * @description		: 加载数据 
    	 * @return			: 成功返回true,否则返回false
    	 *
    	 */
    	@Override
    	public boolean loadData() {
    		boolean bRet = false;
    				
    		//加载sys_parameters表记录
    		bRet = sysParameterService.loadData();
    		if (!bRet) {
    			return bRet;
    		}
    		
    		//加载其它配置数据
    				
    		return bRet;
    	}
    	
    	
    	//获取SysParameterService对象
    	@Override
    	public SysParameterService getSysParameterService() {
    		return sysParameterService;
    	}
    	
    	//获取其它配置数据服务对象方法
    	
    }
    

    3.6、启动时加载

    ​ 全局配置服务类在应用启动时加载到Spring容器中,这样可实现共享,减少对数据库的访问压力。

    ​ 实现一个ApplicationListener类,代码如下:

    package com.abc.questInvest;
    
    import javax.servlet.ServletContext;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.WebApplicationContext;
    
    import com.abc.questInvest.service.GlobalConfigService;
    
    /**
     * @className	: ApplicationStartup
     * @description	: 应用侦听器
     *
     */
    @Component
    public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{
        //全局变量管理对象,此处不能自动注入
        private GlobalConfigService globalConfigService = null;
        
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
            try {
        	    if(contextRefreshedEvent.getApplicationContext().getParent() == null){ 
        	    	//root application context 没有parent.
    				
        	    	System.out.println("========定义全局变量==================");
        	    	// 将 ApplicationContext 转化为 WebApplicationContext
        	        WebApplicationContext webApplicationContext =
        	                (WebApplicationContext)contextRefreshedEvent.getApplicationContext();
        	        // 从 webApplicationContext 中获取  servletContext
        	        ServletContext servletContext = webApplicationContext.getServletContext();
        	        
        	        //加载全局变量管理对象
        	        globalConfigService = (GlobalConfigService)webApplicationContext.getBean(GlobalConfigService.class);
        	        //加载数据
        	        boolean bRet = globalConfigService.loadData();
        	        if (false == bRet) {
        	        	System.out.println("加载全局变量失败");
        	        	return;
        	        }        
        	        //======================================================================
        	        // servletContext设置值
        	        servletContext.setAttribute("GLOBAL_CONFIG_SERVICE", globalConfigService);  
        	        
        	    }
        	} catch (Exception e) {
        	    e.printStackTrace();
        	}        
        }
    }
    

    ​ 注意,globalConfigService不能自动注入,否则得到空指针。通过下列代码来加载bean。

    //加载全局变量管理对象
        	        globalConfigService = (GlobalConfigService)webApplicationContext.getBean(GlobalConfigService.class);
    

    ​ 代码中,将globalConfigService对象作为全局变量加入ServletContext中,就可以实现共享了。

    ​ 在启动类中,加入该应用侦听器ApplicationStartup。

    	public static void main(String[] args) {
        	SpringApplication springApplication = new SpringApplication(QuestInvestApplication.class);
            springApplication.addListeners(new ApplicationStartup());
            springApplication.run(args);  
    	}
    

    3.7、在服务实现类中访问系统参数

    ​ HttpServletRequest类型对象request在控制器方法中可以获取,可作为参数传入服务实现类的方法中。下面是服务实现类访问系统参数的示例代码:

    		//获取ServletContext对象
    		ServletContext servletContext = request.getServletContext();
    		//获取全部数据服务对象
    		GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
    		//获取系统参数url_prefix的值
    		String url_prefix = "";
    		SysParameter sysParameter = null;
    		sysParameter = globalConfigService.getSysParameterService()
    				.getParameterItemByKey("url_param", "url_prefix");
    		if (sysParameter != null) {
    			url_prefix = sysParameter.getItemValue();
    		}
    
    作者:阿拉伯1999
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    养成良好习惯,好文章随手顶一下。
  • 相关阅读:
    java循环结构
    java 修饰符
    java变量类型
    java对象和类
    java 环境配置及开发工具
    easy_install 和 pip
    比利牛斯獒犬 flask web
    vim 命令
    vim vi Ubuntu 设置
    Python interview_python
  • 原文地址:https://www.cnblogs.com/alabo1999/p/14907461.html
Copyright © 2011-2022 走看看