摘要: 主要針對Dao層的一些數據庫查詢的操作,數據實時性不強,直接加入緩存。當緩存中有的時候,就使用緩存中的數據。這樣的方法,最終僅僅使用一個注解實現。對於之前的hibernate二級緩存使用,比較陌生。比如是否支持Redis或者可以自己開發支持。是否支持針對部分需要加入緩存的方法配置,而不是所有的hibernate實體都加入緩存。可能我這種方法對於二級緩存來說,拋開代碼差距,也是殊途同歸的東西。
這幾天工作中,突然遇到了對於有些個實體類,需要被緩存起來。但是這些個實體類數目龐大, 初始化加載的話,太耗費時間。所以初步的方案就是先查緩存,緩存沒有就查詢數據庫,查完數據庫再放入緩存。同時也方便設置過期時間。
但是針對目前的項目來說,Dao是作為獨立的Maven Module,Redis也是獨立的Maven Module,相互耦合的話,代碼變得難以維護,結構不清晰。所以引入了注解,然后在Redis項目中,針對注解做AOP,這樣的話,沒有用到緩存的項目,就可以忽略這樣的注解。如果用到了,可以自動加入緩存。
注解代碼:
package com.ns.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit; /** * 只能注解dao里面對應的get方法,傳遞的參數作為hashkey,返回的值作為value * @author Han */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Cached { /** * redis key * @return */ String key(); /** * 過期時間,默認為0即永不過期 * @return */ long timeout() default 0L; /** * 時間單位,默認為秒 * @return */ TimeUnit timeunit() default TimeUnit.SECONDS; }
Aop切面代碼
package com.ns.redis.aop; import java.lang.reflect.Method; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.ArrayUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import com.ns.annotation.Cached; import com.ns.redis.dao.base.BaseRedisDao; /** * 對dao的getbean的緩存處理 * @author Han */ @Aspect @Component public class AutoRedisCached extends BaseRedisDao<String, Object>{ /* * 約束任意包下的包含Dao的類的任意方法,並且被cached注解 */ @Pointcut("execution(* *..*Dao*.*(*,..) && @annotation(com.ns.annotation.Cached))") private void cacheMethod(){} @Around("cacheMethod()") public Object doArround(ProceedingJoinPoint pjp) throws Throwable{ Object[] args = pjp.getArgs(); //定義序列化器 final RedisSerializer<String> keySerializer = getKeySerializer(); final RedisSerializer<Object> hashValueSerializer = getHashValueSerializer(); final RedisSerializer<Object> hashKeySerializer = getHashKeySerializer(); //序列化參數,作為hashkey byte [] hashkeyBytesTmp = null; if(args.length == 1){ hashkeyBytesTmp = hashKeySerializer.serialize(args[0]); }else{ hashkeyBytesTmp = new byte[0]; for(Object arg : args){ hashkeyBytesTmp = ArrayUtils.addAll(hashkeyBytesTmp, hashKeySerializer.serialize(arg)); } } final byte [] hashkeyBytes = hashkeyBytesTmp; MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); Method method = methodSignature.getMethod(); final Cached cacheinfo = method.getAnnotation(Cached.class); Object obj= null; obj = execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { byte [] tmp = connection.hGet(keySerializer.serialize(cacheinfo.key()), hashkeyBytes); return hashValueSerializer.deserialize(tmp); } }); if(obj == null){ final Object objReturn = pjp.proceed(); if(objReturn != null){ execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { return connection.hSet(keySerializer.serialize(cacheinfo.key()), hashkeyBytes,hashValueSerializer.serialize(objReturn)); } }); if(cacheinfo.timeout()>0){ expire(cacheinfo.key(), cacheinfo.timeout(), cacheinfo.timeunit()); } } obj = objReturn; } //從dao獲取 return obj; } }