用Spring Aop 实现注解Dao层的自动Spring Redis缓存

摘要: 主要针对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;
    }
}
时间: 2024-10-21 23:26:10

用Spring Aop 实现注解Dao层的自动Spring Redis缓存的相关文章

Spring AOP基于注解的“零配置”方式

Spring AOP基于注解的“零配置”方式: Spring的beans.xml中 <!-- 指定自动搜索Bean组件.自动搜索切面类 --> <context:component-scan base-package="org.crazyit.app.service,org.crazyit.app.aspect"> <context:include-filter type="annotation" expression="or

spring AOP自定义注解方式实现日志管理

转:spring AOP自定义注解方式实现日志管理 今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven />     <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->     <conte

spring aop + xmemcached 配置service层缓存策略

Memcached 作用与使用 基本介绍 1,对于缓存的存取方式,简言之,就是以键值对的形式将数据保存在内存中.在日常业务中涉及的操作无非就是增删改查.加入缓存机制后,查询的时候,对数据进行缓存,增删改的时候,清除缓存即可.这其中对于缓存的闭合就非常重要,如果缓存没有及时得到更新,那用户就会获取到过期数据,就会产生问题. 2,对于单一业务的缓存管理(数据库中只操作单表),只需生成一个key,查询时,使用key,置入缓存:增删改时,使用key,清除缓存.将key与表绑定,操作相对简单. 3,但是在

spring AOP自定义注解 实现日志管理

今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven />     <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->     <context:component-scan base-pac

spring AOP + 自定义注解实现权限控制小例子

今天看了一下黑马程序员的视频,上面讲到一个使用spring AOP + 自定义注解的方式来实现权限控制的一个小例子,个人觉得还是可以借鉴,整理出来与大家分享. 需求:service层有一些方法,这些方法需要不同的权限才能访问. 实现方案:自定义一个PrivilegeInfo的注解,使用这个注解为service层中的方法进行权限配置,在aop中根据PrivilegeInfo注解的值,判断用户是否拥有访问目标方法的权限,有则访问目标方法,没有则给出提示. 关键技术:自定义注解及注解解析,spring

(转)利用Spring AOP自定义注解解决日志和签名校验

一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: boolean isValid = accountService.validSignature(appid, signature, client_signature); if (!isValid) return ErrorUtil.buildError(ErrorUtil.ERR_CODE_COM

利用Spring AOP自定义注解解决日志和签名校验

转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: boolean isValid = accountService.validSignature(appid, signature, client_signature); if (!

spring aop 日志 注解

前提:接上篇:spring aop 日志 一:无新增依赖包 二:修改切面类(aop.Log4jHandlerAOP.java) 三:修改applicationContext.xml 1:扫描注入容器新增aop(base-package="dao,service,aop") 2:支持AOP注解(<aop:aspectj-autoproxy/>) 3:去除传统AOP的配置以及切面类的注入 ps: 1:applicationContext.xml <?xml version

Spring AOP +EHcache为Service层方法增加缓存

在铁科院做了一个关于医保报销的项目,在这个个系统中大量使用了下拉列表框,系统主要是给机关单位使用而且都是一些干部退休了啥的,年龄都比较大不愿意自己输入东西,因此界面上的很多值都是下拉列表框从数据字典表里面加载出来. 如此以来字典表的数据量变的越来越大,在一个界面上往往需要频繁的与字典表交互,觉的很影响性能于是我们增加了缓存,即为service层中的指定方法缓存功能,具体实现是利用Spring AOP+EHcache来做. 第一次执行某个方法的时候会去数据库里面查询,当第二次执行该方法时就会去从缓