這篇博文主要介紹如何使用SpringAOP + Redis +註解的方式實現緩衝的開發。 一、術語說明 1、SpringAOP AOP(Aspect Oriented Programming),也就是面向方面編程的技術。AOP基於IOC基礎,是對OOP的有益補充。SpringAOP的可配置式,使得代碼幾乎沒有耦合侵入。
2、Redis Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。Redis已經成為NOSQL的典範。 3、註解 採用註解的方式,使得代碼沒有侵入,動態可配置式。 二、思路 在資料沒有變化的時候,採用本機資料,如果資料有了變化,那麼將從資料庫擷取最新資料,並緩衝到本地(可以通過各種方式的緩衝,比如HTML5可以採用localStorge),參照下圖 三、主要程式碼片段 1、pom.xml,用來整合Spring和Redis
<!-- redis 快取資料庫.....start --><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>1.6.1.RELEASE</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.7.3</version></dependency><!-- redis cache related.....end -->
2、Spring-dispather
<aop:aspectj-autoproxy proxy-target-class="true"/>
3、SpringContent,配置Redis串連
<!-- Redis和緩衝配置開始 --><!-- jedis 配置 --><bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /></bean ><!-- redis伺服器中心 --><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.password}" /> --> <property name="timeout" value="${redis.timeout}" ></property></bean ><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="connectionFactory" /> <property name="keySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property></bean ><!-- cache配置 --><bean id="putCache" class="com.ninesky.framework.PutCacheAOP" > <property name="redisTemplate" ref="redisTemplate" /></bean><!-- cache配置 --> <bean id="getCache" class="com.ninesky.framework.GetCacheAOP" > <property name="redisTemplate" ref="redisTemplate" /> </bean> <!-- Redis和緩衝配置結束 -->
4、Redis.properties
#redis中心#綁定的主機地址redis.host=127.0.0.1#指定Redis監聽連接埠,預設連接埠為6379redis.port=6379 #授權密碼(本例子沒有使用)redis.password=#最大空閑數:空閑連結數大於maxIdle時,將進行回收redis.maxIdle=100 #最大串連數:能夠同時建立的“最大連結個數”redis.maxActive=300 #最大等待時間:單位msredis.maxWait=1000 #使用串連時,檢測串連是否成功 redis.testOnBorrow=true #當用戶端閑置多長時間後關閉串連,如果指定為0,表示關閉該功能redis.timeout=10000
5、自訂註解 (1)產生緩衝版本PutCache.java
/** * 自訂註解,在插入、更新或者刪除的時候更新對應的版本 * @author Chenth */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface PutCache {String name() default "";String value() default "";}
(2)擷取緩衝版本GetCache.java
/** * 自訂註解,對於查詢使用緩衝的方法加入該註解 * @author Chenth */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface GetCache {String name() default "";String value() default "";}
6、加入AOP (1)、GetCache對應的切面
@Aspectpublic class GetCacheAOP {private RedisTemplate<Serializable, Object> redisTemplate;ThreadLocal<Long> time=new ThreadLocal<Long>();ThreadLocal<String> tag=new ThreadLocal<String>();@Pointcut("@annotation(com.ninesky.classtao.springaop.annotation.GetCache)")public void getCache(){System.out.println("我是一個切入點");}/** * 在所有標註@getCache的地方切入 * @param joinPoint */@Before("getCache()")public void beforeExec(JoinPoint joinPoint){MethodSignature ms=(MethodSignature) joinPoint.getSignature();Method method=ms.getMethod();String ActionName = method.getAnnotation(GetCache.class).name();String fieldList = method.getAnnotation(GetCache.class).value();for (String field:fieldList.split(",")) {if ("school_id".equals(field))ActionName+="#"+ActionUtil.getSchoolID();else if ("user_id".equals(field))ActionName+="#"+ActionUtil.getUserID();else if ("user_type".equals(field))ActionName+="#"+ActionUtil.getUserType();else ActionName+="#"+ActionUtil.getParameter(field);}ValueOperations<Serializable, Object> operations =redisTemplate.opsForValue();ActionUtil.setCache(true);//如果是第一次取值.則將版本存放到redis資料庫if (operations.get(ActionName)==null) {operations.increment(ActionName, 1);return;}if (operations.get(ActionName).equals(ActionUtil.getParameter("cache_version"))) throw new CacheException("資料沒有更新,可以採用本機資料!");ActionUtil.setCache_version(operations.get(ActionName)+"");}public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {this.redisTemplate = redisTemplate;}}
(2)、PutCache對應的切面
@Aspectpublic class PutCacheAOP {private RedisTemplate<Serializable, Object> redisTemplate;ThreadLocal<Long> time=new ThreadLocal<Long>();ThreadLocal<String> tag=new ThreadLocal<String>();@Pointcut("@annotation(com.ninesky.classtao.springaop.annotation.PutCache)")public void PutCache(){System.out.println("我是一個切入點");}/** * 在所有標註@PutCache的地方切入 * @param joinPoint */@After("PutCache()")public void AfterExec(JoinPoint joinPoint){MethodSignature ms=(MethodSignature) joinPoint.getSignature();Method method=ms.getMethod();String ActionName = method.getAnnotation(PutCache.class).name();String fieldList = method.getAnnotation(PutCache.class).value();for (String field:fieldList.split(",")) ActionName+="#"+ActionUtil.getParameter(field);ValueOperations<Serializable, Object> operations =redisTemplate.opsForValue();operations.increment(ActionName, 1);}public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {this.redisTemplate = redisTemplate;}}
7、最後,在Controller中添加註解
/** * 添加訊息(校園風采、黨建) * @param request */@PutCache(name="newsList",value="school_id,news_code")@RequestMapping(value="/addNews")public @ResponseBody Object addNews(HttpServletRequest request){NewsVO vo = BeanUtil.formatToBean(NewsVO.class);newsService.addNews(vo);newsService.addInformation(vo);return ResponseUtils.sendSuccess(vo);}/** * 擷取訊息列表(Web) * @param request * @return */@GetCache(name="newsList",value="school_id,news_code")@RequestMapping(value="/getNewsListForWeb")public @ResponseBody Object getNewsList(HttpServletRequest request){NewsVO vo = BeanUtil.formatToBean(NewsVO.class);if(IntegerUtil.isEmpty(vo.getSchool_id()))vo.setSchool_id(ActionUtil.getSchoolID());List<NewsVO> list = newsService.getNewsList(vo);return ResponseUtils.sendSuccess(list);}
8、成功了,該方法降低了應用的資料庫請求次數和時間消耗,提高了查詢效率