ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存

 基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存

  如何能更简洁的利用aop实现redis缓存,话不多说,上demo

  需求:
    数据查询时每次都需要从数据库查询数据,数据库压力很大,查询速度慢,
    因此设置缓存层,查询数据时先从redis中查询,如果查询不到,则到数据库中查询
    然后将数据库中查询的数据放到redis中一份,下次查询时就能直接从redis中查到,不需要查询数据库了

  实现过程:

      先搭建ssm的架子,引入redis,编写redis 缓存方法 RedisCache.java以及序列化用到的工具类


      自定义注解 getCache 目的:
            被这个注解标记的方法实现aop
            防止redis key重复

     编写切面
      @Aspect

        @Pointcut("@annotation(com.spring_redis.cache.GetCache)")
        切入点为自定义注解 即每个被该注解标记的方法实现通知

        @Around("getCache()")
        利用环绕通知
          过程: 查询时,先查询redis 如果存在key-value,则返回不查询
          如果不存在,则查询数据库,之后将查询到的数据存入到redis缓存中
          redis key格式:为了防止key冲突,创建的key格式为:
          包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(integer).123"

  目录结构

      

  maven依赖: 

    

  1 <properties>
  2             <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  3              <!-- spring版本号 -->
  4             <spring.version>4.0.6.RELEASE</spring.version>
  5             <!-- mybatis版本号 -->
  6             <mybatis.version>3.2.7</mybatis.version>
  7         </properties>
  8   <dependencies>
  9   
 10           <!-- spring核心包 -->
 11         <!-- springframe start -->
 12         <dependency>
 13             <groupId>org.springframework</groupId>
 14             <artifactId>spring-core</artifactId>
 15             <version>${spring.version}</version>
 16         </dependency>
 17 
 18         <dependency>
 19             <groupId>org.springframework</groupId>
 20             <artifactId>spring-web</artifactId>
 21             <version>${spring.version}</version>
 22         </dependency>
 23 
 24         <dependency>
 25             <groupId>org.springframework</groupId>
 26             <artifactId>spring-oxm</artifactId>
 27             <version>${spring.version}</version>
 28         </dependency>
 29 
 30         <dependency>
 31             <groupId>org.springframework</groupId>
 32             <artifactId>spring-tx</artifactId>
 33             <version>${spring.version}</version>
 34         </dependency>
 35 
 36         <dependency>
 37             <groupId>org.springframework</groupId>
 38             <artifactId>spring-aop</artifactId>
 39             <version>${spring.version}</version>
 40         </dependency>
 41 
 42         <dependency>
 43             <groupId>org.springframework</groupId>
 44             <artifactId>spring-jdbc</artifactId>
 45             <version>${spring.version}</version>
 46         </dependency>
 47 
 48         <dependency>
 49             <groupId>org.springframework</groupId>
 50             <artifactId>spring-webmvc</artifactId>
 51             <version>${spring.version}</version>
 52         </dependency>
 53 
 54         <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
 55         <dependency>
 56             <groupId>org.springframework</groupId>
 57             <artifactId>spring-context-support</artifactId>
 58             <version>4.0.6.RELEASE</version>
 59         </dependency>
 60 
 61         <dependency>
 62             <groupId>org.springframework</groupId>
 63             <artifactId>spring-context</artifactId>
 64             <exclusions>
 65                 <exclusion>
 66                     <groupId>commons-logging</groupId>
 67                     <artifactId>commons-logging</artifactId>
 68                 </exclusion>
 69             </exclusions>
 70             <version>${spring.version}</version>
 71         </dependency>
 72         <dependency>
 73             <groupId>org.springframework</groupId>
 74             <artifactId>spring-test</artifactId>
 75             <version>${spring.version}</version>
 76         </dependency>
 77         <!-- springframe end -->
 78 
 79           <!-- aop注解  -->
 80           
 81         <dependency>
 82             <groupId>org.aspectj</groupId>
 83             <artifactId>aspectjrt</artifactId>
 84             <version>1.6.12</version>
 85         </dependency>
 86         <dependency>
 87             <groupId>org.aspectj</groupId>
 88             <artifactId>aspectjweaver</artifactId>
 89             <version>1.6.12</version>
 90         </dependency>
 91         <dependency>
 92             <groupId>cglib</groupId>
 93             <artifactId>cglib</artifactId>
 94             <version>2.2</version>
 95         </dependency>
 96           
 97           <!-- mysql驱动包 -->
 98         <dependency>
 99             <groupId>mysql</groupId>
