spring与redis集成之aop整合方案

java使用redis缓存可以使用jedis框架,jedis操作简单,没有什么复杂的东西需要学习,网上资料很多,随便看看就会了.

将spring与redis缓存集成,其实也是使用jedis框架,只不过spring对它进行了一层封装,并将这层封装库命名为spring-data-redis.

下面将要使用spring-data-redis与jedis的jar包,并通过spring的aop功能,将redis缓存无缝无侵入的整合进来.

1.先下载好依赖包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.1.RELEASE</version>
</dependency>
<!-- 还有spring的其它包,这里不一一贴出-->
<dependency>  
    <groupId>org.springframework.data</groupId>  
    <artifactId>spring-data-redis</artifactId>  
    <version>1.4.1.RELEASE</version>  
</dependency> 
<dependency> 
    <groupId>redis.clients</groupId> 
    <artifactId>jedis</artifactId> 
    <version>2.6.0</version> 
</dependency> 

2.再配置spring文件
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="minIdle" value="${redis.minIdle}" />
        <property name="maxIdle" value="${redis.maxIdle}" />  
        <property name="maxTotal" value="${redis.maxActive}" />  
        <property name="maxWaitMillis" value="${redis.maxWait}" />  
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
    </bean>  
    
    <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}" />
        <property name="port" value="${redis.port}" />
        <property name="password" value="${redis.password}" />
        <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>

3.开始编写aop代码
3.1 声明两个注解类,用于定义哪些方法将使用缓存

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Cacheable {
    
    public enum KeyMode{
        DEFAULT,    //只有加了@CacheKey的参数,才加入key后缀中
        BASIC,        //只有基本类型参数,才加入key后缀中,如:String,Integer,Long,Short,Boolean
        ALL;        //所有参数都加入key后缀
    }
    
    public String key() default "";        //缓存key
    public KeyMode keyMode() default KeyMode.DEFAULT;        //key的后缀模式
    public int expire() default 0;        //缓存多少秒,默认无限期
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface CacheKey {}
3.2 创建一个Aop拦截器的处理类,用于拦截加了@Cacheable的方法
@Aspect
@Component
public class CacheableAop {
    
    @Autowired private RedisTemplate redisTemplate;
    
    @Around("@annotation(cache)")
    public Object cached(final ProceedingJoinPoint pjp,Cacheable cache) throws Throwable {
        
        String key=getCacheKey(pjp, cache);
        ValueOperations<String, Object> valueOper=redisTemplate.opsForValue();
        Object value=valueOper.get(key);    //从缓存获取数据
        if(value!=null) return value;        //如果有数据,则直接返回
        
        value = pjp.proceed();        //跳过缓存,到后端查询数据
        if(cache.expire()<=0) {        //如果没有设置过期时间,则无限期缓存
            valueOper.set(key, value);
        } else {                    //否则设置缓存时间
            valueOper.set(key, value,cache.expire(),TimeUnit.SECONDS);
        }
        return value;
    }
    
    /**
     * 获取缓存的key值
     * @param pjp
     * @param cache
     * @return
     */
    private String getCacheKey(ProceedingJoinPoint pjp,Cacheable cache) {
        
        StringBuilder buf=new StringBuilder();
        buf.append(pjp.getSignature().getDeclaringTypeName()).append(".").append(pjp.getSignature().getName());
        if(cache.key().length()>0) {
            buf.append(".").append(cache.key());
        }
        
        Object[] args=pjp.getArgs();
        if(cache.keyMode()==KeyMode.DEFAULT) {
            Annotation[][] pas=((MethodSignature)pjp.getSignature()).getMethod().getParameterAnnotations();
            for(int i=0;i<pas.length;i++) {
                for(Annotation an:pas[i]) {
                    if(an instanceof CacheKey) {
                        buf.append(".").append(args[i].toString());
                        break;
                    }
                }
            }
        } else if(cache.keyMode()==KeyMode.BASIC) {
            for(Object arg:args) {
                if(arg instanceof String) {
                    buf.append(".").append(arg);
                } else if(arg instanceof Integer || arg instanceof Long || arg instanceof Short) {
                    buf.append(".").append(arg.toString());
                } else if(arg instanceof Boolean) {
                    buf.append(".").append(arg.toString());
                }
            }
        } else if(cache.keyMode()==KeyMode.ALL) {
            for(Object arg:args) {
                buf.append(".").append(arg.toString());
            }
        }
        
        return buf.toString();
    }
}
4.使用缓存示例

@Service
@Transactional
public class DemoServiceImpl implements DemoService {
 
    @Autowired private DemoDao demoDao;
 
    public List<Demo> findAll() {
        return demoDao.findAll();
    }
    
    /*
        对get()方法配置使用缓存,缓存有效期为3600秒,缓存的key格式为:{package_name}.DemoServiceImpl.get
        同时为参数配置了@CacheKey后,表示此参数的值将做为key的后缀,此例的key,最终是:{package_name}.DemoServiceImpl.get.{id}
        可以为多个参数配置@CacheKey,拦截器会调用参数的toString()做为key的后缀
        若配置多个@CacheKey参数,那么最终的key格式为:{package_name}.{class_name}.{method}.{arg1}.{arg2}.{...}
     */
    @Cacheable(expire=3600)
    public Demo get(@CacheKey String id) {
        return demoDao.get(id);
    }
 
    public Demo getByName(String name) {
        return demoDao.getByName(name);
    }
}
若为名称相同的方法配置缓存,可以在@Cacheable中加入key属性,追加额外的key后缀
@Cacheable还有一个KeyMode属性,用于配置哪些参数可以追加到key后缀中,
默认取值 DEFAULT:表示只有加了@CacheKey的参数才能追加到key后缀
BASIC:自动将基本类型追加到key后缀,而无需再配置@CacheKey
ALL:自动将所有参数追加到lkey后缀,而无需再配置@CacheKey

原文:https://blog.csdn.net/zhanngle/article/details/41077423 
 

猜你喜欢

转载自blog.csdn.net/weixin_40205234/article/details/86686015