项目搭建过程中,缓存逻辑在某种程度上,是必不可少了,本文在之前的Maven多模块项目的基础上,在service层使用AOP增加了redis缓存逻辑。
具体代码已上传git : http://git.oschina.net/alexgaoyh/MutiModule-parent
具体效果,可以直接执行junit单元测试即可,文章并不针对这些测试进行截图操作了。
下面备注上一些需要注意的事项:
<spring-data-redis>1.4.1.RELEASE</spring-data-redis>
<redis.clients.jedis>2.6.0</redis.clients.jedis>
<org.codehaus.jackson>1.9.13</org.codehaus.jackson>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring-data-redis}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.clients.jedis}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>${org.codehaus.jackson}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${org.codehaus.jackson}</version>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="minIdle" value="1" />
<property name="maxIdle" value="5" />
<property name="maxTotal" value="5" />
<property name="maxWaitMillis" value="10001" />
<property name="testOnBorrow" value="false" />
</bean>
<bean id="jedisConnFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="192.168.0.135" />
<property name="port" value="6379" />
<property name="password" value="" />
<property name="usePool" value="true" />
<property name="poolConfig" ref="poolConfig" />
</bean>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory" />
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean>
</beans>
<!-- redis 缓存控制层 begin-->
<bean id="redisHandler" class="com.alexgaoyh.MutiModule.aop.redis.RedisAdvice" >
<property name="redisTemplate" ref="redisTemplate"></property>
</bean>
<aop:config>
<aop:aspect id="aspect" ref="redisHandler">
<!-- 在多个表达式之间使用 || , or 表示 或 ,使用 && , and 表示 与 , ! 表示 非 -->
<!-- <aop:pointcut id="pointRedisHandler" expression="execution(* com.alexgaoyh.MutiModule.service.*.*.selectByPrimaryKey(..))
or execution(* com.alexgaoyh.MutiModule.service.*.*.insert(..)) "/> -->
<aop:pointcut id="pointRedisHandler" expression="execution(* com.alexgaoyh.MutiModule.service.*.*.selectByPrimaryKey(..))"/>
<!-- <aop:before method="doBefore" pointcut-ref="pointRedisHandler"/>
<aop:after method="doAfter" pointcut-ref="pointRedisHandler"/> -->
<aop:around method="doAround" pointcut-ref="pointRedisHandler"/>
<!-- <aop:after-returning method="doReturn" pointcut-ref="pointRedisHandler"/>
<aop:after-throwing method="doThrowing" throwing="ex" pointcut-ref="pointRedisHandler"/> -->
</aop:aspect>
</aop:config>
<!-- redis 缓存控制层 end-->
在书写过程中,有一个地方需要注意,因为使用redis进行set get操作的时候,value部分是存放的json串,那么,在json->object的时候,就需要获取到转换的object类型。具体实现如下:
package com.alexgaoyh.MutiModule.aop.redis;
import java.io.Serializable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.codehaus.jackson.map.ObjectMapper;
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.data.redis.serializer.RedisSerializer;
public class RedisAdvice {
protected RedisTemplate<Serializable, Serializable> redisTemplate;
public RedisTemplate<Serializable, Serializable> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(
RedisTemplate<Serializable, Serializable> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 手动控制调用核心业务逻辑,以及调用前和调用后的处理,
*
* 注意:当核心业务抛异常后,立即退出,转向After Advice
* 执行完毕After Advice,再转到Throwing Advice
* Object[] getArgs:返回目标方法的参数 Signature getSignature:返回目标方法的签名
* Object getTarget:返回被织入增强处理的目标对象 Object getThis:返回AOP框架为目标对象生成的代理对象
* @param pjp
* @return
* @throws Throwable
*/
private Object doAround(ProceedingJoinPoint pjp) throws Throwable {
//返回值类型, add 方法将对应的Object 转换为 json 保存到 缓存中,在 get方法的时候,通过下面注释的返回值类型,将json 转换为对应的object
//pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes()).getReturnType();
// 输出 execution(DemoServiceImpl.insert(..))
//pjp.toShortString();
//跳转到这里的方法名 形如 insert selectByPrimaryKey
//pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes()).getName();
String baseKey = pjp.toShortString();
Object[] args = pjp.getArgs();
//下面这个if判断,针对的是selectByPrimaryKey(Integer id)方法,即 有入参,并且入参的第一个类型为Integer
//后期如果有新增方法的话,是需要这里进行数据判断的,可以针对不同的方法,使用不同的切面
if (args != null && args.length > 0 && args[0].getClass() == Integer.class) {
System.out.println("key = " + baseKey + "_" + args[0]);
ObjectMapper mapper = new ObjectMapper();
Object obj = this.get(baseKey + "_" +args[0]);
if(obj == null) {
//调用核心逻辑
Object retVal = pjp.proceed();
this.add(baseKey + "_" +args[0], mapper.writeValueAsString(retVal));
System.out.println("缓存为空");
return retVal;
}else {
System.out.println("缓存不为空");
obj = mapper.readValue(obj.toString(), pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),
((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes()).getReturnType());
return obj;
}
}
return null;
}
/**
* 添加
* @param key
* @param value
*/
public void add(final String key, final String value) {
if(redisTemplate != null) {
redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
connection.set(
redisTemplate.getStringSerializer().serialize(key),
redisTemplate.getStringSerializer().serialize(value));
return null;
}
});
}
}
/**
* 根据key获取对象
*/
public Object get(final String key) {
return redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] keyByte = redisTemplate.getStringSerializer().serialize(key);
byte[] value = connection.get(keyByte);
String str = redisTemplate.getStringSerializer().deserialize(value);
return str;
}
} );
}
/**
* 获取 RedisSerializer
*
*/
protected RedisSerializer<String> getRedisSerializer() {
return redisTemplate.getStringSerializer();
}
}

key 的选取,就是使用
pjp.toShortString() + "_" + args[0]
这样就能避免key的冲突
并且需要注意下面这一段代码: json->object 的时候,是需要知道当前的数据类型的,获取service层请求方法的返回方法。
//返回值类型, add 方法将对应的Object 转换为 json 保存到 缓存中,在 get方法的时候,通过下面注释的返回值类型,将json 转换为对应的object
//pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes()).getReturnType();