100             <artifactId>mysql-connector-java</artifactId>
101             <version>5.1.31</version>
102         </dependency>
103           
104           <!-- dbcp2连接池 -->
105         <dependency>
106             <groupId>org.apache.commons</groupId>
107             <artifactId>commons-dbcp2</artifactId>
108             <version>2.0.1</version>
109         </dependency>
110 
111         <!-- json数据 -->
112         <dependency>
113             <groupId>org.codehaus.jackson</groupId>
114             <artifactId>jackson-mapper-asl</artifactId>
115             <version>1.9.13</version>
116         </dependency>
117 
118           <!-- mybatis核心包 -->
119         <dependency>
120             <groupId>org.mybatis</groupId>
121             <artifactId>mybatis</artifactId>
122             <version>${mybatis.version}</version>
123         </dependency>
124         <!-- mybatis/spring包 -->
125         <dependency>
126             <groupId>org.mybatis</groupId>
127             <artifactId>mybatis-spring</artifactId>
128             <version>1.2.2</version>
129         </dependency>
130   
131           <dependency>  
132             <groupId>org.springframework.data</groupId>  
133             <artifactId>spring-data-redis</artifactId>  
134             <version>1.6.1.RELEASE</version>  
135         </dependency>  
136          <dependency>  
137             <groupId>redis.clients</groupId>  
138             <artifactId>jedis</artifactId>  
139             <version>2.7.3</version>  
140         </dependency> 
141   
142           <!-- servlet-api -->
143         <dependency>
144             <groupId>javax.servlet</groupId>
145             <artifactId>javax.servlet-api</artifactId>
146             <version>3.0.1</version>
147             <scope>provided</scope>
148         </dependency>
149         <dependency>
150             <groupId>javax.servlet.jsp</groupId>
151             <artifactId>jsp-api</artifactId>
152             <version>2.2</version>
153             <scope>provided</scope>
154         </dependency>
155         <!-- servlet-api end -->
156   
157           <dependency>
158             <groupId>log4j</groupId>
159             <artifactId>log4j</artifactId>
160             <version>1.2.17</version>
161         </dependency>
162   </dependencies>
163   

 

   这里只给出redis 的相关配置

    在applicationContext-dao.xml 里添加

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
 6     xmlns:util="http://www.springframework.org/schema/util"
 7     xsi:schemaLocation="http://www.springframework.org/schema/beans
 8     http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
 9     http://www.springframework.org/schema/context
