項目搭建過程當中,緩存邏輯在某種程度上,是必不可少了,本文在以前的Maven多模塊項目的基礎上,在service層使用AOP增長了redis緩存邏輯。 java
具體代碼已上傳git : http://git.oschina.net/alexgaoyh/MutiModule-parent git
具體效果,能夠直接執行junit單元測試便可,文章並不針對這些測試進行截圖操做了。 redis
下面備註上一些須要注意的事項: spring
<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類型。具體實現以下: express
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 的選取,就是使用 json
pjp.toShortString() + "_" + args[0]
這樣就能避免key的衝突 緩存
而且須要注意下面這一段代碼: json->object 的時候,是須要知道當前的數據類型的,獲取service層請求方法的返回方法。 app
//返回值類型, add 方法將對應的Object 轉換爲 json 保存到 緩存中,在 get方法的時候,經過下面註釋的返回值類型,將json 轉換爲對應的object //pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes()).getReturnType();