Web項目啟動加載數據至內存--SpringApplicationListener實現


需求:

    1.項目開發中會有一些平凡使用的數據需要加載到內存中;以減少數據庫交互次數.降低服務器和數據庫壓力.

思路:

    1.在系統啟動時,將監聽web容器創建完成事件;
    2.創建一個用於存儲相關數據的Dic類;
    3.在監聽到容器創建完成后,將為Dic類中的靜態變量賦值;
    4.這樣就可以在應用中隨意使用Dic類中的數據;

優劣勢:

    1.減少web服務與數據庫的交互次數,減輕雙方壓力;
    2.web服務啟動時間將會被延長;

環境:

     import org.springframework.context.ApplicationListener;
     import org.springframework.context.event.ContextRefreshedEvent;

過程:

    1.創建一個類實現接口 ApplicationListener<ContextRefreshedEvent>,並將這個類用@component標簽掃描入Spring的對象管理池;
@Component
public class AppSpringEventListener implements ApplicationListener<ContextRefreshedEvent>{
	@Override
	public void onApplicationEvent(ContextRefreshedEvent arg0) {
		//spring會調用兩次這個方法,因為啟動時會創建了兩個容器(root application context 和projectName-servlet context),
		//我們只在root上下文創建成功后執行這個方法,初始化參數
		if(arg0.getApplicationContext().getParent()==null){
			//初始化數據字典
			DictionariesHelper.getInstance().init();
			//初始化省市縣字典
			RegionHelper.getInstance().init();
		}	
	}
}
 
    2.在DictionariesHelper類中定義靜態變量用於存儲字典信息,在RegionHelper中定義靜態變量用於存儲省市縣信息;
@Component
public class DictionariesHelper {
	private static final Logger log = LoggerFactory.getLogger(DictionariesHelper.class);
	//按層級存儲字典信息
	private static Map<Object,Object> dataDictionaries = new LinkedHashMap<Object,Object>();
	//存儲所有字典信息
	private static Map<String, String> dataAllMap = new LinkedHashMap<String, String>();
	private static DictionariesHelper dictionariesHelper;
	//service組件
	private static DataDictionariesServise dataDictionariesServise;
	//私有化構造函數
	private DictionariesHelper(){}
	
	public static DictionariesHelper getInstance(){
		if(DictionariesHelper.dictionariesHelper==null){
			DictionariesHelper.dictionariesHelper = new DictionariesHelper();
		}
		return DictionariesHelper.dictionariesHelper;
	}
	/**
	 * 初始化數據字典
	 */
	public void init(){
		if(DictionariesHelper.dataDictionaries.size()==0){
			initDataDictionaries();
		}
	}
	/**
	 * 重載數據字典
	 */
	public void reLoad() {
		// TODO Auto-generated method stub
		DictionariesHelper.dataDictionaries.clear();
		DictionariesHelper.dataAllMap.clear();
		init();
	}
	/**
	 * 初始化數據字典
	 */
	private void initDataDictionaries(){
		log.info("--------------數據字典初始化開始---------------");
		//獲取系統當前時間
		Long startTime = System.currentTimeMillis();
		List<Map<String, Object>> typeList = dataDictionariesServise.queryDataType();
		for(Map<String, Object> typeMap:typeList){
			LinkedHashMap<Object, Object> paraMap = new LinkedHashMap<Object,Object>();
			paraMap.put("OBJTYPE", typeMap.get("OBJTYPE"));
			paraMap.put("OBJNAME", typeMap.get("OBJNAME"));
			paraMap.put("OBJTYPECODE", typeMap.get("CONTYPEID"));
			List<Map<String, Object>> dataList = dataDictionariesServise.queryDataByType(typeMap);
			for(Map<String, Object> dataMap:dataList){
				//加載所有數據至dataAllMap
				DictionariesHelper.dataAllMap.put((String)dataMap.get("CONCODE"),(String)dataMap.get("CONNAME"));
			}
			DictionariesHelper.dataDictionaries.put(typeMap.get("OBJTYPE"), dataList);
		}
		log.info("--------------數據字典完成初始化,共用時"+(System.currentTimeMillis()-startTime)+"ms---------------");
	}
	
	/**
	 * 獲取所有字典數據
	 */
	public static Map<Object,Object> getDataDictionaries(){
		return DictionariesHelper.dataDictionaries;
	}
	
    /**
	 * 根據類型獲取下設的字典數據
	 * @param type 類型編碼(T_COMMON_TYPE表中的OBJTYPE字段)
	 * @return map:key為code,value為name
	 */
	public static List getDicListByType(String type){
		return  (List)DictionariesHelper.dataDictionaries.get(type);
	}
	
