@cacheable的c 缓存 cache 使用怎么使用

15:41 提问
Spring 缓存@Cacheable 失效问题
@Cacheable(value = "ServTypeCache",key="#dimsCode")
public DimsType getServByCode(String dimsCode){
return cfgDao.getServByCode(dimsCode);
&cache:annotation-driven /&
&!-- 创建spring cache bean --&
&bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"&
&property name="caches"&
&bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"&
&property name="name" value="ServTypeCache"/&
&/property&
Controller调用,每次都会打出sql,怎么回事呀
DimsType dims = mConfigService.getServByCode(dimsCode);
按赞数排序
去cfgDao.getServByCode这个方法里看看呢
public interface IConfigDaoMapper {
public DimsType getServByCode(String dimsCode);
public String getKeysByCode(String dimsCode);
cfgDao.getServByCode这个方法里没啥呀 是个mapper接口类
这就显而易见了,sql是在**Mapper.xml中出现的,会显示在log中,是为了方便查找错误。
用了缓存 应该只会查第一次的吧,所有应该只显示一次呀
我也遇到这个问题了,楼主解决了没有呢?抱大腿。。
求问楼主,最后是如何解决的?
额,我试过了。
cache:annotation-driven/ 放在Spring MVC 的配置文件里,Controller里调用,缓存是生效的,Junit不生效。
放到Spring的配置文件里,Controller调用缓存是不生效的,Junit生效。
所以,如果Spring 和 Spring MVC的配置文件是分开的,最好把cache:annotation-driven/在两边都加上。
至于为什么,我还不清楚。
在使用Spring @Cacheable注解的时候,要注意,如果类A的方法f()被标注了@Cacheable注解,那么当类A的其他方法,例如:f2(),去直接调用f()的时候,@Cacheable是不起作用的,原因是@Cacheable是基于Spring AOP代理类,f2()属于内部方法,直接调用f()时,是不走代理的
其他相关推荐9318人阅读
Spring(9)
redis与Spring Cache的整合请参看
@Cacheable
@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable 作用和配置方法
缓存的名称,在 spring 配置文件中定义,必须指定至少一个
例如:@Cacheable(value=”mycache”)@Cacheable(value={”cache1”,”cache2”}
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
@Cacheable(value=”testcache”,key=”#userName”)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@Cacheable(value=”testcache”,condition=”#userName.length()&2”)
@Cacheable(value=”accountCache”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。这里的缓存中的 key 就是参数 userName,value 就是 Account 对象。“accountCache”缓存是在 spring*.xml 中定义的名称。
@Cacheable(value="accountCache")
public Account getAccountByName(String userName) {
System.out.println("real query account."+userName);
return getFromDB(userName);
@CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 作用和配置方法
缓存的名称,在 spring 配置文件中定义,必须指定至少一个
@CachePut(value=”my cache”)
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
@CachePut(value=”testcache”,key=”#userName”)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@CachePut(value=”testcache”,condition=”#userName.length()&2”)
@CachePut 注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。
@CachePut(value="accountCache",key="#account.getName()")
public Account updateAccount(Account account) {
return updateDB(account);
@CacheEvict
@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 作用和配置方法
缓存的名称,在 spring 配置文件中定义,必须指定至少一个
@CacheEvict(value=”my cache”)
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
@CacheEvict(value=”testcache”,key=”#userName”)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@CacheEvict(value=”testcache”,condition=”#userName.length()&2”)
allEntries
是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation
是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存
@CachEvict(value=”testcache”,beforeInvocation=true)
@CacheEvict(value="accountCache",key="#account.getName()")
public void updateAccount(Account account) {
updateDB(account);
@CacheEvict(value="accountCache",allEntries=true)
public void reload() {
reloadAll()
@Cacheable(value="accountCache",condition="#userName.length() &=4")
public Account getAccountByName(String userName) {
return getFromDB(userName);
@CacheConfig
所有的@Cacheable()里面都有一个value=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了,
所以,有了@CacheConfig这个配置,@CacheConfig is a class-level annotation that allows to share the cache names,如果你在你的方法写别的名字,那么依然以方法的名字为准。
@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
下面提供一些常用的条件缓存
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id)
//@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存;
@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
public User conditionSave(final User user)
//@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反)
@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
public User conditionSave2(final User user)
//@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存;
@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
public User conditionDelete(final User user)
有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id–&user;username—&user;email—&user的缓存;此时就需要@Caching组合多个注解标签了。
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
public User save(User user) {
自定义缓存注解
比如之前的那个@Caching组合,会让方法上的注解显得整个代码比较乱,此时可以使用自定义注解把这些注解组合到一个注解中,如:
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
这样我们在方法上使用如下代码即可,整个代码显得比较干净。
@UserSaveCache
public User save(User user)
比如findByUsername时,不应该只放username–&user,应该连同id—&user和email—&user一起放入;这样下次如果按照id查找直接从缓存中就命中了
cacheable = {
@Cacheable(value = "user", key = "#username")
@CachePut(value = "user", key = "#result.id", condition = "#result != null"),
@CachePut(value = "user", key = "#result.email", condition = "#result != null")
public User findByUsername(final String username) {
System.out.println("cache miss, invoke find by username, username:" + username);
for (User user : users) {
if (user.getUsername().equals(username)) {
return null;
其实对于:id—&user;username—-&user;email—&user;更好的方式可能是:id—&user;username—&id;email—&id;保证user只存一份;如:
@CachePut(value="cacheName", key="#user.username", cacheValue="#user.username")
public void save(User user)
@Cacheable(value="cacheName", key="#user.username", cacheValue="#caches[0].get(#caches[0].get(#username).get())")
public User findByUsername(String username)
SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
methodName
当前被调用的方法名
root.methodName
当前被调用的方法
root.method.name
当前被调用的目标对象
root.target
targetClass
当前被调用的目标对象类
root.targetClass
当前被调用的方法的参数列表
root.args[0]
当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache
root.caches[0].name
argument name
执行上下文
当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数
执行上下文
方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache evict’的beforeInvocation=false)
@CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username", beforeInvocation = true)
public void conditionUpdate(User user)
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:184394次
积分:2482
积分:2482
排名:第15551名
原创:72篇
评论:31条
阅读:22025
(2)(9)(11)(10)(5)(12)(1)(24)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用
作者:彩虹过后的羽翼
字体:[ ] 类型:转载 时间:
本篇文章主要介绍了spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用,具有一定的参考价值,有兴趣的可以了解一下。
maven项目中在pom.xml中依赖2个jar包,其他的spring的jar包省略:
&dependency&
&groupId&redis.clients&/groupId&
&artifactId&jedis&/artifactId&
&version&2.8.1&/version&
&/dependency&
&dependency&
&groupId&org.springframework.data&/groupId&
&artifactId&spring-data-redis&/artifactId&
&version&1.7.2.RELEASE&/version&
&/dependency&
spring-Redis.xml中的内容:
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.2.xsd"&
&context:property-placeholder location="classpath:redis-config.properties" /&
&!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 --&
&cache:annotation-driven cache-manager="cacheManager" /&
&!-- redis 相关配置 --&
&bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"&
&property name="maxIdle" value="${redis.maxIdle}" /&
&property name="maxWaitMillis" value="${redis.maxWait}" /&
&property name="testOnBorrow" value="${redis.testOnBorrow}" /&
&bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/&
&bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"&
&property name="connectionFactory" ref="JedisConnectionFactory" /&
&!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value --&
&bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"&
&property name="caches"&
&!-- 这里可以配置多个redis --&
&!-- &bean class=".util.RedisCache"&
&property name="redisTemplate" ref="redisTemplate" /&
&property name="name" value="default"/&
&/bean& --&
&bean class=".util.RedisCache"&
&property name="redisTemplate" ref="redisTemplate" /&
&property name="name" value="common"/&
&!-- common名称要在类或方法的注解中使用 --&
&/property&
redis-config.properties中的内容:
# Redis settings
# server IP
redis.host=127.0.0.1
# server port
redis.port=6379
# server pass
redis.pass=
# use dbIndex
redis.database=0
# 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例
redis.maxIdle=300
# 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间(毫秒),则直接抛出JedisConnectionException;
redis.maxWait=3000
# 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
redis.testOnBorrow=true
<.util.RedisCache类中的内容:
import java.io.ByteArrayInputS
import java.io.ByteArrayOutputS
import java.io.IOE
import java.io.ObjectInputS
import java.io.ObjectOutputS
import org.springframework.cache.C
import org.springframework.cache.support.SimpleValueW
import org.springframework.dao.DataAccessE
import org.springframework.data.redis.connection.RedisC
import org.springframework.data.redis.core.RedisC
import org.springframework.data.redis.core.RedisT
public class RedisCache implements Cache{
private RedisTemplate&String, Object& redisT
public RedisTemplate&String, Object& getRedisTemplate() {
return redisT
public void setRedisTemplate(RedisTemplate&String, Object& redisTemplate) {
this.redisTemplate = redisT
public void setName(String name) {
this.name =
public String getName() {
// TODO Auto-generated method stub
return this.
public Object getNativeCache() {
// TODO Auto-generated method stub
return this.redisT
public ValueWrapper get(Object key) {
// TODO Auto-generated method stub
System.out.println("get key");
final String keyf = key.toString();
Object object =
object = redisTemplate.execute(new RedisCallback&Object&() {
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] key = keyf.getBytes();
byte[] value = connection.get(key);
if (value == null) {
return toObject(value);
return (object != null &#63; new SimpleValueWrapper(object) : null);
public void put(Object key, Object value) {
// TODO Auto-generated method stub
System.out.println("put key");
final String keyf = key.toString();
final Object valuef =
final long liveTime = 86400;
redisTemplate.execute(new RedisCallback&Long&() {
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] keyb = keyf.getBytes();
byte[] valueb = toByteArray(valuef);
connection.set(keyb, valueb);
if (liveTime & 0) {
connection.expire(keyb, liveTime);
return 1L;
private byte[] toByteArray(Object obj) {
byte[] bytes =
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
}catch (IOException ex) {
ex.printStackTrace();
private Object toObject(byte[] bytes) {
Object obj =
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
public void evict(Object key) {
// TODO Auto-generated method stub
System.out.println("del key");
final String keyf = key.toString();
redisTemplate.execute(new RedisCallback&Long&() {
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
return connection.del(keyf.getBytes());
public void clear() {
// TODO Auto-generated method stub
System.out.println("clear key");
redisTemplate.execute(new RedisCallback&String&() {
public String doInRedis(RedisConnection connection)
throws DataAccessException {
connection.flushDb();
return "ok";
public &T& T get(Object key, Class&T& type) {
// TODO Auto-generated method stub
public ValueWrapper putIfAbsent(Object key, Object value) {
// TODO Auto-generated method stub
到了这一步,大部分人会想在web.xml的启动配置文件地方(context-param)加入了spring-redis.xml,让项目启动时加载这个配置文件吧,但是这样启动后注解不生效。
正确的做法是:web.xml中配置了servlet控制器:
&servlet-name&SpringMVC&/servlet-name&
&servlet-class&org.springframework.web.servlet.DispatcherServlet&/servlet-class&
&init-param&
&param-name&contextConfigLocation&/param-name&
&param-value&/WEB-INF/spring-mvc.xml&/param-value&
&/init-param&
&load-on-startup&1&/load-on-startup&
&async-supported&true&/async-supported&
&/servlet&
在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为spring-mvc.xml的配置文件,如果不指定的话,默认是applicationContext.xml
只需要在spring-mvc.xml文件中引入spring-redis配置文件即可,正如spring-redis.xml中的启用注解说的:&cache:annotation-driven cache-manager="cacheManager" /&注解一定要声明在spring主配置文件中才会生效。
spring-mvc.xml内容,省略了spring与spring MVC整合的那部分:
&&#63;xml version="1.0" encoding="UTF-8"&#63;&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd"&
&!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 --&
&context:component-scan base-package="" /&
&!-- 引入同文件夹下的redis属性配置文件 --&
&import resource="spring-redis.xml"/&
在service的实现类中:
public class UserServiceImpl implements UserService{
@Autowired
private UserBo userBo;
@Cacheable(value="common",key="'id_'+#id")
public User selectByPrimaryKey(Integer id) {
return userBo.selectByPrimaryKey(id);
@CachePut(value="common",key="#user.getUserName()")
public void insertSelective(User user) {
userBo.insertSelective(user);
@CacheEvict(value="common",key="'id_'+#id")
public void deleteByPrimaryKey(Integer id) {
userBo.deleteByPrimaryKey(id);
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。
&&&&&&&使用Spring Cache需要我们做两方面的事:
n&&声明某些方法使用缓存
n&&配置Spring对Cache的支持
&&&&&&&和Spring对事务管理的支持一样,Spring对Cache的支持也有基于注解和基于XML配置两种方式。下面我们先来看看基于注解的方式。
1&&&&&&&基于注解的支持
&&&&&& Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面我们将来详细介绍一下Spring基于注解对Cache的支持所提供的几个注解。
1.1&&&&@Cacheable
&&&&&& @Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。
1.1.1&&value属性指定Cache名称
&&&&&& value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。
&&&@Cacheable("cache1")//Cache是发生在cache1上的
&&&public&User find(Integer id) {
&&&@Cacheable({"cache1",&"cache2"})//Cache是发生在cache1和cache2上的
&&&public&User find(Integer id) {
1.1.2&&使用key属性自定义key
&&&&&& key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
&&&&&&&自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。
&&&@Cacheable(value="users", key="#id")
&&&public&User find(Integer id) {
&&&@Cacheable(value="users", key="#p0")
&&&public&User find(Integer id) {
&&&@Cacheable(value="users", key="#user.id")
&&&public&User find(User user) {
&&&@Cacheable(value="users", key="#p0.id")
&&&public&User find(User user) {
&&&&&&&除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
methodName
当前方法名
#root.methodName
#root.method.name
当前被调用的对象
#root.target
targetClass
当前被调用的对象的class
#root.targetClass
当前方法参数组成的数组
#root.args[0]
当前被调用的方法使用的Cache
#root.caches[0].name
&&&&&&&当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:
&&&@Cacheable(value={"users",&"xxx"}, key="caches[1].name")
&&&public&User find(User user) {
1.1.3&&condition属性指定发生的条件
&&&&&&&有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。
&&&@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
&&&public&User find(User user) {
&&&&& System.out.println("find user by user "&+ user);
&&&&&&return&
1.2&&&&&@CachePut
&&&&&&&在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
&&&&&& @CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。
&&&@CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中
&&&public&User find(Integer id) {
1.3&&&&&@CacheEvict
&&&&&& @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
1.3.1&&allEntries属性
&&&&&& allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。
&&&@CacheEvict(value="users", allEntries=true)
&&&public&void&delete(Integer id) {
&&&&& System.out.println("delete user by id: "&+ id);
1.3.2&&beforeInvocation属性
&&&&&&&清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
&&&@CacheEvict(value="users", beforeInvocation=true)
&&&public&void&delete(Integer id) {
&&&&& System.out.println("delete user by id: "&+ id);
&&&&&&&其实除了使用@CacheEvict清除缓存元素外,当我们使用Ehcache作为实现时,我们也可以配置Ehcache自身的驱除策略,其是通过Ehcache的配置文件来指定的。由于Ehcache不是本文描述的重点,这里就不多赘述了,想了解更多关于Ehcache的信息,请查看我关于Ehcache的专栏。
1.4&&&&&@Caching
&&&&&& @Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
&&&@Caching(cacheable =&@Cacheable("users"), evict = {&@CacheEvict("cache2"),
&&&&&&&&&@CacheEvict(value =&"cache3", allEntries =&true) })
&&&public&User find(Integer id) {
1.5&&&&&使用自定义注解
&&&&&& Spring允许我们在配置可缓存的方法时使用自定义的注解,前提是自定义的注解上必须使用对应的注解进行标注。如我们有如下这么一个使用@Cacheable进行标注的自定义注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public&@interface&MyCacheable&{
&&&&&&&那么在我们需要缓存的方法上使用@MyCacheable进行标注也可以达到同样的效果。
&&&@MyCacheable
&&&public&User findById(Integer id) {
&&&&& System.out.println("find user by id: "&+ id);
&&&&& User user =&new&User();
&&&&& user.setId(id);
&&&&& user.setName("Name"&+ id);
&&&&&&return&
2&&&&&&&配置Spring对Cache的支持
2.1&&&&&声明对Cache的支持
2.1.1&&基于注解
&&&&&&&配置Spring对基于注解的Cache的支持,首先我们需要在Spring的配置文件中引入cache命名空间,其次通过&cache:annotation-driven /&就可以启用Spring对基于注解的Cache的支持。
&?xml&version="1.0"&encoding="UTF-8"?&
&beans&xmlns="http://www.springframework.org/schema/beans"
&&&xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
&&&xmlns:cache="http://www.springframework.org/schema/cache"
&&&xsi:schemaLocation="http://www.springframework.org/schema/beans
&&&& http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
&&&& http://www.springframework.org/schema/cache
&&&& http://www.springframework.org/schema/cache/spring-cache.xsd"&
&&&&cache:annotation-driven/&
&&&&&& &cache:annotation-driven/&有一个cache-manager属性用来指定当前所使用的CacheManager对应的bean的名称,默认是cacheManager,所以当我们的CacheManager的id为cacheManager时我们可以不指定该参数,否则就需要我们指定了。
&&&&&& &cache:annotation-driven/&还可以指定一个mode属性,可选值有proxy和aspectj。默认是使用proxy。当mode为proxy时,只有缓存方法在外部被调用的时候Spring Cache才会发生作用,这也就意味着如果一个缓存方法在其声明对象内部被调用时Spring Cache是不会发生作用的。而mode为aspectj时就不会有这种问题。另外使用proxy时,只有public方法上的@Cacheable等标注才会起作用,如果需要非public方法上的方法也可以使用Spring Cache时把mode设置为aspectj。
&&&&&&&此外,&cache:annotation-driven/&还可以指定一个proxy-target-class属性,表示是否要代理class,默认为false。我们前面提到的@Cacheable、@cacheEvict等也可以标注在接口上,这对于基于接口的代理来说是没有什么问题的,但是需要注意的是当我们设置proxy-target-class为true或者mode为aspectj时,是直接基于class进行操作的,定义在接口上的@Cacheable等Cache注解不会被识别到,那对应的Spring Cache也不会起作用了。
&&&&&&&需要注意的是&cache:annotation-driven/&只会去寻找定义在同一个ApplicationContext下的@Cacheable等缓存注解。
2.1.2&&基于XML配置
&&&&&&&除了使用注解来声明对Cache的支持外,Spring还支持使用XML来声明对Cache的支持。这主要是通过类似于aop:advice的cache:advice来进行的。在cache命名空间下定义了一个cache:advice元素用来定义一个对于Cache的advice。其需要指定一个cache-manager属性,默认为cacheManager。cache:advice下面可以指定多个cache:caching元素,其有点类似于使用注解时的@Caching注解。cache:caching元素下又可以指定cache:cacheable、cache:cache-put和cache:cache-evict元素,它们类似于使用注解时的@Cacheable、@CachePut和@CacheEvict。下面来看一个示例:
&&&&cache:advice&id="cacheAdvice"&cache-manager="cacheManager"&
&&&&&&&cache:caching&cache="users"&
&&&&&&&&&&cache:cacheable&method="findById"&key="#p0"/&
&&&&&&&&&&cache:cacheable&method="find"&key="#user.id"/&
&&&&&&&&&&cache:cache-evict&method="deleteAll"&all-entries="true"/&
&&&&&&&/cache:caching&
&&&&/cache:advice&
&&&&&&&上面配置定义了一个名为cacheAdvice的cache:advice,其中指定了将缓存findById方法和find方法到名为users的缓存中。这里的方法还可以使用通配符“*”,比如“find*”表示任何以“find”开始的方法。
&&&&&&&有了cache:advice之后,我们还需要引入aop命名空间,然后通过aop:config指定定义好的cacheAdvice要应用在哪些pointcut上。如:
&&&&aop:config&proxy-target-class="false"&
&&&&&&&aop:advisor&advice-ref="cacheAdvice"&pointcut="execution(* com.xxx.UserService.*(..))"/&
&&&&/aop:config&
&&&&&&&上面的配置表示在调用com.xxx.UserService中任意公共方法时将使用cacheAdvice对应的cache:advice来进行Spring Cache处理。更多关于Spring Aop的内容不在本文讨论范畴内。
2.2&&&&&配置CacheManager
&&&&&& CacheManager是Spring定义的一个用来管理Cache的接口。Spring自身已经为我们提供了两种CacheManager的实现,一种是基于Java API的ConcurrentMap,另一种是基于第三方Cache实现——Ehcache,如果我们需要使用其它类型的缓存时,我们可以自己来实现Spring的CacheManager接口或AbstractCacheManager抽象类。下面分别来看看Spring已经为我们实现好了的两种CacheManager的配置示例。
2.2.1&&基于ConcurrentMap的配置
&&&&bean&id="cacheManager"&class="org.springframework.cache.support.SimpleCacheManager"&
&&&&&&&property&name="caches"&
&&&&&&&&&&set&
&&&&&&&&&&&&&bean&class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"&p:name="xxx"/&
&&&&&&&&&&/set&
&&&&&&&/property&
&&&&/bean&
&&&&&&&上面的配置使用的是一个SimpleCacheManager,其中包含一个名为“xxx”的ConcurrentMapCache。
2.2.2&&基于Ehcache的配置
&&&&!--&Ehcache实现&--&
&&&&bean&id="cacheManager"&class="org.springframework.cache.ehcache.EhCacheCacheManager"&p:cache-manager-ref="ehcacheManager"/&
&&&&bean&id="ehcacheManager"&class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"&p:config-location="ehcache-spring.xml"/&
&&&&&&&上面的配置使用了一个Spring提供的EhCacheCacheManager来生成一个Spring的CacheManager,其接收一个Ehcache的CacheManager,因为真正用来存入缓存数据的还是Ehcache。Ehcache的CacheManager是通过Spring提供的EhCacheManagerFactoryBean来生成的,其可以通过指定ehcache的配置文件位置来生成一个Ehcache的CacheManager。若未指定则将按照Ehcache的默认规则取classpath根路径下的ehcache.xml文件,若该文件也不存在,则获取Ehcache对应jar包中的ehcache-failsafe.xml文件作为配置文件。更多关于Ehcache的内容这里就不多说了,它不属于本文讨论的内容,欲了解更多关于Ehcache的内容可以参考我之前发布的Ehcache系列文章,也可以参考官方文档等。
3&&&&&&&键的生成策略
&&&&&&&键的生成策略有两种,一种是默认策略,一种是自定义策略。
3.1&&&&&默认策略
&&&&&&&默认的key生成策略是通过KeyGenerator生成的,其默认策略如下:
n&&如果方法没有参数,则使用0作为key。
n&&如果只有一个参数的话则使用该参数作为key。
n&&如果参数多余一个的话则使用所有参数的hashCode作为key。
&&&&&&&如果我们需要指定自己的默认策略的话,那么我们可以实现自己的KeyGenerator,然后指定我们的Spring Cache使用的KeyGenerator为我们自己定义的KeyGenerator。
&&&&&&&使用基于注解的配置时是通过cache:annotation-driven指定的.
&&&&cache:annotation-driven&key-generator="userKeyGenerator"/&
&&&&bean&id="userKeyGenerator"&class="com.xxx.cache.UserKeyGenerator"/&
&&&&&&&而使用基于XML配置时是通过cache:advice来指定的。
&&&&cache:advice&id="cacheAdvice"&cache-manager="cacheManager"&key-generator="userKeyGenerator"&
&&&&/cache:advice&
&&&&&&&需要注意的是此时我们所有的Cache使用的Key的默认生成策略都是同一个KeyGenerator。
3.2&&&&&自定义策略
&&&&&&&自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。
&&&@Cacheable(value="users", key="#id")
&&&public&User find(Integer id) {
&&&@Cacheable(value="users", key="#p0")
&&&public&User find(Integer id) {
&&&@Cacheable(value="users", key="#user.id")
&&&public&User find(User user) {
&&&@Cacheable(value="users", key="#p0.id")
&&&public&User find(User user) {
&&&&&&&除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
methodName
当前方法名
#root.methodName
#root.method.name
当前被调用的对象
#root.target
targetClass
当前被调用的对象的class
#root.targetClass
当前方法参数组成的数组
#root.args[0]
当前被调用的方法使用的Cache
#root.caches[0].name
&&&&&&&当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:
&&&@Cacheable(value={"users",&"xxx"}, key="caches[1].name")
&&&public&User find(User user) {
4&&&&&&&Spring单独使用Ehcache
&&&&&&&前面介绍的内容是Spring内置的对Cache的支持,其实我们也可以通过Spring自己单独的使用Ehcache的CacheManager或Ehcache对象。通过在Application Context中配置EhCacheManagerFactoryBean和EhCacheFactoryBean,我们就可以把对应的EhCache的CacheManager和Ehcache对象注入到其它的Spring bean对象中进行使用。
4.1&&&&&EhCacheManagerFactoryBean
&&&& EhCacheManagerFactoryBean是Spring内置的一个可以产生Ehcache的CacheManager对象的FactoryBean。其可以通过属性configLocation指定用于创建CacheManager的Ehcache配置文件的路径,通常是ehcache.xml文件的路径。如果没有指定configLocation,则将使用默认位置的配置文件创建CacheManager,这是属于Ehcache自身的逻辑,即如果在classpath根路径下存在ehcache.xml文件,则直接使用该文件作为Ehcache的配置文件,否则将使用ehcache-xxx.jar中的ehcache-failsafe.xml文件作为配置文件来创建Ehcache的CacheManager。此外,如果不希望创建的CacheManager使用默认的名称(在ehcache.xml文件中定义的,或者是由CacheManager内部定义的),则可以通过cacheManagerName属性进行指定。下面是一个配置EhCacheManagerFactoryBean的示例。
&&&&!--&定义CacheManager --&
&&&&bean&id="cacheManager"&class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"&
&&&&&&&!--&指定配置文件的位置&--&
&&&&&&&property&name="configLocation"&value="/WEB-INF/config/ehcache.xml"/&
&&&&&&&!--&指定新建的CacheManager的名称&--&
&&&&&&&property&name="cacheManagerName"&value="cacheManagerName"/&
&&&&/bean&
4.2&&&&&EhCacheFactoryBean
&&&&&& EhCacheFactoryBean是用来产生Ehcache的Ehcache对象的FactoryBean。定义EhcacheFactoryBean时有两个很重要的属性我们可以来指定。一个是cacheManager属性,其可以指定将用来获取或创建Ehcache的CacheManager对象,若未指定则将通过CacheManager.create()获取或创建默认的CacheManager。另一个重要属性是cacheName,其表示当前EhCacheFactoryBean对应的是CacheManager中的哪一个Ehcache对象,若未指定默认使用beanName作为cacheName。若CacheManager中不存在对应cacheName的Ehcache对象,则将使用CacheManager创建一个名为cacheName的Cache对象。此外我们还可以通过EhCacheFactoryBean的timeToIdle、timeToLive等属性指定要创建的Cache的对应属性,注意这些属性只对CacheManager中不存在对应Cache时新建的Cache才起作用,对已经存在的Cache将不起作用,更多属性设置请参考Spring的API文档。此外还有几个属性是对不管是已经存在还是新创建的Cache都起作用的属性:statisticsEnabled、sampledStatisticsEnabled、disabled、blocking和cacheEventListeners,其中前四个默认都是false,最后一个表示为当前Cache指定CacheEventListener。下面是一个定义EhCacheFactoryBean的示例。
&&&&!--&定义CacheManager --&
&&&&bean&id="cacheManager"&class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"&
&&&&&&&!--&指定配置文件的位置&--&
&&&&&&&property&name="configLocation"&value="/WEB-INF/config/ehcache.xml"/&
&&&&&&&!--&指定新建的CacheManager的名称&--&
&&&&&&&property&name="cacheManagerName"&value="cacheManagerName"/&
&&&&/bean&
&&&&!--&定义一个Ehcache&--&
&&&&bean&id="userCache"&class="org.springframework.cache.ehcache.EhCacheFactoryBean"&
&&&&&&&property&name="cacheName"&value="user"/&
&&&&&&&property&name="cacheManager"&ref="cacheManager"/&
&&&&/bean&
阅读(...) 评论()

我要回帖

更多关于 cacheable 缓存时间 的文章

 

随机推荐