一、术语说明
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监听端口,默认端口为6379
 redis.port=6379 
 #授权密码(本例子没有使用)
 redis.password=
 #最大空闲数:空闲链接数大于maxIdle时,将进行回收
 redis.maxIdle=100  
 #最大连接数:能够同时建立的“最大链接个数”
 redis.maxActive=300  
 #最大等待时间:单位ms
 redis.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对应的切面

@Aspect
 public 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对应的切面
 @Aspect
 public 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、成功了,该方法降低了应用的数据库请求次数和时间消耗,提高了查询效率