面向接口编程——整合多缓存实例
在企业级JavaWeb框架中,面对业务的复杂性和变化性,往往一种企业级功能需要开发多套技术以便灵活使用,本文通过Redis和Ehcache的整合,来介绍面向接口编程的方便性。
- 第一步根据缓存的共性,抽象出接口类:
1 public interface ICacheManager { 2 3 <T extends Object> T get(String cacheName, Object key); 4 5 void put(String cacheName, Object key, Object value); 6 7 void remove(String cacheName, Object key); 8 9 void removeAll(String cacheName); 10 11 }
- 第二步分别对Redis和Ehcache做具体实现:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 5 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 6 7 <description>Jedis Configuration</description> 8 9 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> 10 <!-- 最大连接数 --> 11 <property name="maxTotal" value="${redis.pool.maxTotal}"/> 12 <!-- 最大空闲 --> 13 <property name="maxIdle" value="${redis.pool.maxIdle}"/> 14 <!-- 每次最大连接数 --> 15 <property name="numTestsPerEvictionRun" value="${redis.pool.numTestsPerEvictionRun}"/> 16 <!-- 释放扫描的扫描间隔 --> 17 <property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}"/> 18 <!-- 连接的最小空闲时间 --> 19 <property name="minEvictableIdleTimeMillis" value="${redis.pool.minEvictableIdleTimeMillis}"/> 20 <!-- 连接控歘按时间多久后释放,当空闲时间>该值且空闲连接>最大空闲连接数时直接释放 --> 21 <property name="softMinEvictableIdleTimeMillis" value="${redis.pool.softMinEvictableIdleTimeMillis}"/> 22 <!-- 获得链接时的最大等待毫秒数,小于0:阻塞不确定时间,默认-1 --> 23 <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/> 24 <!-- 在获得链接的时候检查有效性,默认false --> 25 <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/> 26 <!-- 在空闲时检查有效性,默认false --> 27 <property name="testWhileIdle" value="${redis.pool.testWhileIdle}"/> 28 <!-- 连接耗尽时是否阻塞,false报异常,true阻塞超时 默认:true--> 29 <property name="blockWhenExhausted" value="${redis.pool.blockWhenExhausted}"/> 30 </bean> 31 32 <bean id="redisConnectionFactory" 33 class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> 34 <constructor-arg index="0" ref="jedisPoolConfig" /> 35 <property name="hostName" value="${redis.ip}" /> 36 <property name="port" value="${redis.port}" /> 37 <property name="password" value="${redis.password}" /> 38 <property name="timeout" value="${redis.timeout}" /> 39 </bean> 40 41 <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> 42 <property name="connectionFactory" ref="redisConnectionFactory" /> 43 </bean> 44 45 <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> 46 <constructor-arg index="0" ref="redisTemplate" /> 47 </bean> 48 </beans>
1 public class RedisCacheManagerImpl implements ICacheManager { 2 3 public final static RedisCacheManager redisCacheManager = SpringContextHolder.getBean("redisCacheManager"); 4 5 protected static CacheManager getCacheManager() { 6 return redisCacheManager; 7 } 8 9 @Override 10 @SuppressWarnings("unchecked") 11 public <T extends Object> T get(String cacheName, Object key) { 12 Cache cache = getCacheManager().getCache(cacheName); 13 Cache.ValueWrapper valueWrapper = cache.get(cacheName + "." + key); 14 return (T) valueWrapper.get(); 15 } 16 17 @Override 18 public void put(String cacheName, Object key, Object value) { 19 Cache cache = getCacheManager().getCache(cacheName); 20 cache.put(cacheName + "." + key, value); 21 } 22 23 @Override 24 public void remove(String cacheName, Object key) { 25 Cache cache = getCacheManager().getCache(cacheName); 26 cache.evict(cacheName + "." + key); 27 } 28 29 @Override 30 public void removeAll(String cacheName) { 31 Cache cache = getCacheManager().getCache(cacheName); 32 cache.clear(); 33 } 34 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ehcache updateCheck="false" name="defaultCache"> 3 4 <!-- 5 磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存 6 path:指定在硬盘上存储对象的路径 7 --> 8 <diskStore path="java.io.tmpdir/otc"/> 9 10 <!-- 默认缓存配置. --> 11 <defaultCache maxEntriesLocalHeap="100" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" 12 overflowToDisk="true" maxEntriesLocalDisk="100000"/> 13 14 </ehcache>
1 public class EhCacheManagerImpl implements ICacheManager { 2 3 public final static CacheManager cacheManager = SpringContextHolder.getBean("cacheManager"); 4 5 protected static CacheManager getCacheManager() { 6 return cacheManager; 7 } 8 9 /** 10 * 获得一个Cache,没有则创建一个。 11 * @param cacheName 12 * @return 13 */ 14 private static synchronized Cache getCache(String cacheName) { 15 Cache cache = getCacheManager().getCache(cacheName); 16 if (cache == null) { 17 getCacheManager().addCache(cacheName); 18 cache = getCacheManager().getCache(cacheName); 19 cache.getCacheConfiguration().setEternal(true); 20 } 21 return cache; 22 } 23 24 @Override 25 @SuppressWarnings("unchecked") 26 public <T extends Object> T get(String cacheName, Object key) { 27 Element element = getCache(cacheName).get(key); 28 return element == null ? null : (T) element.getObjectValue(); 29 } 30 31 @Override 32 public void put(String cacheName, Object key, Object value) { 33 Element element = new Element(key, value); 34 getCache(cacheName).put(element); 35 } 36 37 @Override 38 public void remove(String cacheName, Object key) { 39 getCache(cacheName).remove(key); 40 } 41 42 @Override 43 public void removeAll(String cacheName) { 44 Cache cache = getCacheManager().getCache(cacheName); 45 cache.removeAll(true); 46 } 47 }
- 第三步:创建枚举类和工厂类用于生成对应的缓存类
1 public enum CacheEnum{ 2 3 ehcache("ehCacheManagerImpl", "ehcache 实现"), 4 redis("redisCacheManagerImpl", "redis 实现"); 5 6 private String cacheType; 7 8 private String cacheDesc; 9 10 CacheEnum(String cacheType, String cacheDesc) { 11 this.cacheType = cacheType; 12 this.cacheDesc = cacheDesc; 13 } 14 15 public String getCacheType() { 16 return cacheType; 17 } 18 19 public String getCacheDesc() { 20 return cacheDesc; 21 } 22 }
1 public final class CacheFactory { 2 3 /** 4 * 获取默认缓存对象 5 * @return 6 */ 7 public static ICacheManager getCache(){ 8 return getCache(CacheEnum.ehcache); 9 } 10 11 /** 12 * 根据类型获取缓存对象 13 * @param cacheType 14 * @return 15 */ 16 public static ICacheManager getCache(CacheEnum cacheType){ 17 if(cacheType == null) 18 cacheType = CacheEnum.ehcache; 19 return SpringContextHolder.getBean(cacheType.getCacheType()); 20 } 21 }
第四步:使用缓存类
1 @Service 2 public class DictContextService { 3 private ICacheManager cacheManager = CacheFactory.getCache(); 4 @Autowired 5 private DictionaryMapper dictionaryMapper; 6 @Autowired 7 private DictionaryItemMapper dictionaryItemMapper; 8 @Autowired 9 private JdbcTemplate jdbcTemplate; 10 11 private final String DICTIONARYS_CACHE_KEY = "dictionarys_cache_key"; 12 private final String DICTIONARY_ITEMS_CACKE_KEY = "dictionary_items_cacke"; 13 14 /** 15 * 获取 完整对应后的 有效字典 16 * @return 17 */ 18 public Map<String,Dictionary> getMapDictionary(){ 19 List<Dictionary> dictionarys = getValidDictionarys(); 20 Map<String, Dictionary> rltDictMap = new HashMap<String, Dictionary>(); 21 for (Dictionary dictionary : dictionarys) { 22 if(dictionary.getDictType() == Constant.NUMBER_INTEGER_1 && 23 dictionary.getIsCache() == Constant.NUMBER_INTEGER_1) { //(内部字典)开启缓存的字典 24 List<DictionaryItem> dictItems = getDictResponseItems(dictionary.getDictCode()); //获取字典对应的字典项 25 dictionary.setDictItems(dictItems); 26 rltDictMap.put(dictionary.getDictCode(), dictionary); 27 }else{ 28 rltDictMap.put(dictionary.getDictCode(), dictionary); 29 } 30 } 31 return rltDictMap; 32 } 33 34 /** 35 * 获取字典项(内部字典项、外部字典项) 36 * @param dictionary 37 * @return 38 */ 39 public List<DictionaryItem> getDictItems(final Dictionary dictionary){ 40 Object[] params = new Object[] {dictionary.getDictCode()}; 41 String querySql = "select dict_item_id dictItemId, dict_code dictCode, item_code itemCode, " 42 + " item_name itemName from ct_dictionary_item a where a.DICT_CODE = ?"; 43 44 if(dictionary.getDictType() == Constant.NUMBER_INTEGER_2) { 45 querySql = dictionary.gettSource(); 46 params = null; 47 String tParam = dictionary.gettParams() == null ? "" : dictionary.gettParams(); 48 Object[] _param = tParam.split(","); 49 if(_param.length > 0 && StringUtil.isNotEmpty(String.valueOf(_param[0]))) { 50 params = _param; 51 } 52 } 53 54 List<DictionaryItem> rItemList = jdbcTemplate.query(querySql, params, new RowMapper<DictionaryItem>() { 55 @Override 56 public DictionaryItem mapRow(ResultSet rs, int rowNum) throws SQLException { 57 DictionaryItem dictItem = new DictionaryItem(); 58 if(dictionary.getDictType() == Constant.NUMBER_INTEGER_1) { 59 dictItem.setDictItemId(rs.getString("dictItemId")); 60 dictItem.setDictCode(rs.getString("dictCode")); 61 dictItem.setItemCode(rs.getString("itemCode")); 62 dictItem.setItemName(rs.getString("itemName")); 63 }else { 64 dictItem.setDictCode(dictionary.getDictCode()); 65 dictItem.setItemCode(rs.getString(dictionary.getfCode())); 66 dictItem.setItemName(rs.getString(dictionary.getfName())); 67 } 68 return dictItem; 69 } 70 71 }); 72 73 return rItemList; 74 75 } 76 77 /** 78 * 获取 字典对应的字典项 79 * @return 80 */ 81 public List<DictionaryItem> getDictResponseItems(String dictCode){ 82 if(StringUtil.isEmpty(dictCode)) 83 return null; 84 List<DictionaryItem> dictionaryItems = getValidDictionaryItems(); 85 List<DictionaryItem> rltItems = new ArrayList<DictionaryItem>(); 86 for (DictionaryItem dictionaryItem : dictionaryItems) { 87 if(dictCode.equals(dictionaryItem.getDictCode())) { 88 rltItems.add(dictionaryItem); 89 } 90 } 91 return rltItems; 92 } 93 94 /** 95 * 获取 所有有效的字典 96 * @return 97 */ 98 public List<Dictionary> getValidDictionarys(){ 99 List<Dictionary> dictionarys = cacheManager.get(Dictionary.class.getName(), DICTIONARYS_CACHE_KEY); 100 if(dictionarys == null) { 101 dictionarys = dictionaryMapper.getValidDictionarys(); 102 cacheManager.put(Dictionary.class.getName(), DICTIONARYS_CACHE_KEY, dictionarys); 103 } 104 return dictionarys; 105 } 106 107 /** 108 * 获取 所有有效的字典项 109 * @return 110 */ 111 public List<DictionaryItem> getValidDictionaryItems(){ 112 List<DictionaryItem> dictionaryItems = cacheManager.get(DictionaryItem.class.getName(), DICTIONARY_ITEMS_CACKE_KEY); 113 if(dictionaryItems == null) { 114 dictionaryItems = dictionaryItemMapper.getValidDictionaryItems(); 115 cacheManager.put(DictionaryItem.class.getName(), DICTIONARY_ITEMS_CACKE_KEY, dictionaryItems); 116 } 117 return dictionaryItems; 118 } 119 }