【Spring】1七、spring cache 與redis緩存整合html
spring cache,基本可以知足通常應用對緩存的需求,但現實老是很複雜,當你的用戶量上去或者性能跟不上,總須要進行擴展,這個時候你或許對其提供的內存緩存不滿意了,由於其不支持高可用性,也不具有持久化數據能力,這個時候,你就須要自定義你的緩存方案了,還好,spring 也想到了這一點。java
本篇文章採用spring cache與Redis進行整合,實現本身想要的緩存。redis
咱們先配置redis:spring
第一步,要安裝redis,這個自行百度,咱們主要是配置redis。緩存
增長一個redis配置文件,能夠放在跟目錄下app
redis.host=192.168.0.43 redis.port=6379 redis.pass=2015 redis.maxIdle=50 redis.maxActive=50 redis.maxWait=50 redis.testOnBorrow=true redis.timeout=1000
還須要在spring的配置文件中去配置rediside
<context:property-placeholder location="classpath:conf/redis.properties" /> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxTotal" value="${redis.maxActive}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="poolConfig" ref="poolConfig" /> <property name="port" value="${redis.port}" /> <property name="hostName" value="${redis.host}" /> <property name="password" value="${redis.pass}" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="connectionFactory" /> </bean>
好了,配置redis完成了。性能
如今咱們來配置spring的cache:this
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven /> <!-- 緩存管理器 --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="default" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.queryCityListByParentCode" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.queryIndustryListByParentCode" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.queryIndustryInfoById" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.queryIndustryNameByIds" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.queryCityNameByIds" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.isSpecialSchool" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="commonService.getProvinceByCity" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="permissionService.queryMenuList" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="permissionService.queryOperationOfMenu" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="roleService.queryAllRole" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="permissionService.queryPermissionTree" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="permissionService.queryPermissaionMenuByRoleCode" /> <property name="timeout" value="${redis.timeout}" /> </bean> <bean class="com.config.SystemRedisCache"> <property name="redisTemplate" ref="redisTemplate" /> <property name="name" value="permissionService.queryAllPermissionByRoleCode" /> <property name="timeout" value="${redis.timeout}" /> </bean> </set> </property> <!-- <property name="fallbackToNoOpCache" value="false"/> --> </bean> </beans>
其實上面的配置文件,已經把redis與spring註解緩存的關係配置到了spring的xml文件中了。編碼
對應的SystemRedisCache類是一個實現cache接口的自定義的緩存實現類。
import org.springframework.cache.Cache; import org.springframework.cache.support.SimpleValueWrapper; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.StringUtils; /** * 〈一句話功能簡述〉<br> * 〈功能詳細描述〉 * * @author Administrator * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class SystemRedisCache implements Cache { /** * Redis */ private RedisTemplate<String, Object> redisTemplate; /** * 緩存名稱 */ private String name; /** * 超時時間 */ private long timeout; /* * (non-Javadoc) * @see org.springframework.cache.Cache#getName() */ @Override public String getName() { return this.name; } /* * (non-Javadoc) * @see org.springframework.cache.Cache#getNativeCache() */ @Override public Object getNativeCache() { // TODO Auto-generated method stub return this.redisTemplate; } /* * (non-Javadoc) * @see org.springframework.cache.Cache#get(java.lang.Object) */ @Override public ValueWrapper get(Object key) { if (StringUtils.isEmpty(key)) { return null; } else { final String finalKey; if (key instanceof String) { finalKey = (String) key; } else { finalKey = key.toString(); } Object object = null; object = redisTemplate.execute(new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = finalKey.getBytes(); byte[] value = connection.get(key); if (value == null) { return null; } return SerializableObjectUtil.unserialize(value); } }); return (object != null ? new SimpleValueWrapper(object) : null); } } /* * (non-Javadoc) * @see org.springframework.cache.Cache#get(java.lang.Object, java.lang.Class) */ @SuppressWarnings("unchecked") @Override public <T> T get(Object key, Class<T> type) { if (StringUtils.isEmpty(key) || null == type) { return null; } else { final String finalKey; final Class<T> finalType = type; if (key instanceof String) { finalKey = (String) key; } else { finalKey = key.toString(); } final Object object = redisTemplate.execute(new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = finalKey.getBytes(); byte[] value = connection.get(key); if (value == null) { return null; } return SerializableObjectUtil.unserialize(value); } }); if (finalType != null && finalType.isInstance(object) && null != object) { return (T) object; } else { return null; } } } /* * (non-Javadoc) * @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object) */ @Override public void put(final Object key, final Object value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return; } else { final String finalKey; if (key instanceof String) { finalKey = (String) key; } else { finalKey = key.toString(); } if (!StringUtils.isEmpty(finalKey)) { final Object finalValue = value; redisTemplate.execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection connection) { connection.set(finalKey.getBytes(), SerializableObjectUtil.serialize(finalValue)); // 設置超時間 connection.expire(finalKey.getBytes(), timeout); return true; } }); } } } /* * 根據Key 刪除緩存 */ @Override public void evict(Object key) { if (null != key) { final String finalKey; if (key instanceof String) { finalKey = (String) key; } else { finalKey = key.toString(); } if (!StringUtils.isEmpty(finalKey)) { redisTemplate.execute(new RedisCallback<Long>() { public Long doInRedis(RedisConnection connection) throws DataAccessException { return connection.del(finalKey.getBytes()); } }); } } } /* * 清楚系統緩存 */ @Override public void clear() { // TODO Auto-generated method stub // redisTemplate.execute(new RedisCallback<String>() { // public String doInRedis(RedisConnection connection) throws DataAccessException { // connection.flushDb(); // return "ok"; // } // }); } public RedisTemplate<String, Object> getRedisTemplate() { return redisTemplate; } public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } public void setName(String name) { this.name = name; } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } }
主要的方法就是get和put方法,裏面的邏輯都是根據咱們本身的需求去實現的。
如今有個問題,咱們發現,在spring配置本身的註解緩存的配置文件中配置了多個cache,那spring是怎麼去找到對應的cacheManager呢?
咱們直接以代碼給你們呈現出來:
/** * * 公共接口 * * @author Administrator * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ @Service("commonService") public class CommonServiceImpl implements CommonService { /** * 日誌記錄器 */ private static final Logger LOGGER = LoggerFactory.getLogger(CommonServiceImpl.class); @Autowired private DalClient dalClient; /* * @Autowired RedisTemplate<?, ?> redisTemplate; */ /** * 根據名稱獲取自增序列squence的當前值 * * @param SequenceName 自增序列名稱 * @return 自增序列當前值 * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override public String getSequenceByName(String SequenceName) { if (StringUtils.isEmpty(SequenceName)) { LOGGER.error("自增序列名稱爲空,沒法返回正常的自增序列值"); return null; } else { Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("sequenceName", SequenceName); // 查詢sequence當前值 Map<String, Object> resultMap = dalClient.queryForMap("common.GET_SEQUENCE_BY_NAME", paramMap); if (null != resultMap && !resultMap.isEmpty()) { return String.valueOf(resultMap.get("sequenceValue")); } else { return null; } } } /** * 根據上一級的城市編碼 查詢 全部下屬城市 列表 * * @param parentCityCode * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.queryCityListByParentCode", key = "new String('commonService.queryCityListByParentCode')+#parentCityCode.toString()", condition = "null != #parentCityCode") public List<CityBean> queryCityListByParentCode(final Integer parentCityCode) { Map<String, Object> paramMap = new HashMap<String, Object>(); if (null != parentCityCode) { // 根據所選省份 \ 城市 查詢所屬城市列表 paramMap.put("parentCityCode", parentCityCode); final List<CityBean> cityListResult = dalClient.queryForList("T_CITY.SELECT_BY_PARENTCODE", paramMap, CityBean.class); return cityListResult; } else { final List<CityBean> provinceListResult = dalClient.queryForList("T_CITY.SELECT_ALL_FIRST_STEP_CITY", paramMap, CityBean.class); return provinceListResult; } } /** * 根據上一級的行業編碼 查詢 全部下屬全部行業 * * @param parentCityCode * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.queryIndustryListByParentCode", key = "new String('commonService.queryIndustryListByParentCode')+#parentIndustryCode.toString", condition = "null != #parentIndustryCode") public List<IndustryBean> queryIndustryListByParentCode(final Integer parentIndustryCode) { Map<String, Object> paramMap = new HashMap<String, Object>(); if (null != parentIndustryCode) { paramMap.put("parentIndustryCode", parentIndustryCode); final List<IndustryBean> industryListResult = dalClient.queryForList("T_INDUSTRY.SELECT_BY_PARENTCODE", paramMap, IndustryBean.class); return industryListResult; } else { final List<IndustryBean> industryListResult = dalClient.queryForList( "T_INDUSTRY.SELECT_ALL_FIRST_STEP_INDUSTRY", paramMap, IndustryBean.class); return industryListResult; } } /** * 根據行業編碼查詢 行業信息 * * @param industryCode * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.queryIndustryInfoById", key = "new String('commonService.queryIndustryInfoById')+#industryCode", condition = "(null != #industryCode) and (#industryCode.length() > 0)") public IndustryBean queryIndustryInfoById(final String industryCode) { if (StringUtils.isEmpty(industryCode)) { return null; } else { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("industryCode", industryCode); final IndustryBean industryInfoResult = dalClient.queryForObject("T_INDUSTRY.SELECT_BY_ID", paramMap, IndustryBean.class); return industryInfoResult; } } /** * 遞歸刪除 元素 由於可能存在重複的 * * @param list 列表 * @param item 要刪除的元素 * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ private void deleteListElement(ArrayList<String> list, String item) { if (null != list && !list.isEmpty() && StringUtils.isNotBlank(item)) { if (list.contains(item)) { list.remove(item); if (list.contains(item)) { deleteListElement(list, item); } } } } /** * 根據行業id查詢 行業名稱 * * @param industryIds 行業Id可能有多個 以分號分隔 * @return 行業名稱列表 * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.queryIndustryNameByIds", key = "new String('commonService.queryIndustryNameByIds')+#industryIds", condition = "null != #industryIds and #industryIds.length() > 0") public List<String> queryIndustryNameByIds(final String industryIds) { if (StringUtils.isBlank(industryIds)) { return null; } else { String[] industryIdArr = industryIds.split(";"); if (null != industryIdArr && industryIdArr.length > 0) { ArrayList<String> paramList = new ArrayList<String>(); paramList.addAll(Arrays.asList(industryIdArr)); if (null != paramList && !paramList.isEmpty()) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("industryIdList", paramList); // 查詢行業列表 List<IndustryBean> queryResultList = dalClient.queryForList("T_INDUSTRY.SELECT_BY_ID_LIST", paramMap, IndustryBean.class); // 封裝查詢結果 List<String> industryNameList = new ArrayList<String>(); if (null != queryResultList && !queryResultList.isEmpty()) { // 遍歷查詢列表 將已經存在的編碼去掉 剩下的 就是 根據編碼查詢不出行業的 直接將行業的名字返回 String tempId; for (IndustryBean industryInfo : queryResultList) { if (null != industryInfo) { if (null == industryInfo.getIndustryCode()) { continue; } else { tempId = industryInfo.getIndustryCode().toString(); if (paramList.contains(tempId)) { deleteListElement(paramList, tempId); } if (StringUtils.isNotBlank(industryInfo.getIndustryName())) { industryNameList.add(industryInfo.getIndustryName()); } } } } } // 將根據編碼查詢不出來 的 行業編碼 直接返回 industryNameList.addAll(paramList); return industryNameList; } } return null; } } /** * 根據城市id查詢 城市名稱 * * @param industryIds 行業Id可能有多個 以分號分隔 * @return 行業名稱列表 * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.queryCityNameByIds", key = "new String('commonService.queryCityNameByIds')+#cityIds", condition = "null != #cityIds and #cityIds.length() > 0") public List<String> queryCityNameByIds(String cityIds) { if (StringUtils.isBlank(cityIds)) { return null; } else { String replacyedCityIds = cityIds.replace(";", ","); String[] industryIdArr = replacyedCityIds.split(","); if (null != industryIdArr && industryIdArr.length > 0) { ArrayList<String> paramList = new ArrayList<String>(); paramList.addAll(Arrays.asList(industryIdArr)); if (null != paramList && !paramList.isEmpty()) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("cityIdList", paramList); // 查詢行業列表 List<CityBean> queryResultList = dalClient.queryForList("T_CITY.SELECT_BY_ID_LIST", paramMap, CityBean.class); List<String> industryNameList = new ArrayList<String>(); if (null != queryResultList && !queryResultList.isEmpty()) { // 遍歷查詢列表 將已經存在的編碼去掉 剩下的 就是 根據編碼查詢不出行業的 直接將行業的名字返回 // 封裝查詢結果 String tempId; for (CityBean industryInfo : queryResultList) { if (null != industryInfo) { if (null == industryInfo.getCityCode()) { continue; } else { tempId = industryInfo.getCityCode().toString(); if (paramList.contains(tempId)) { deleteListElement(paramList, tempId); } if (StringUtils.isNotBlank(industryInfo.getCityName())) { industryNameList.add(industryInfo.getCityName()); } } } } } // 將根據編碼查詢不出來 的 行業編碼 直接返回 industryNameList.addAll(paramList); return industryNameList; } } return null; } } /** * 查詢第一級全部職位 * * @return */ @Override public List<JobTypeVo> queryFirstJobList() { /* * List<JobTypeVo> redisIndustryListResult = redisTemplate.execute(new RedisCallback<List<JobTypeVo>>() { * @Override public List<JobTypeVo> doInRedis(RedisConnection connection) { byte[] industryListList = * connection.get((RedisConstants.JOB_FIRST_LIST).getBytes()); if (null != industryListList && * industryListList.length > 0) { return (List<JobTypeVo>) SerializableObjectUtil.unserialize(industryListList); * } else { return null; } } }); if (null != redisIndustryListResult && !redisIndustryListResult.isEmpty()) { * return redisIndustryListResult; } else { */ final List<JobTypeVo> queryIndustryListResult = dalClient.queryForList("T_JOB_TYPE.SELECT_FIRST_JOB_CODE", null, JobTypeVo.class); /* * if (null != queryIndustryListResult && !queryIndustryListResult.isEmpty()) { redisTemplate.execute(new * RedisCallback<Boolean>() { * @Override public Boolean doInRedis(RedisConnection connection) { * connection.set((RedisConstants.JOB_FIRST_LIST).getBytes(), * SerializableObjectUtil.serialize(queryIndustryListResult)); return true; } }); } */ return queryIndustryListResult; /* } */ } /** * 查詢 對應級別的職位信息 * * @param typeValue * @param jobCode * @return */ @Override public List<JobTypeBean> queryJobTypeList(final int typeValue, final int jobCode) { /* * List<JobTypeBean> redisIndustryListResult = redisTemplate.execute(new RedisCallback<List<JobTypeBean>>() { * @Override public List<JobTypeBean> doInRedis(RedisConnection connection) { byte[] industryListList = * connection.get((RedisConstants.JOB_FIRST_LIST + typeValue + jobCode) .getBytes()); if (null != * industryListList && industryListList.length > 0) { return (List<JobTypeBean>) * SerializableObjectUtil.unserialize(industryListList); } else { return null; } } }); if (null != * redisIndustryListResult && !redisIndustryListResult.isEmpty()) { return redisIndustryListResult; } else { */ Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("typeValue", typeValue); paramMap.put("jobFirstCode", jobCode); final List<JobTypeBean> queryResult = dalClient.queryForList("T_JOB_TYPE.SELECT_BY_JOB_CODE", paramMap, JobTypeBean.class); /* * if (null != queryResult && !queryResult.isEmpty()) { redisTemplate.execute(new RedisCallback<Boolean>() { * @Override public Boolean doInRedis(RedisConnection connection) { * connection.set((RedisConstants.JOB_FIRST_LIST + typeValue + jobCode).getBytes(), * SerializableObjectUtil.serialize(queryResult)); return true; } }); } */ return queryResult; /* } */ } /** * 判斷學校是否 特殊學校 * * @param schoolName 學校名稱 * @param schoolType 學校分類(1:211 暫無其餘) * @return true:是, false:否 * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.isSpecialSchool", key = "new String('commonService.isSpecialSchool')+#schoolName + #schoolType", condition = "null != #schoolName and null !=#schoolType and #schoolName.length() > 0") public boolean isSpecialSchool(String schoolName, int schoolType) { if (StringUtils.isEmpty(schoolName)) { return false; } else { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("schoolName", schoolName); paramMap.put("schoolType", schoolType); Map<String, Object> resultMap = dalClient.queryForMap("T_MY_SPECIAL_SCHOOL.SELECT_BY_NAME_TYPE", paramMap); if (null != resultMap && !resultMap.isEmpty() && resultMap.containsKey("NUM")) { return (long) resultMap.get("NUM") > 0; } else { return false; } } } /** * 根據城市名稱獲取 城市所在 省份名稱 * * @param cityNames * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @Override @Cacheable(value = "commonService.getProvinceByCity", key = "new String('commonService.getProvinceByCity')+#cityNames", condition = "null != #cityNames and #cityNames.length() > 0") public String getProvinceByCity(final String cityNames) { if (StringUtils.isBlank(cityNames)) { return null; } else { String[] cityArr = cityNames.split("、"); Map<String, Object> paramMap = new HashMap<String, Object>(); Map<String, Object> resultMap; String provinceName; List<String> provinceLait = new ArrayList<String>(); for (String cityName : cityArr) { if (StringUtils.isNotBlank(cityName)) { paramMap.put("cityName", cityName); resultMap = dalClient.queryForMap("T_CITY.SELECT_PROVINCE_NAMEBY_CITY_NAME", paramMap); if (null != resultMap && !resultMap.isEmpty() && resultMap.containsKey("provinceName")) { provinceName = String.valueOf(resultMap.get("provinceName")); if (!provinceLait.contains(provinceName)) { provinceLait.add(provinceName); } } } } StringBuffer sb = new StringBuffer(100); if (!provinceLait.isEmpty()) { for (int i = 0; i < provinceLait.size(); i++) { if (i < provinceLait.size() - 1) { sb.append(provinceLait.get(i)).append(","); } else { sb.append(provinceLait.get(i)); } } } return sb.toString(); } }
已queryCityListByParentCode方法爲例:
在這個方法上面有@Cacheable這個註解,這個是spring3.1之後增長的註解緩存標籤,它會根據value = "commonService.queryCityListByParentCode"中value的屬性值去查找咱們配置在spring的xml文件中的name屬性去查找,找到對應的配置文件後,該方法會經過咱們自定義的緩存實現類去實現對應的邏輯,若是對spring註解的意義不清楚的能夠先去了解下spring cache註解的含義。
友情連接:【Spring】2二、Spring緩存註解@Cache使用
做者:王中秋