Spring提供了一种在方法级别进行的缓存抽象,如果已经为提供的参数执行过方法,那么再次以同样参数执行时则不必执行方法就可以返回被缓存结果。
本章需要的依赖文件为:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.springframework.samples.service.servicegroupId>
<artifactId>SpringAOPTestartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-framework.version>4.3.10.RELEASEspring-framework.version>
<hibernate.version>5.2.10.Finalhibernate.version>
<logback.version>1.2.3logback.version>
<slf4j.version>1.7.25slf4j.version>
<junit.version>4.12junit.version>
<aspectj.version>1.8.10aspectj.version>
<hazelcast.version>3.8.4hazelcast.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>${spring-framework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${spring-framework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring-framework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>${spring-framework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring-framework.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>${spring-framework.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
<scope>compilescope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>${logback.version}version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-entitymanagerartifactId>
<version>${hibernate.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring-framework.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>${aspectj.version}version>
dependency>
<dependency>
<groupId>com.hazelcastgroupId>
<artifactId>hazelcast-allartifactId>
<version>${hazelcast.version}version>
dependency>
dependencies>
project>
public class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" + "id=" + id +", name='" + name + '\'' + '}';
}
}
然后创建UserService类,其中包含一个缓存方法getUser来返回输入id对应的User对象:
public class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" + "id=" + id +", name='" + name + '\'' + '}';
}
}
然后就可以创建上下文XML文件配置缓存,此处使用的是SimpleCacheManager:
<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-4.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
<cache:annotation-driven />
<bean id="userService" class="com.wiley.beginningspring.ch10.UserService" />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean id="users" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" />
set>
property>
bean>
beans>
最后创建Main类执行程序:
public class Main {
public static void main(String... args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean(UserService.class);
User userFetch1 = userService.getUser(1);
System.out.println(userFetch1);
User userFetch2 = userService.getUser(1);
System.out.println(userFetch2);
}
}
输出结果为:
可以观察发现,两次getUser(1)只实际执行了一次方法。
也可以采用Java文件配置缓存,此时配置文件为:
@Configuration
@ComponentScan(basePackages = {"com.wiley.beginningspring.ch10"})
@EnableCaching
public class ApplicationConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("users")));
return cacheManager;
}
}
此时Main函数中导入上下文变为ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
该注解放在被缓存方法前,必须用字符串或者value特性来提供储存器名称,也可以用value特性提供多换个储存器用花括号括起来。
用@Cacheble属性的key特性使用SpEL来自定义键值
@Cacheable(value = "users", key = "#user.nationalId")
public User getUser(User user) {
System.out.println("User with id " + user.getId() + " requested.");
return users.get(user.getId());
}
此处自定义国家id为键值。
使用@Cacheable注解的condition特性,可以根据条件应用缓存:
@Cacheable(value = "users", condition = "#user.age < 35")
public User getUser(User user) {
System.out.println("User with id " + user.getId() + " requested.");
return users.get(user.getId());
}
此处指定缓存35岁一下用户。在Main中测试:
User user1 = new User(2, "Mert", "5552345060", 34);
User userFetch1 = userService.getUser(user1);
System.out.println(userFetch1);
User userFetch2 = userService.getUser(user1);
System.out.println(userFetch2);
User user2 = new User(1, "Kenan", "5554332088", 37);
User userFetch3 = userService.getUser(user2);
System.out.println(userFetch3);
User userFetch4 = userService.getUser(user2);
System.out.println(userFetch4);
结果为:
可发下id=2的请求只执行了一次,id = 1的请求执行了两次。因为二者对应对象年龄区别。
定义了相关方法从给定缓存器中逐出一个值。如果刚刚的查找方法会创建缓存,那么更新或者删除操作就需要更新和删除原有缓存以免查找到本来不应该存在的数据。
@CacheEvict("users")
public void removeUser(int id) {
users.remove(id);
}
该注解与@Cacheable类似,但是先执行方法再将返回值放入缓存。
可以为一个方法定义提供@Cacheable、@CacheEvict或者@CachePut数组。
@Caching(cacheable = {
@Cacheable(value = "students", condition = "#obj instanceof T(com.wiley.beginningspring.ch10.Student)"),
@Cacheable(value = "teachers", condition = "#obj instanceof T(com.wiley.beginningspring.ch10.Teacher)")
})
public Person getPerson(Person obj) {
return ppl.get(obj.getId());
}
该缓存器可以设置缓存列表并利用列表中缓存进行操作。
id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
id="users" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" />
set>
property>
主要用于测试目的,实际上并不在存储器中缓存任何数据。
id="cacheManager" class="org.springframework.cache.support.NoOpCacheManager" />
在幕后使用了JDK的ConcurrentMap,与SimpleCacheManager配置相同功能,但不需要像前面那样定义缓存存储器。
id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />
使用单个缓存管理器来定义多个缓存管理器。
id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
id="teachers" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" />
set>
property>
class="com.hazelcast.spring.cache.HazelcastCacheManager">
ref="hazelcast" />
list>
property>
上例将简单缓存管理器和Hazelcast缓存管理器捆绑在一起,为他们定义不同的缓存储存器用于缓存不同数据。
注解的key、unless、condition特性的缓存抽象中使用了SpEL
SpEL根据上下文对表达式进行评估,并通过使用缓存抽象,提供了root对象相关联的缓存特定的内置参数
在@PostConstruct注解的方法中初始化缓存存储器。
@PostConstruct
public void setup() {
Cache usersCache = cacheManager.getCache("users");
for (Integer key : users.keySet()) {
usersCache.put(key, users.get(key));
}
}
User类同上。
此时上下文配置中缓存管理器配置为Ehcache
id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache" />
id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
在配置中加载了ehcache缓存框架的配置。
<ehcache>
<cache name="users"
maxElementsInMemory="1000" />
ehcache>
此时只需要在上下文的缓存定义中定义cacheManager Bean既可以获取配置并运行了。
id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager" />
配置全在上下文配置文件中完成。
id="hazelcast">
name="users">
"true" class-name="com.wiley.beginningspring.ch10.User" write-delay-seconds="0"/>
id="userService" class="com.wiley.beginningspring.ch10.UserService" />
id="cacheManager" class="com.hazelcast.spring.cache.HazelcastCacheManager">
ref="hazelcast" />