    /**
	 * 根據key獲取value
	 * @param code 編碼(T_COMMON_INFO中的CONCODE)
	 * @return 值(T_COMMON_INFO中的CONNAME)
	 */
	public static String getDicValueByCode(String code){
		return DictionariesHelper.dataAllMap.get(code);
	}
	
	@Autowired(required = true)
	public void setDataDictionariesServise(DataDictionariesServise dataDictionariesServise){
		DictionariesHelper.dataDictionariesServise = dataDictionariesServise;
	}
}
  @Component
public class RegionHelper {
	private static final Logger log = LoggerFactory.getLogger(RegionHelper.class);
	private static Map<Object,Object> regionDictionaries = new LinkedHashMap<Object, Object>(); 
	private static RegionHelper regionHelper;
	//service組件
	private static DataDictionariesServise dataDictionariesServise;
	private static List<Map<String, Object>> zTreeDatalist = new ArrayList<Map<String, Object>>();
	//省市縣級別
	public static Integer province = 1;
	public static Integer city = 2;
	public static Integer town = 3;
	public static RegionHelper getInstance(){
		if(RegionHelper.regionHelper==null){
			RegionHelper.regionHelper = new RegionHelper();
		}
		return RegionHelper.regionHelper;
	}
    
	/**
	 * 初始化省市縣字典
	 */
	public void init(){
		if(RegionHelper.regionDictionaries.size()==0){
			initRegionDictionaries();
		}
	}
    
	/**
	 * 重載省市縣字典
	 */
	public void reLoad(){
		RegionHelper.regionDictionaries.clear();
		init();
	}
    
	/**
	 * 初始化省市縣字典
	 */
	private void initRegionDictionaries() {
		log.info("--------------省市縣字典初始化開始---------------");
		//獲取系統當前時間
		Long startTime = System.currentTimeMillis();
		List<Map<String, Object>> list= dataDictionariesServise.queryRegion();
		for(Map<String, Object> map:list){
			//根據map構造字典
			loadRegionData(map);
		}
		log.info("--------------省市縣字典完成初始化,共用時"+(System.currentTimeMillis()-startTime)+"ms---------------");
	}
	
	/**
	 * 根據編碼獲取這個區划的Map,其中AREALIST是下設區縣
	 * @param regionCode 需要的省市縣
	 * @return 區划Map
	 */
	public static Map<Object,Object> getRegionMapByRegionCode(String regionCode){
		Integer level = RegionHelper.getRegionLevelByCode(regionCode);
		if(level==RegionHelper.province){
			return (Map<Object,Object>)RegionHelper.regionDictionaries.get(regionCode);
		}else if(level==RegionHelper.city){
			return (Map<Object,Object>)((Map<Object,Object>)(((Map<Object,Object>)RegionHelper.regionDictionaries.get(regionCode.substring(0,2)+"0000")).get("AREALIST"))).get(regionCode);
		}else if(level==RegionHelper.town){
			return (Map<Object,Object>)((Map<Object,Object>)((Map<Object,Object>)((Map<Object,Object>)(((Map<Object,Object>)RegionHelper.regionDictionaries.get(regionCode.substring(0,2)+"0000")).get("AREALIST"))).get(regionCode.substring(0,4)+"00")).get("AREALIST")).get(regionCode);
		}else{
			return null;
		}
	}
    
	/**
	 * 根據編碼獲取這個區划的下設區域,數據符合zTree簡單數據格式
	 * @param regionCode 需要的省市縣編碼
	 * @return 符合zTree簡單數據格式的json字符串
	 */
	public static String getRegionZTreeDataByRegionCode(String regionCode){
		RegionHelper.zTreeDatalist.clear();
		return JSONArray.toJSONString(RegionHelper.getRegionZTreeListByMap(RegionHelper.getRegionMapByRegionCode(regionCode)));
	}

	private static List<Map<String, Object>> getRegionZTreeListByMap(Map<Object, Object> map) {
		Map<String,Object> resultMap = new LinkedHashMap<String, Object>();
		resultMap.put("id", map.get("CODE"));
		resultMap.put("name", map.get("NAME"));
		resultMap.put("pId", map.get("SUPERIOR"));
		boolean isAdd = true;
		if((Map<Object,Object>)map.get("AREALIST")!=null&&((Map<Object,Object>)map.get("AREALIST")).size()>0){
			resultMap.put("isParent",true);
			RegionHelper.zTreeDatalist.add(resultMap);
			isAdd = false;
			Iterator iter = ((Map<Object,Object>)map.get("AREALIST")).keySet().iterator();
			while(iter.hasNext()){
				RegionHelper.getRegionZTreeListByMap((Map<Object,Object>)((Map<Object,Object>)map.get("AREALIST")).get(iter.next()));
			}
		}else{
			resultMap.put("isParent",false);
		}
		if(isAdd){
			RegionHelper.zTreeDatalist.add(resultMap);
		}
		//數組反轉
		//Collections.reverse(RegionHelper.zTreeDatalist);
		return RegionHelper.zTreeDatalist;
	}
    
