在网上找了一些文章,可能是比较老了,都比较麻烦,还要自己写一个Cache接口的Redis实现类,然后配置到Spring自带的CacheManager中。
其实Redis里面已经自带了CacheManager接口和Cache接口的实现,以下两种方式都可以直接配置使用,完全不必自己写代码实现。
配置好了之后就可以在代码中使用@Cacheable等注解直接标注到需要缓存的方法上,可以零耦合的使用Redis缓存,非常方便优雅。
注意:下面配置文件中超时时间的单位均为秒,0表示永不超时
第一种方式,直接使用Redis自带的CacheManager
* 优势:简单直观,效率高
* 劣势:系统中只能用Redis一种缓存了
<!-- Redis缓存配置,这里没什么好说的,网上都有 -->
<bean id="redisPoolConfig"
class="redis.clients.jedis.JedisPoolConfig"
p:maxIdle="${redis.maxIdle}"
p:maxTotal="${redis.maxActive}"
p:maxWaitMillis="${redis.maxWait}"
p:testOnBorrow="${redis.testOnBorrow}" />
<bean id="redisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:poolConfig-ref="redisPoolConfig"
p:hostName="${redis.host}"
p:port="${redis.port}"
p:password="${redis.password}"
p:timeout="${redis.timeout}" />
<!-- 配置序列化器,这个JDK的是最快的,不过序列化出来的字符串会比较长 -->
<bean id="valueSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
<!-- redis template,被后面的RedisCacheManager或RedisCache引用 -->
<bean id="redisTemplate"
class="org.springframework.data.redis.core.StringRedisTemplate"
p:connectionFactory-ref="redisConnectionFactory"
p:valueSerializer-ref="valueSerializer" />
<!-- 允许基于注解的使用,proxy-target-class默认为false,即使用JDK Proxy机制,如设置为true则基于CGLIB动态生成代理类 -->
<cache:annotation-driven cache-manager="cacheManager" />
<!-- 使用Redis自带的CacheManager,需要传入构造参数。usePrefix必须打开,否则@Cacheable的value不起作用 -->
<bean id="cacheManager"
class="org.springframework.data.redis.cache.RedisCacheManager"
p:usePrefix="true"
p:defaultExpiration="3600">
<constructor-arg><ref bean="redisTemplate"/></constructor-arg>
<!-- 下面列表每一项对应一个Cache对象的名字,也就是@Cacheable的value指向的值 -->
<constructor-arg>
<list>
<value>default</value><!-- 缓存名,在注解中用value引用 -->
<value>user</value><!-- 可以配置多个缓存,以实现不同的超时策略 -->
</list>
</constructor-arg>
<!-- 用expires属性分别配置其超时时间,缺省则使用defaultExpiration,单位均为秒 -->
<property name="expires">
<map>
<entry key="user" value="0"/><!-- 此缓存永不超时,即使Redis重启也不会丢失哟 -->
</map>
</property>
</bean>
第二种方式,使用Spring内置CacheManager,
内部使用RedisCache对象
* 优势:可以配置使用多种不同缓存,比如ehcache+redis
* 劣势:发现cacheName和key是用一个单独的缓存项实现的,推测其查询一次需要读取两次缓存,效率略低
<!-- 前面一样,仅替换掉cacheManager的定义 -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.data.redis.cache.RedisCache"><!-- 这个类没有默认构造方法 -->
<constructor-arg index="0" name="name"><value>default</value></constructor-arg><!-- 缓存名,在注解中用value引用 -->
<constructor-arg index="1" name="prefix"><null/></constructor-arg>
<constructor-arg index="2" name="redisOperations"><ref bean="redisTemplate"/></constructor-arg>
<constructor-arg index="3" name="expiration"><value>3600</value></constructor-arg>
</bean>
</set>
</property>
</bean>
关于@Cacheable等注解的使用
* 因为Spring基于JDK Proxy或CGLIB动态产生代理类的实现机制,因此在同一个类中调用带注解的方法时,@Cachable等注解不起作用
* 推荐的方式,把可缓存的方法放在接口中,比如Service接口或Dao接口
* 如果不用接口,则也要把这些可缓存的方法放在单独的类中
* 另外这个类一定要置于Spring容器的管理之下,自己直接创建该类实例是不起作用的
例子
@Component
public interface UserService {
@Cacheable("user") // 只有一个参数,可以省略key,直接用参数作为key,这里的value = "user"对应配置文件中的Cache对象名
User getUser(String userId);
@CachePut(value = "user", key = "#user.id") // 注意必须返回对象,才会被放入缓存中,这里的key可以是spEL表达式
User addUser(User user);
@CacheEvict(value = "user", key = "#userId") // 删除用户并清除缓存
void deleteUser(String userId);
}
在UserService的实现类中,正常编写数据库访问逻辑即可,不必编写任何缓存相关代码。
Spring容器在创建Bean实例的时候,会通过Proxy或CGLIB把你的实现类的相关方法包装一层,来实现缓存机制。