詳解Spring Boot使用系統參數表提升系統的靈活性
一、使用系統參數表的好處
以數據庫表形式存儲的系統參數表比配置文件(.properties文件或.yaml文件)要更靈活,因為無需重啟系統就可以動態更新。
系統參數表可用於存儲下列數據:
表字段枚舉值,如下列字段:
`question_type` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '題型,1-單選題,2-多選題,3-問答題',
這個字段現在有3種取值,但是難保將來有擴展的可能,如:是非題、計算題、應用題等。
因此將取值的枚舉值用系統參數表來配置,可以提高系統擴展靈活性。
另一方面,對於前端而言,就可以通過查詢系統參數表數據,用於UI呈現,而不必硬編碼。如前端需要用下拉框來顯示所有可能的”題型“,這個列表就可以查詢系統參數表來獲取。
因此可以將所有字段枚舉值納入系統參數表管理。
參數設置,如郵件參數,對接的第三方系統的URL等。
二、系統參數表的表結構
系統參數表的表結構如下:
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', '請求接口方法');
三、系統參數表在項目中的使用
在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的參數類別的子項列表 * */ 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(); }
以上就是詳解Spring Boot使用系統參數表提升系統的靈活性的詳細內容,更多關於Spring Boot使用系統參數表提升系統的靈活性的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 關於Spring Boot動態權限變更問題的實現方案
- feign實現傳遞參數的三種方式小結
- 使用spring動態獲取接口的不同實現類
- java實現AES 32位加密解密的方案
- 聊聊在獲取方法參數名方面,Spring真的就比Mybatis強?