10     http://www.springframework.org/schema/context/spring-context-3.2.xsd
11     http://www.springframework.org/schema/tx
12     http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
13     http://www.springframework.org/schema/aop
14     http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
15     http://www.springframework.org/schema/util 
16     http://www.springframework.org/schema/util/spring-util-3.2.xsd">
17         
18         
19         <!-- 加载db.properties文件中的内容,db.properties文件中key命名要有一定的特殊规则 -->
20         <context:property-placeholder location="classpath:properties/db.properties" />
21         
22         
23         
24         <!-- 配置数据源 ,dbcp -->
25         
26         <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
27         <property name="driverClassName" value="${jdbc.driver}" />
28         <property name="url" value="${jdbc.url}" />
29         <property name="username" value="${jdbc.username}" />
30         <property name="password" value="${jdbc.password}" />
31         </bean>
32 
33 
34         <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
35         p:dataSource-ref="dataSource" p:configLocation="classpath:mybatis/sqlMapConfig.xml"
36         ></bean>
37               <!-- Redis和缓存配置开始 -->  
38             <!-- jedis 配置 -->  
39             <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >  
40                   <property name="maxIdle" value="100" />  
41                   <property name="maxWaitMillis" value="1000" />  
42                   <property name="testOnBorrow" value="true" />  
43             </bean >  
44             
45             <!-- redis连接池 -->
46                 <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
47                     <constructor-arg name="poolConfig" ref="poolConfig"/>
48                     <constructor-arg name="host" value="127.0.0.1"/>
49                     <constructor-arg name="port" value="6379"/>
50                 </bean>
51 
52               <!-- redis服务器中心 -->  
53             <bean id="connectionFactory"  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >  
54                   <property name="poolConfig" ref="poolConfig" />  
55                   <property name="port" value="6379" />  
56                   <property name="hostName" value="127.0.0.1" />  
57                   <!-- <property name="password" value="${redis.password}" /> --> 
58                   <property name="timeout" value="10000" ></property>  
59             </bean >  
60             <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >  
61                   <property name="connectionFactory" ref="connectionFactory" />  
62                   <property name="keySerializer" >  
63                       <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />  
64                   </property>  
65                   <property name="valueSerializer" >  
66                       <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />  
67                   </property>  
68             </bean > 
69             
70             
71             
72             
73             
74             <!-- cache配置 -->  
75             <bean id="putCache" class="com.spring_redis.cache.PutCacheAOP" >  
76                   <property name="redisTemplate" ref="redisTemplate" />
77             </bean>  
78             
79             <!-- cache配置 -->  
80              <bean id="getCache" class="com.spring_redis.cache.GetCacheAOP" >  
81                    <property name="redisTemplate" ref="redisTemplate" />
82              </bean>  
83              
84              <!-- Redis和缓存配置结束 -->
85             
86             <!-- mapper扫描器 -->
87             <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
88                 <!-- 扫描包路径,如果需要扫描多个包,中间使用半角逗号隔开 -->
89                 <property name="basePackage" value="com.spring_redis.mapper"></property>
90                 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
91             </bean>
92             
93             <bean id="roomservice" class="com.spring_redis.service.impl.RoomServiceImpl" >  
94                    
95              </bean>  
96             
97 </beans>

 

  springmvc.xml

 1 <beans xmlns="http://www.springframework.org/schema/beans"
 2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
 3     xmlns:context="http://www.springframework.org/schema/context"
 4     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
 7         http://www.springframework.org/schema/mvc 
 8         http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
 9         http://www.springframework.org/schema/context 
