基于 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缓存的过程了
有不对之处,还望指出 欢迎留言
!