Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。
Spring 的缓存技术还具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache 集成。
其特点总结如下:
1.通过少量的配置 annotation 注释即可使得既有代码支持缓存
2.支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存
3.支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
4.支持 AspectJ,并通过其实现任何方法的缓存支持
5.支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性
一、基于注解的支持
Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable、@CachePut 和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,@CachePut主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable不同的是,它每次都会触发真实方法的调用,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。
1.@Cacheable
@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。
value:缓存的名称,在 spring 配置文件中定义,必须指定至少一个。如@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}
key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。如@Cacheable(value=”testcache”,key=”#userName”)
condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存。如@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
注:除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
属性名称 |
描述 |
示例 |
methodName |
当前方法名 |
#root.methodName |
method |
当前方法 |
#root.method.name |
target |
当前被调用的对象 |
#root.target |
targetClass |
当前被调用的对象的class |
#root.targetClass |
args |
当前方法参数组成的数组 |
#root.args[0] |
caches |
当前被调用的方法使用的Cache |
#root.caches[0].name |
2.@CachePut
在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。
3.@CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存。如:@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。如:@CachEvict(value=”testcache”,beforeInvocation=true)
其他参数和@Cacheable相同
4.@Caching
@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。如: @Caching(cacheable = @Cacheable(“users”), evict = { @CacheEvict(“cache2”),@CacheEvict(value = “cache3”, allEntries = true) })
二、实例
使用map集合实现缓存管理,演示spring cache的使用。
1.创建缓存对象实例
package org.springframework.cache.demo;
import java.io.Serializable;
//缓存对象
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private int id;
private String name;
public User(){
}
public User(String name){
this.name= name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.对象服务实现类
package org.springframework.cache.demo;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
/**
* 业务服务
*
*/
public class UserService {
@Cacheable(value = “userCache”,key=”#userName”)
// 使用了一个缓存名叫 userCache
public User getUserByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
return getFromDB(userName);
}
@CacheEvict(value = “userCache”, key = “#user.name”)
// 清空 accountCache 缓存
public void updateUser(User user) {
updateDB(user);
}
@CacheEvict(value = “userCache”, allEntries = true,beforeInvocation=true)
// 清空 accountCache 缓存
public void reload() {
}
private User getFromDB(String userName) {
System.out.println(“查询数据库…” + userName);
return new User(userName);
}
private void updateDB(User user) {
System.out.println(“更新数据库数据…” + user.getName());
}
}
3.缓存实现
package org.springframework.cache.demo.mycache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
public class MyCache implements Cache {
private String name;
private Map<String, Object> store = new ConcurrentHashMap<String, Object>();;
public MyCache() {
}
public MyCache(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void clear() {
store.clear();
}
public void evict(Object obj) {
}
public ValueWrapper get(Object key) {
ValueWrapper result = null;
Object thevalue = store.get(key);
if (thevalue != null) {
result = new SimpleValueWrapper(thevalue);
}
return result;
}
public <T> T get(Object key, Class<T> clazz) {
return clazz.cast(store.get(key));
}
public String getName() {
return name;
}
public Object getNativeCache() {
return store;
}
public void put(Object key, Object value) {
store.put((String) key, value);
}
public ValueWrapper putIfAbsent(Object key, Object value) {
put(key, value);
return new SimpleValueWrapper(value);
}
}
4.spring配置
<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”
xmlns:p=”http://www.springframework.org/schema/p”
xsi:schemaLocation=”http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd”>
<!– 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 –>
<cache:annotation-driven cache-manager=”cacheManager” />
<bean id=”userService” class=”org.springframework.cache.demo.UserService” />
<!– generic cache manager –>
<bean id=”cacheManager” class=”org.springframework.cache.support.SimpleCacheManager”>
<property name=”caches”>
<set>
<bean class=”org.springframework.cache.demo.mycache.MyCache”
p:name=”userCache” />
</set>
</property>
</bean>
</beans>5.运行类
package org.springframework.cache.demo.mycache;
import org.springframework.cache.demo.User;
import org.springframework.cache.demo.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyMain {
@SuppressWarnings(“resource”)
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(“spring-cache-mycache.xml”);
UserService userService = context.getBean(UserService.class);
// 第一次查询,应该走数据库
System.out.print(“第一次查询…”);
userService.getUserByName(“hello”);
// 第二次查询,应该不查数据库,直接返回缓存的值
System.out.println(“第二次查询…”);
userService.getUserByName(“hello”);
System.out.println();
System.out.println(“==============”);
// 更新某个记录的缓存,首先构造两个用户记录,然后记录到缓存中
User user1 = userService.getUserByName(“user1”);
// 开始更新其中一个
user1.setId(1000);
userService.updateUser(user1);
// 因为被更新了,所以会查询数据库
userService.getUserByName(“user1”);
// 再次查询,应该走缓存
userService.getUserByName(“user1”);
// 更新所有缓存
userService.reload();
System.out.println(“清楚所有缓存”);
// 查询数据库
userService.getUserByName(“user1”);
userService.getUserByName(“user2”);
// 查询缓存
userService.getUserByName(“user1”);
userService.getUserByName(“user2”);
}
}
运行结果:
第一次查询…查询数据库…hello
第二次查询…
==============
查询数据库…user1
更新数据库数据…user1
清楚所有缓存
查询数据库…user1
查询数据库…user2
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-09/135128.htm