基于AOP配置的Spring缓存

来源:互联网 发布:成都室内设计公司 知乎 编辑:程序博客网 时间:2024/04/26 01:30

等从3.1开始,Spring引入对Cache的支持,Spring Cache可以通过@Cacheable注解方式,也可以通过AOP配置方式实现,注解方式在之前的文章中已经介绍过,这里介绍通过AOP配置方式实现

相比于注解方式,AOP配置优点在于对业务代码的零侵入性,不需要在业务代码中添加任何与缓存有关的代码,对于需要缓存的方法集中管理,方便维护;而对注解式的实现方式,至少需要在方法或类上增加如@CacheEable等关键字,随着缓存方法的不段增加,这种注解分散在项目中的各个文件的各个方法上,维护起来很困难,因此相比于注解方式,个人更倾向于AOP配置

Redis的服务端配置前面已经介绍过这里也不再介绍,下面看在Spring中,缓存的配置

1、增加redis.properties的配置文件

redis.sentinel.host1=127.0.0.1redis.sentinel.port1=26379redis.sentinel.host2=127.0.0.1redis.sentinel.port2=26479redis.pool.maxTotal=1024 redis.pool.maxIdle=200 redis.pool.maxWaitMillis=1000 redis.pool.testOnBorrow=true redis.pool.timeBetweenEvictionRunsMillis=30000 redis.pool.minEvictableIdleTimeMillis=30000 redis.pool.softMinEvictableIdleTimeMillis=10000 redis.pool.numTestsPerEvictionRun=1024 #1000*60*60*1redis.pool.expire=3600000redis.pool.unlock=false 

2、applicationContext.xml文件中增加Redis的支持

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">   <property name="locations">       <list>          <value>classpath:config/redis.properties</value>       </list>    </property></bean><!-- redis属性配置 --><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">       <property name="maxTotal" value="${redis.pool.maxTotal}"/>       <property name="maxIdle" value="${redis.pool.maxIdle}" />       <property name="numTestsPerEvictionRun" value="${redis.pool.numTestsPerEvictionRun}" />       <property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}" />       <property name="minEvictableIdleTimeMillis" value="${redis.pool.minEvictableIdleTimeMillis}" />       <property name="softMinEvictableIdleTimeMillis" value="${redis.pool.softMinEvictableIdleTimeMillis}" />       <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />       <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />   </bean><!-- redis集群配置 哨兵模式 --><bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">    <property name="master">        <bean class="org.springframework.data.redis.connection.RedisNode">            <property name="name" value="mymaster"></property>        </bean>    </property>    <property name="sentinels">        <set>            <bean class="org.springframework.data.redis.connection.RedisNode">                <constructor-arg name="host" value="${redis.sentinel.host1}"></constructor-arg>                <constructor-arg name="port" value="${redis.sentinel.port1}"></constructor-arg>            </bean>            <bean class="org.springframework.data.redis.connection.RedisNode">                <constructor-arg name="host" value="${redis.sentinel.host2}"></constructor-arg>                <constructor-arg name="port" value="${redis.sentinel.port2}"></constructor-arg>            </bean>        </set>    </property></bean><!-- 连接工厂 --><bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">    <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>    <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg></bean><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">    <property name="connectionFactory" ref="redisConnectionFactory"></property></bean>

在注解方式中,需要使用<cache:annotation-driven/> 启动Spring注解功能,这里就不需要了,在上面的applicationContext.xml文件中增加如下AOP配置

<!-- 缓存Key生成器 --><bean id="cacheKeyGenerator" class="com.bug.common.CacheKeyGenerator"></bean><!-- 缓存管理器 --><bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">    <constructor-arg ref="redisTemplate" /></bean><cache:advice id="cacheAdvice" cache-manager="cacheManager" key-generator="cacheKeyGenerator">    <cache:caching cache="user">        <cache:cacheable method="selectUserById"/>        <cache:cache-evict method="deleteUserById"/>    </cache:caching></cache:advice><aop:config>    <aop:pointcut id="cachePointcut" expression="execution(* com.bug.service.*.impl..*(..))" />    <aop:advisor advice-ref="cacheAdvice" pointcut-ref="cachePointcut"/></aop:config>

cache:advice下可以指定多个cache:caching,有点类似于基于注解的@caching,cache:caching元素下又可以指定cache:cacheable、cache:cache-put和cache:cache-evict元素,它们类似于使用注解时的@Cacheable、@CachePut和@CacheEvict,这里的方法名还可以用通配符如select*,表示任何以select开头的方法都可以使用缓存; 有了cache:advice之后,还需要引入aop命名空间,然后通过aop:config指定定义好的cacheAdvice要应用在哪些pointcut上

以上是AOP的主要配置

在同在的项目组中,需要引入Redis缓存,但是有两个要求:1、对现有业务代码不能有任何侵入性,这一点用AOP已经可以满足,2、要有降级方案,如果不需要缓存,需要把缓存关掉,因此需要配置一个开关,当开关打开时,缓存生效,当开关关闭时,缓存不生效,但是这个开关配置在哪里呢,经查阅Spring源码时发现,每一次调用缓存之前,需经过一个拦截器CacheInterceptor,源码如下

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {    @Override    public Object invoke(final MethodInvocation invocation) throws Throwable {        Method method = invocation.getMethod();        Invoker aopAllianceInvoker = new Invoker() {            @Override            public Object invoke() {                try {                    return invocation.proceed();                } catch (Throwable ex) {                    throw new ThrowableWrapper(ex);                }            }        };        try {            return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());        } catch (ThrowableWrapper th) {            throw th.original;        }    }    private static class ThrowableWrapper extends RuntimeException {        private final Throwable original;        ThrowableWrapper(Throwable original) {            this.original = original;        }    }}

因此我的想法是,重写这个拦截器,在拦截器中首先查询缓存是否开启的标识,判断如果缓存开启,继续执行下面的execute方法,如果未开启,则跳过,这样就可以满足降级方案的要求了,但是遗憾的是,CacheInterceptor拦截器是写死在源码中的,不能通过配置文件的方式进行覆盖,因此需要修改Spring源码才行,修改点如下,把源码中的CacheInterceptor替换成自己的Interceptor(未经验证过,只是猜测)

这里写图片描述

0 0
原创粉丝点击