10         http://www.springframework.org/schema/context/spring-context-3.2.xsd 
11         http://www.springframework.org/schema/aop 
12         http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
13         http://www.springframework.org/schema/tx
14         http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
15 
16     <aop:aspectj-autoproxy proxy-target-class="true"/>
17 
18     <!-- 可以扫描controller、service、... 这里让扫描controller,指定controller的包 com.ssm.controlle -->
19     <context:component-scan base-package="com.spring_redis">
20     </context:component-scan>
21     <!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置 mvc:annotation-driven默认加载很多的参数绑定方法 -->
22     <mvc:annotation-driven />
23     <!-- 视图解析器 解析jsp解析,默认使用jstl标签,classpath下的得有jstl的包 -->
24     <bean
25         class="org.springframework.web.servlet.view.InternalResourceViewResolver">
26         <!-- 配置jsp路径的前缀 <property name="prefix" value="/jsp/"/> -->
27         <!-- 配置jsp路径的后缀 -->
28         <property name="suffix" value=".jsp" />
29     </bean>
30 
31     
32 </beans>

   <aop:aspectj-autoproxy proxy-target-class="true"/>  开启注解这个一定要写到springmvc.xml里,否则注解会不起作用

 

  那重点开始了

  创建自定义注解

 1 /** 
 2  * 自定义注解,对于查询使用缓存的方法加入该注解 
 3  * @author Chenth 
 4  */  
 5 @Retention(RetentionPolicy.RUNTIME)  
 6 @Target({ElementType.METHOD})  
 7 public @interface GetCache {  
 8     String name() default "";  
 9     String value() default "";  
10 }   

  被这个自定义注解所标记的方法将实现下面的切面

 

  配置切面

  1 package com.spring_redis.cache;
  2 
  3 import java.io.Serializable;
  4 import java.lang.reflect.Method;
  5 
  6 
  7 import org.aspectj.lang.JoinPoint;
  8 import org.aspectj.lang.ProceedingJoinPoint;
  9 import org.aspectj.lang.annotation.Around;
 10 import org.aspectj.lang.annotation.Aspect;
 11 import org.aspectj.lang.annotation.Pointcut;
 12 import org.aspectj.lang.reflect.MethodSignature;
 13 import org.springframework.beans.factory.annotation.Autowired;
 14 import org.springframework.data.redis.core.RedisTemplate;
 15 import org.springframework.stereotype.Component;
 16 
 17 import com.spring_redis.util.RedisCache;
 18 
 19 @Component
 20 @Aspect
 21 public class GetCacheAOP  {  
 22       
 23     @Autowired
 24     private RedisTemplate<Serializable, Object> redisTemplate;
 25     
 26     private RedisCache redisCache = new RedisCache();
 27   
 28       
 29     @Pointcut("@annotation(com.spring_redis.cache.GetCache)")  
 30     public void getCache(){
 31         System.out.println("我是一个切入点");  
 32     }  
 33     
 34     /** 
 35      * 在所有标注@getCache的地方切入 
 36      * @param joinPoint 
 37      */
 38     @Around("getCache()")
 39     public Object beforeExec(ProceedingJoinPoint joinPoint){  
 40         
 41         
 42         //前置:到redis中查询缓存
 43         System.out.println("调用从redis中查询的方法...");
 44         
 45         //redis中key格式:    id
 46         String redisKey = getCacheKey(joinPoint);
 47         
 48         //获取从redis中查询到的对象
 49         Object objectFromRedis = redisCache.getDataFromRedis(redisKey);
 50         
 51         //如果查询到了
 52         if(null != objectFromRedis){
 53             System.out.println("从redis中查询到了数据...不需要查询数据库");
 54             return objectFromRedis;
 55         }
 56         
 57         System.out.println("没有从redis中查到数据...");
 58         
 59         //没有查到,那么查询数据库
 60         Object object = null;
 61         try {
 62             object = joinPoint.proceed();
 63         } catch (Throwable e) {
 64             
 65             e.printStackTrace();
 66         }
 67         
 68         System.out.println("从数据库中查询的数据...");
 69         
 70         //后置:将数据库中查询的数据放到redis中
 71         System.out.println("调用把数据库查询的数据存储到redis中的方法...");
 72         
 73         redisCache.setDataToRedis(redisKey, object);
 74         System.out.println("redis中的数据..."+object.toString());
 75         //将查询到的数据返回
 76         return object;
 77     }
 78     
 79     /**
 80      * 根据类名、方法名和参数值获取唯一的缓存键
 81      * @return 格式为 "包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(int).123"
 82      */
 83    
 84     @SuppressWarnings("unused")
 85     private String getCacheKey(ProceedingJoinPoint joinPoint) {
 86     
 87     
 88         MethodSignature ms=(MethodSignature) joinPoint.getSignature();  
 89         Method method=ms.getMethod();  
 90         String ActionName = method.getAnnotation(GetCache.class).name();  
 91         String fieldList = method.getAnnotation(GetCache.class).value();  
 92         //System.out.println("签名是"+ms.toString());
 93         for (String field:fieldList.split(","))   
 94              ActionName +="."+field;
 95     
 96         //先获取目标方法参数
 97         String id = null;
 98         Object[] args = joinPoint.getArgs();
 99         if (args != null && args.length > 0) {
100             id = String.valueOf(args[0]);
101         }
102         
103         ActionName += "="+id;
104         String redisKey = ms+"."+ActionName;
105         return redisKey;
106     }
107     
108     
109     public void setRedisTemplate(  
110             RedisTemplate<Serializable, Object> redisTemplate) {  
111         this.redisTemplate = redisTemplate;  
112     }
113 }  

  

 @Pointcut("@annotation(com.spring_redis.cache.GetCache)") 这个切入点的作用是
                                      在所有标注@getCache的地方切入 
 @Around("getCache()")这里用的是后置通知,即查询之前先查询redis,如果有数据就返回数据,没有就穿透的数据库查询数据,之后再缓存到redis中

 

  这里并没有太多的讲解配置ssm框架,可能后续会写关于spring+springmvc+mybatis的框架整合

  编写mapper层,service层,controller层

  mapper

/**
 * 
 * @author     cmy
 * @date     2016-10-22
 * @description 持久化
 */

public interface RoomMapper {

    @Insert("insert into room(roomName,address) values(#{roomName},#{addRess})")
    int insert(Room room);

