Spring緩存機(jī)制實(shí)例代碼
Spring的緩存機(jī)制非常靈活,可以對容器中任意Bean或者Bean的方法進(jìn)行緩存,因此這種緩存機(jī)制可以在JavaEE應(yīng)用的任何層次上進(jìn)行緩存。
Spring緩存底層也是需要借助其他緩存工具來實(shí)現(xiàn),例如EhCache(Hibernate緩存工具),上層則以統(tǒng)一API編程。
要使用Spring緩存,需要以下三步
- 1.向Spring配置文件導(dǎo)入context:命名空間
- 2.在Spring配置文件啟用緩存,具體是添加 <cache:annotation-driven cache-manager="緩存管理器ID" />
- 3.配置緩存管理器,不同的緩存實(shí)現(xiàn)配置不同,如果是EhCache,需要先配置一個ehcache.xml
例如
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="java.io.tmpdir" /> <!-- 配置默認(rèn)的緩存區(qū) --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> <!-- 配置名為users的緩存區(qū) --> <cache name="users" maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" /> </ehcache>
上面的ehcache.xml
配置了兩個緩存區(qū),Spring中的Bean將會緩存在這些緩存區(qū)中,一般的,Spring容器中有多少個Bean,就會在ehcache中定義多少個緩存區(qū)。
接著在Spring配置文件中配置緩存管理器如下,其中第一個Bean是一個工廠Bean,用來配置EhCache的CacheManager, 第二個Bean才是為Spring緩存配置的緩存管理器,所以將第一個Bean注入第二個Bean。
<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配置,
<?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類,
@Service("userService") @Cacheable(value="users") public class UserServiceImpl implements UserService { @Override public User getUsersByNameAndAge(String name, int age) { System.out.println("正在執(zhí)行g(shù)etUsersByNameAndAge().."); return new User(name,age); } @Override public User getAnotherUser(String name, int age) { System.out.println("正在執(zhí)行g(shù)etAnotherUser().."); return new User(name,age); } }
基于類的緩存,將會緩存類中的所有方法,緩存之后,程序調(diào)用該類實(shí)例的任何方法,只要傳入的參數(shù)相同,Spring將不會真正執(zhí)行該方法,而是直接根據(jù)傳入的參數(shù)去查找緩存中的數(shù)據(jù)!
比如像下面這樣使用緩存數(shù)據(jù),
public static void test2() { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = ctx.getBean("userService", UserService.class); User u1 = us.getUsersByNameAndAge("張三", 50); //由于第二次調(diào)用userService方法時,使用了相同參數(shù),那么真正的方法將不會執(zhí)行, //Spring將直接從緩存按參數(shù)查找數(shù)據(jù) User u2 = us.getAnotherUser("張三", 50); System.out.println(u1==u2); }
輸出結(jié)果,
正在執(zhí)行g(shù)etUsersByNameAndAge()..
true
可以看到,上面的getAnotherUser()并沒有真正執(zhí)行,因?yàn)閭魅氲膮?shù)與之前的方法傳入的參數(shù)相同,于是Spring直接從緩存區(qū)數(shù)據(jù)了。
上面的Bean類中的注解@Cacheable
除了必選屬性value之外,還有key, condition,, unless屬性,后面三個都是用來設(shè)置Spring存儲策略,對于基于類的緩存來說,Spring默認(rèn)以方法傳入的參數(shù)作為key去緩存中查找結(jié)果。
當(dāng)然我們也可以修改key的策略,讓Spring按照其他標(biāo)準(zhǔn),比如按照第一個參數(shù)是否相同來作為key,在緩存中查找結(jié)果。
將上面的Bean類修改如下,
@Service("userService") @Cacheable(value="users", key="#name") public class UserServiceImpl implements UserService { @Override public User getUsersByNameAndAge(String name, int age) {
意味著我們傳入相同的name,Spring就不會真正執(zhí)行方法。只有name不同的時候,方法才會真正執(zhí)行,例如下面,
public static void test2() { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = ctx.getBean("userService", UserService.class); User u1 = us.getUsersByNameAndAge("張三", 50); //將@Cacheable的key參數(shù)改為key="#name"之后,下面的方法將可以執(zhí)行。 User u2 = us.getAnotherUser("李四", 50); System.out.println(u1==u2); }
可以看到這回getAnotherUser()
方法得到執(zhí)行了,
1 正在執(zhí)行g(shù)etUsersByNameAndAge()..
2 正在執(zhí)行g(shù)etAnotherUser()..
3 false
我們也可以設(shè)置condition屬性,例如,
@Service("userService") @Cacheable(value="users", condition="#age<100") public class UserServiceImpl implements UserService { @Override public User getUsersByNameAndAge(String name, int age) {
那么對于下面的代碼來說,兩個方法都不會被緩存,Spring每次都是執(zhí)行真正的方法取結(jié)果,
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); }
執(zhí)行結(jié)果,
正在執(zhí)行g(shù)etUsersByNameAndAge()..
正在執(zhí)行g(shù)etAnotherUser()..
false
方法級別的緩存
方法級別的緩存則只會對方法起作用了,不同的方法可以設(shè)置不用的緩存區(qū),例如下面這樣,
@Service("userService") public class UserServiceImpl implements UserService { @Cacheable("users1") @Override public User getUsersByNameAndAge(String name, int age) { System.out.println("正在執(zhí)行g(shù)etUsersByNameAndAge().."); return new User(name,age); } @Cacheable("users2") @Override public User getAnotherUser(String name, int age) { System.out.println("正在執(zhí)行g(shù)etAnotherUser().."); return new User(name,age); } }
使用下面的測試代碼,
public static void test2() { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = ctx.getBean("userService", UserService.class); //第一次執(zhí)行方法,方法將會真正執(zhí)行并緩存 User u1 = us.getUsersByNameAndAge("張三", 500); //雖然下面方法傳入相同參數(shù),但是因?yàn)檫@兩個方法在不同的緩存區(qū),所以無法使用緩存數(shù)據(jù) User u2 = us.getAnotherUser("張三", 500); System.out.println(u1==u2); //上面已經(jīng)緩存過,這里不會真正執(zhí)行,直接使用緩存 User u3 = us.getAnotherUser("張三", 500); System.out.println(u3==u2); }
執(zhí)行結(jié)果,
正在執(zhí)行g(shù)etUsersByNameAndAge()..
正在執(zhí)行g(shù)etAnotherUser()..
false
true
使用@CacheEvict清除緩存
被@CacheEvict修飾的方法可以用來清除緩存,使用@CacheEvict
可以指定如下屬性。
allEntries, 是否清空整個緩存區(qū)
beforeInvocation: 是否在執(zhí)行方法之前清除緩存。默認(rèn)是方法執(zhí)行成功之后才清除。
condiition以及key, 與@Cacheable
中一樣的含義。
下面示范簡單用啊,
@Service("userService") @Cacheable("users") public class UserServiceImpl implements UserService { @Override public User getUsersByNameAndAge(String name, int age) { System.out.println("正在執(zhí)行g(shù)etUsersByNameAndAge().."); return new User(name,age); } @Override public User getAnotherUser(String name, int age) { System.out.println("正在執(zhí)行g(shù)etAnotherUser().."); return new User(name,age); } //指定根據(jù)name,age參數(shù)清楚緩存 @CacheEvict(value="users") public void evictUser(String name, int age) { System.out.println("--正在清空"+name+","+age+"對應(yīng)的緩存--"); } //指定清除user緩存區(qū)所有緩存的數(shù)據(jù) @CacheEvict(value="users", allEntries=true) public void evictAll() { System.out.println("--正在清空整個緩存--"); } }
下面是測試類,
public static void test2() { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService us = ctx.getBean("userService", UserService.class); //系統(tǒng)會緩存兩個方法 User u1 = us.getUsersByNameAndAge("張三", 500); User u2 = us.getAnotherUser("李四",400); //調(diào)用evictUser()方法清除緩沖區(qū)指定的數(shù)據(jù) us.evictUser("李四", 400); //前面清除了 李四, 400 的緩存,下面的方法返回的數(shù)據(jù)將會再次被緩存 User u3 = us.getAnotherUser("李四", 400); System.out.println(us == u3); //false //前面已經(jīng)緩存了 張三, 500的數(shù)據(jù),下面方法不會重新執(zhí)行,直接取緩存中的數(shù)據(jù) User u4 = us.getAnotherUser("張三", 500); System.out.println(u1==u4); //輸出true //清空整個緩存 us.evictAll(); //由于整個緩存都已經(jīng)被清空,下面的代碼都會被重新執(zhí)行 User u5 = us.getAnotherUser("張三", 500); User u6 = us.getAnotherUser("李四", 400); System.out.println(u1==u5); //輸出false System.out.println(u3==u6); //輸出false }
執(zhí)行結(jié)果,
正在執(zhí)行g(shù)etUsersByNameAndAge()..
正在執(zhí)行g(shù)etAnotherUser()..
--正在清空李四,400對應(yīng)的緩存--
正在執(zhí)行g(shù)etAnotherUser()..
false
true
--正在清空整個緩存--
正在執(zhí)行g(shù)etAnotherUser()..
正在執(zhí)行g(shù)etAnotherUser()..
false
false
總結(jié)
以上就是本文關(guān)于Spring緩存機(jī)制實(shí)例代碼的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
- 詳解springboot整合ehcache實(shí)現(xiàn)緩存機(jī)制
- Spring Boot 中使用cache緩存的方法
- 淺談SpringCache與redis集成實(shí)現(xiàn)緩存解決方案
- 淺談SpringBoot集成Redis實(shí)現(xiàn)緩存處理(Spring AOP實(shí)現(xiàn))
- SpringBoot+Mybatis項(xiàng)目使用Redis做Mybatis的二級緩存的方法
- springboot+mybatis+redis 二級緩存問題實(shí)例詳解
- SpringBoot項(xiàng)目中使用redis緩存的方法步驟
- 實(shí)例詳解Spring Boot實(shí)戰(zhàn)之Redis緩存登錄驗(yàn)證碼
相關(guān)文章
SpringBoot處理JSON數(shù)據(jù)方法詳解
這篇文章主要介紹了SpringBoot整合Web開發(fā)中Json數(shù)據(jù)處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-10-10@JsonFormat 實(shí)現(xiàn)日期格式自動格式化
這篇文章主要介紹了@JsonFormat 實(shí)現(xiàn)日期格式自動格式化,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08詳解SpringBoot 快速整合Mybatis(去XML化+注解進(jìn)階)
本篇文章主要介紹了詳解SpringBoot 快速整合Mybatis(去XML化+注解進(jìn)階),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11SpringBoot啟動流程入口參數(shù)創(chuàng)建對象源碼分析
這篇文章主要為大家介紹了SpringBoot啟動流程入口參數(shù)研究及創(chuàng)建對象源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04JavaFX實(shí)現(xiàn)UI美觀效果代碼實(shí)例
這篇文章主要介紹了JavaFX實(shí)現(xiàn)UI美觀效果代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07劍指Offer之Java算法習(xí)題精講鏈表與字符串及數(shù)組
跟著思路走,之后從簡單題入手,反復(fù)去看,做過之后可能會忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質(zhì)的變化2022-03-03徹底搞懂java并發(fā)ThreadPoolExecutor使用
這篇文章主要為大家介紹了徹底搞懂java并發(fā)ThreadPoolExecutor使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02springboot自定義starter實(shí)現(xiàn)過程圖解
這篇文章主要介紹了springboot自定義starter實(shí)現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02