标签:
项目搭建过程中,缓存逻辑在某种程度上,是必不可少了,本文在之前的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();
标签:
原文地址:http://my.oschina.net/alexgaoyh/blog/410965