    @Select("select * from room where id=#{id}")
    public Room selectByPrimaryKey(@Param("id")Integer id);

}

  service

 1 /**
 2  * 
 3  * @author     cmy
 4  * @date     2016-10-22
 5  * @description test
 6  */
 7 public interface RoomService {
 8     
 9     
10     int insert(Room room)throws Exception;
11     
12     
13     Room selectByPrimaryKey(Integer id)throws Exception;
14     
15 }
16 
17 // 实现
18 /**
19  * @author         cmy
20  * @date         2016-10-22
21  * @description  test 实现
22  */
23 public class RoomServiceImpl implements RoomService{
24 
25     @Autowired
26     private RoomMapper mapper;
27     
28     @Override
29     public int insert(Room room) throws Exception {
30         
31         return mapper.insert(room);
32     }
33 
34     @Override
35     public Room selectByPrimaryKey(Integer id) throws Exception {
36         
37         return mapper.selectByPrimaryKey(id);
38     }
39 
40     
41 }

   controller

/**
 * 
 * @author     cmy
 * @date     2016-10-22
 * @description test controller
 */

@Controller
@RequestMapping("room")
public class RoomController {

    @Autowired
    private RoomService  roomService;
    
    @GetCache(name="room",value="id")
    @RequestMapping("selectByPrimaryKey")
    public @ResponseBody Object roomList(Integer id) throws Exception{  
        System.out.println("已查询到数据,准备缓存到redis...  "+roomService.selectByPrimaryKey(id).getRoomName());
        return roomService.selectByPrimaryKey(id);
    }
    

}

  缓存要用到的工具类  RedisCache

 1 public class RedisCache {
 2     
 3     @Autowired
 4     private JedisPool jedisPool = new JedisPool();
 5     
 6 
 7     //从redis缓存中查询,反序列化
 8     public Object getDataFromRedis(String redisKey){
 9         //查询
10         Jedis jedis = jedisPool.getResource();
11         byte[] result = jedis.get(redisKey.getBytes());
12         
13         //如果查询没有为空
14         if(null == result){
15             return null;
16         }
17         
18         //查询到了,反序列化
19         return SerializeUtil.unSerialize(result);
20     }
21     
22     //将数据库中查询到的数据放入redis
23     public void setDataToRedis(String redisKey, Object obj){
24         
25         //序列化
26         byte[] bytes = SerializeUtil.serialize(obj);
27         
28         //存入redis
29         Jedis jedis = jedisPool.getResource();
30         String success = jedis.set(redisKey.getBytes(), bytes);
31         
32         if("OK".equals(success)){
33             System.out.println("数据成功保存到redis...");
34         }
35     }
36 }

  缓存要用到的序列化和反序列化工具

 1 /**
 2  * 
 3  * @Description: 序列化反序列化工具
 4  */
 5 public class SerializeUtil {
 6     /**
 7      * 
 8      * 序列化
 9      */
10     public static byte[] serialize(Object obj){
11         
12         ObjectOutputStream oos = null;
13         ByteArrayOutputStream baos = null;
14         
15         try {
16             //序列化
17             baos = new ByteArrayOutputStream();
18             oos = new ObjectOutputStream(baos);
19             
20             oos.writeObject(obj);
21             byte[] byteArray = baos.toByteArray();
22             return byteArray;
23             
24         } catch (IOException e) {
25             e.printStackTrace();
26         }    
27         return null;
28     }
29     
30     /**
31      * 
32      * 反序列化
33      * @param bytes
34      * @return
35      */
36     public static Object unSerialize(byte[] bytes){
37         
38         ByteArrayInputStream bais = null;
39         
40         try {
41             //反序列化为对象
42             bais = new ByteArrayInputStream(bytes);
43             ObjectInputStream ois = new ObjectInputStream(bais);
44             return ois.readObject();
45             
46         } catch (Exception e) {
47             e.printStackTrace();
48         }
49         return null;
50     }
51 }

 

 以上就是利用aop+自定义注解实现 redis缓存的过程了

      有不对之处,还望指出 欢迎留言

有参考到的文章:http://www.cnblogs.com/mrlinfeng/p/5857775.html

        http://blog.csdn.net/chentian610/article/details/51012789

posted @ 2016-10-23 20:58  叫我明羽  阅读(13131)  评论(0编辑  收藏  举报