	/**
	 * @param regionCode 地區編碼
	 * @return 省市縣級別:1代表省;2代表市3代表縣
	 */
	public static Integer getRegionLevelByCode(String regionCode){
		Integer i = Integer.parseInt(regionCode);
		if(i%100==0){
			if(i%10000==0){
				return RegionHelper.province;
			}else {
				return RegionHelper.city;
			}
		}else{
			return RegionHelper.town;
		}
	}
	
	private Boolean loadRegionData(Map<String, Object> map) {
		//加載省
		if(RegionHelper.regionDictionaries.get(map.get("PROCODE"))==null){
			Map<Object,Object> dataMap = new LinkedHashMap<Object,Object>();
			dataMap.put("CODE", map.get("PROCODE"));
			dataMap.put("NAME", map.get("PRONAME"));
			dataMap.put("AREALIST",new LinkedHashMap<Object,Object>());
			RegionHelper.regionDictionaries.put(map.get("PROCODE"), dataMap);
		}
		if(map.get("PROCODE")==null){
			return true;
		}
		//加載市
		Map<Object,Object> obj = (Map<Object,Object>)(((Map<Object,Object>)RegionHelper.regionDictionaries.get(map.get("PROCODE"))).get("AREALIST"));
		if(obj.get(map.get("CITYCODE"))==null&&map.get("CITYCODE")!=null){
			Map<Object,Object> dataMap = new LinkedHashMap<Object,Object>();
			dataMap.put("CODE", map.get("CITYCODE"));
			dataMap.put("NAME", map.get("CITYNAME"));
			dataMap.put("SUPERIOR", map.get("PROCODE"));
			dataMap.put("AREALIST",new LinkedHashMap<Object,Object>());
			obj.put(map.get("CITYCODE"), dataMap);
		}
		if(map.get("CITYCODE")==null){
			return true;
		}
		//加載縣
		obj = (Map<Object,Object>)((Map<Object,Object>)((Map<Object,Object>)(((Map<Object,Object>)RegionHelper.regionDictionaries.get(map.get("PROCODE"))).get("AREALIST"))).get(map.get("CITYCODE"))).get("AREALIST");
		if(obj.get("TOWNCODE")==null&&map.get("TOWNCODE")!=null){
			Map<Object,Object> dataMap = new LinkedHashMap<Object,Object>();
			dataMap.put("CODE", map.get("TOWNCODE"));
			dataMap.put("NAME", map.get("TOWNNAME"));
			dataMap.put("SUPERIOR", map.get("CITYCODE"));
			obj.put(map.get("TOWNCODE"), dataMap);
		}
		return true;
	}
	
	@Autowired(required = true)
	public void setDataDictionariesServise(DataDictionariesServise dataDictionariesServise){
		RegionHelper.dataDictionariesServise = dataDictionariesServise;
	} 
}
   //調用省市縣內存數據
String ztreeData = RegionHelper.getRegionZTreeDataByRegionCode("XXX");
//調用字典內存數據
List dicList = DictionariesHelper.getDicListByType("xxx");
 

原理:

    1.Spring會在web容器創建完成時調用SpringApplicationListener接口中的onApplicationEvent方法,所以我們自定義類實現 SpringApplicationListener接口,重寫onApplicationEvent方法.以便spring在容器完成創建事件后調用我的實現類,運行自定義方法.

總結:

    1.使用@Component標注兩個工具類是為了可以使用@Autowired標簽幫助我將service對象注入對象中;
    2.不能在在聲明service對象的屬性名上直接使用@Autowired標簽,那樣的話值注不進去還spring報錯(不清楚為什么).所以我寫了set方法用於注入service;
    3.在兩個Helper類中,我用於存儲數據的都是LinkedHashMap,而不是HashMap,因為,字典和省市縣在讀取數據時很多時候都會排序的尋求.而HashMap是無序的.這樣就算我在SQL中將數據排序.當放入Map中時又變成無序的了.而LinkedHashMap會記錄數據進入的循序.這樣的話就可以滿足我排序的要求;
    4.在 SpringApplicationListener中的onApplicationEvent方法中我做了一個if判斷,是因為Spring在啟動中會在兩個容器創建時(root application context和projectName-servlet context).其中projectName-servlet context是root application context的子容器.所以,我們判斷當當前容器的父容器是空時(也就是root application context),才執行我們的初始化方法.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM