Spring的缓存机制非常灵活,可以对容器中任意Bean或者Bean的方法进行缓存,因此这种缓存机制可以在JavaEE应用的任何层次上进行缓存。
Spring缓存底层也是需要借助其他缓存工具来实现,例如EhCache(Hibernate缓存工具),上层则以统一API编程。
要使用Spring缓存,需要以下三步
- 1.向Spring配置文件导入context:命名空间
- 2.在Spring配置文件启用缓存,具体是添加 <cache:annotation-driven cache-manager="缓存管理器ID" />
- 3.配置缓存管理器,不同的缓存实现配置不同,如果是EhCache,需要先配置一个ehcache.xml
例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<? xml version = "1.0" encoding = "UTF-8" ?>
< ehcache >
< diskStore path = "java.io.tmpdir" />
<!-- 配置默认的缓存区 -->
< defaultCache
maxElementsInMemory = "10000"
eternal = "false"
timeToIdleSeconds = "120"
timeToLiveSeconds = "120"
maxElementsOnDisk = "10000000"
diskExpiryThreadIntervalSeconds = "120"
memoryStoreEvictionPolicy = "LRU" />
<!-- 配置名为users的缓存区 -->
< cache name = "users"
maxElementsInMemory = "10000"
eternal = "false"
overflowToDisk = "true"
timeToIdleSeconds = "300"
timeToLiveSeconds = "600" />
</ ehcache >
|
上面的ehcache.xml
配置了两个缓存区,Spring中的Bean将会缓存在这些缓存区中,一般的,Spring容器中有多少个Bean,就会在ehcache中定义多少个缓存区。
接着在Spring配置文件中配置缓存管理器如下,其中第一个Bean是一个工厂Bean,用来配置EhCache的CacheManager, 第二个Bean才是为Spring缓存配置的缓存管理器,所以将第一个Bean注入第二个Bean。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
< cache:annotation-driven cache-manager = "cacheManager" />
<!-- 配置EhCache的CacheManager
通过configLocation指定ehcache.xml文件的位置 -->
< bean id = "ehCacheManager"
class = "org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation = "classpath:ehcache.xml"
p:shared = "false" />
<!-- 配置基于EhCache的缓存管理器
并将EhCache的CacheManager注入该缓存管理器Bean -->
< bean id = "cacheManager"
class = "org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref = "ehCacheManager" >
</ bean >
|
下面是一个完整的Spring配置,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<? 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:cache = "http://www.springframework.org/schema/cache"
xmlns:context = "http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
< context:component-scan
base-package = "com.service" />
< cache:annotation-driven cache-manager = "cacheManager" />
<!-- 配置EhCache的CacheManager
通过configLocation指定ehcache.xml文件的位置 -->
< bean id = "ehCacheManager"
class = "org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation = "classpath:ehcache.xml"
p:shared = "false" />
<!-- 配置基于EhCache的缓存管理器
并将EhCache的CacheManager注入该缓存管理器Bean -->
< bean id = "cacheManager"
class = "org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref = "ehCacheManager" >
</ bean >
</ beans >
|
下面将以@Cacheable
为例,演示Spring基于EhCache缓存的用法。 Cacheable
用于修饰类或者方法,如果修饰类,则类中所有方法都会被缓存。
类级别的缓存
例如有如下Bean类,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Service ( "userService" )
@Cacheable (value= "users" )
public class UserServiceImpl implements UserService {
@Override
public User getUsersByNameAndAge(String name, int age) {
System.out.println( "正在执行getUsersByNameAndAge().." );
return new User(name,age);
}
@Override
public User getAnotherUser(String name, int age) {
System.out.println( "正在执行getAnotherUser().." );
return new User(name,age);
}
}
|
基于类的缓存,将会缓存类中的所有方法,缓存之后,程序调用该类实例的任何方法,只要传入的参数相同,Spring将不会真正执行该方法,而是直接根据传入的参数去查找缓存中的数据!
比如像下面这样使用缓存数据,
1
2
3
4
5
6
7
8
9
|
public static void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "beans.xml" );
UserService us = ctx.getBean( "userService" , UserService. class );
User u1 = us.getUsersByNameAndAge( "张三" , 50 );
//由于第二次调用userService方法时,使用了相同参数,那么真正的方法将不会执行,
//Spring将直接从缓存按参数查找数据
User u2 = us.getAnotherUser( "张三" , 50 );
System.out.println(u1==u2);
}
|
输出结果,
正在执行getUsersByNameAndAge()..
true
可以看到,上面的getAnotherUser()并没有真正执行,因为传入的参数与之前的方法传入的参数相同,于是Spring直接从缓存区数据了。
上面的Bean类中的注解@Cacheable
除了必选属性value之外,还有key, condition,, unless属性,后面三个都是用来设置Spring存储策略,对于基于类的缓存来说,Spring默认以方法传入的参数作为key去缓存中查找结果。
当然我们也可以修改key的策略,让Spring按照其他标准,比如按照第一个参数是否相同来作为key,在缓存中查找结果。
将上面的Bean类修改如下,
1
2
3
4
5
6
|
@Service ( "userService" )
@Cacheable (value= "users" , key= "#name" )
public class UserServiceImpl implements UserService {
@Override
public User getUsersByNameAndAge(String name, int age) {
|
意味着我们传入相同的name,Spring就不会真正执行方法。只有name不同的时候,方法才会真正执行,例如下面,
1
2
3
4
5
6
7
8
|
public static void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "beans.xml" );
UserService us = ctx.getBean( "userService" , UserService. class );
User u1 = us.getUsersByNameAndAge( "张三" , 50 );
//将@Cacheable的key参数改为key="#name"之后,下面的方法将可以执行。
User u2 = us.getAnotherUser( "李四" , 50 );
System.out.println(u1==u2);
}
|
可以看到这回getAnotherUser()
方法得到执行了,
1 正在执行getUsersByNameAndAge()..
2 正在执行getAnotherUser()..
3 false
我们也可以设置condition属性,例如,
1
2
3
4
5
6
|
@Service ( "userService" )
@Cacheable (value= "users" , condition= "#age<100" )
public class UserServiceImpl implements UserService {
@Override
public User getUsersByNameAndAge(String name, int age) {
|
那么对于下面的代码来说,两个方法都不会被缓存,Spring每次都是执行真正的方法取结果,
1
2
3
4
5
6
7
|
public static void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "beans.xml" );
UserService us = ctx.getBean( "userService" , UserService. class );
User u1 = us.getUsersByNameAndAge( "张三" , 500 );
User u2 = us.getAnotherUser( "李四" , 500 );
System.out.println(u1==u2);
}
|
执行结果,
正在执行getUsersByNameAndAge()..
正在执行getAnotherUser()..
false
方法级别的缓存
方法级别的缓存则只会对方法起作用了,不同的方法可以设置不用的缓存区,例如下面这样,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Service ( "userService" )
public class UserServiceImpl implements UserService {
@Cacheable ( "users1" )
@Override
public User getUsersByNameAndAge(String name, int age) {
System.out.println( "正在执行getUsersByNameAndAge().." );
return new User(name,age);
}
@Cacheable ( "users2" )
@Override
public User getAnotherUser(String name, int age) {
System.out.println( "正在执行getAnotherUser().." );
return new User(name,age);
}
}
|
使用下面的测试代码,
1
2
3
4
5
6
7
8
9
10
11
12
|
public static void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "beans.xml" );
UserService us = ctx.getBean( "userService" , UserService. class );
//第一次执行方法,方法将会真正执行并缓存
User u1 = us.getUsersByNameAndAge( "张三" , 500 );
//虽然下面方法传入相同参数,但是因为这两个方法在不同的缓存区,所以无法使用缓存数据
User u2 = us.getAnotherUser( "张三" , 500 );
System.out.println(u1==u2);
//上面已经缓存过,这里不会真正执行,直接使用缓存
User u3 = us.getAnotherUser( "张三" , 500 );
System.out.println(u3==u2);
}
|
执行结果,
正在执行getUsersByNameAndAge()..
正在执行getAnotherUser()..
false
true
使用@CacheEvict清除缓存
被@CacheEvict修饰的方法可以用来清除缓存,使用@CacheEvict
可以指定如下属性。
allEntries, 是否清空整个缓存区
beforeInvocation: 是否在执行方法之前清除缓存。默认是方法执行成功之后才清除。
condiition以及key, 与@Cacheable
中一样的含义。
下面示范简单用啊,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Service ( "userService" )
@Cacheable ( "users" )
public class UserServiceImpl implements UserService {
@Override
public User getUsersByNameAndAge(String name, int age) {
System.out.println( "正在执行getUsersByNameAndAge().." );
return new User(name,age);
}
@Override
public User getAnotherUser(String name, int age) {
System.out.println( "正在执行getAnotherUser().." );
return new User(name,age);
}
//指定根据name,age参数清楚缓存
@CacheEvict (value= "users" )
public void evictUser(String name, int age) {
System.out.println( "--正在清空" +name+ "," +age+ "对应的缓存--" );
}
//指定清除user缓存区所有缓存的数据
@CacheEvict (value= "users" , allEntries= true )
public void evictAll() {
System.out.println( "--正在清空整个缓存--" );
}
}
|
下面是测试类,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public static void test2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "beans.xml" );
UserService us = ctx.getBean( "userService" , UserService. class );
//系统会缓存两个方法
User u1 = us.getUsersByNameAndAge( "张三" , 500 );
User u2 = us.getAnotherUser( "李四" , 400 );
//调用evictUser()方法清除缓冲区指定的数据
us.evictUser( "李四" , 400 );
//前面清除了 李四, 400 的缓存,下面的方法返回的数据将会再次被缓存
User u3 = us.getAnotherUser( "李四" , 400 );
System.out.println(us == u3); //false
//前面已经缓存了 张三, 500的数据,下面方法不会重新执行,直接取缓存中的数据
User u4 = us.getAnotherUser( "张三" , 500 );
System.out.println(u1==u4); //输出true
//清空整个缓存
us.evictAll();
//由于整个缓存都已经被清空,下面的代码都会被重新执行
User u5 = us.getAnotherUser( "张三" , 500 );
User u6 = us.getAnotherUser( "李四" , 400 );
System.out.println(u1==u5); //输出false
System.out.println(u3==u6); //输出false
}
|
执行结果,
正在执行getUsersByNameAndAge()..
正在执行getAnotherUser()..
--正在清空李四,400对应的缓存--
正在执行getAnotherUser()..
false
true
--正在清空整个缓存--
正在执行getAnotherUser()..
正在执行getAnotherUser()..
false
false
总结
以上就是本文关于Spring缓存机制实例代码的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://www.cnblogs.com/fysola/p/6378400.html