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