spring(13)缓存数据
生活随笔
收集整理的這篇文章主要介紹了
spring(13)缓存数据
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【0】README
1)本文部分文字描述轉(zhuǎn)自:“Spring In Action(中/英文版)”,旨在review ?“spring(13)緩存數(shù)據(jù)”?的相關(guān)知識(shí);
2)緩存:緩存可以存儲(chǔ)經(jīng)常會(huì)用到的信息,這樣每次需要的時(shí)候,這些info 都是立即可用的;
3)for complate source code, please visit ?https://github.com/pacosonTang/SpringInAction/tree/master/spring13;
【1】 啟用對(duì)緩存的支持 1)intro:spring對(duì) 緩存的支持有兩種方式: way1)注解驅(qū)動(dòng)的緩存; way2)XML 聲明的緩存
2)使用spring 的緩存抽象時(shí),最為通用的方式就是在方法上添加 @Cacheable 和 @CacheEvit注解; 2.1)在往bean上添加緩存注解的時(shí)候,必須要啟用 spring 對(duì)注解驅(qū)動(dòng)緩存的支持。如果使用 java配置的話(huà),可以在其中的一個(gè)配置類(lèi)上添加 @EnableCaching注解,這樣就能啟用注解驅(qū)動(dòng)緩存了。(干貨——@EnableCaching注解的作用) 2.2)java配置啟用注解驅(qū)動(dòng)的緩存,代碼如下所示: @Configuration @EnableCaching // 啟用緩存 public class CachingConfig {@Beanpublic CacheManager cacheManager() { // 聲明緩存管理器return new ConcurrentMapCacheManager();} } 2.3)xml 配置配置啟用注解驅(qū)動(dòng)的緩存,代碼如下所示: <?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:cache="http://www.springframework.org/schema/cache" 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"> <cache:annotation-driven /> <bean id="cacheManager" class= "org.springframework.cache.concurrent.ConcurrentMapCacheManager" /> </beans> 對(duì)以上代碼的分析(Analysis): A1)@EnableCaching and <cache:annotation-driven> 的工作方式是相同的;他們都會(huì)創(chuàng)建一個(gè)切面并觸發(fā) spring 緩存注解的切點(diǎn); A2) 根據(jù)所使用的 注解以及緩存的狀態(tài),這個(gè)切面會(huì)從緩存中獲取數(shù)據(jù),并將數(shù)據(jù)添加到緩存中或者從緩存中移除這個(gè)值; A3)以上的代碼還聲明了一個(gè) 緩存管理器的bean;(ConcurrentMapCacheManager)
【1.1】配置緩存管理器? 1)intro:spring3.1 內(nèi)置了 5個(gè) 緩存管理器實(shí)現(xiàn),如下所示:
【1.1.1】 使用 Ehcache 緩存 1)intro to Ehcache:Ehcache 是最為流行的緩存供應(yīng)商之一;spring 為其提供的緩存管理器 叫做 EhCacheCacheManager; 2)如何在java中對(duì)其配置(以java 配置的方式設(shè)置 EhCacheCacheManager) @Configuration @EnableCaching // 啟用緩存 public class CacheConfig {/** @Bean public CacheManager cacheManager() { // 聲明緩存管理器 return new* ConcurrentMapCacheManager(); }*/@Beanpublic EhCacheCacheManager cacheManager(CacheManager cm) {return new EhCacheCacheManager(cm);}@Bean public EhCacheManagerFactoryBean ehcache() { EhCacheManagerFactoryBean cacheFactory = new EhCacheManagerFactoryBean();cacheFactory.setConfigLocation(new ClassPathResource("com/spring/spittr/cache/ehcache.xml"));return cacheFactory; } } 對(duì)以上代碼的分析(Analysis):?我們需要?使用 EhCache的 CacheManager 來(lái)進(jìn)行注入;而spring提供了EhCacheManagerFactoryBean 來(lái)生成 EhCache 的 CacheManager;
3)除了在spring中配置 的bean,還需要針對(duì)EhCache 的配置。Ehcache 為 XML 定義了 自己的配置模式,需要在一個(gè) XML 文件中配置緩存,該文件需要符合 EhCache 所定義的模式; 3.1)在創(chuàng)建 EhCacheManagerFactoryBean,的過(guò)程中,需要告訴它 EhCache 配置文件在什么地方; 3.2)在這里,通過(guò)調(diào)用 setConfigLocation()方法,傳入 ClassPathResource,用來(lái)指明Ehcache XML 配置文件相對(duì)于根路徑的位置;(參見(jiàn)上一小節(jié)的代碼) 3.3)下面是一個(gè) ehcache.xml 文件的實(shí)例 <ehcache><cache name="spitterCache"maxBytesLocalHeap="50m"timeToLiveSeconds="100"></cache> </ehcache>
【1.1.2】 使用Redis 緩存 1)intro:Redis 作為 key-value存儲(chǔ),也非常適合 存儲(chǔ)緩存; 2)Redis 可以用來(lái)為 spring 緩存抽象機(jī)制存儲(chǔ)緩存條目:?spring data Redis 提供了 RedisCacheManager,這是 CacheManager 的一個(gè)實(shí)現(xiàn);RedisCacheManager 會(huì)與一個(gè) Redis 服務(wù)器協(xié)作,并通過(guò) RedisTemplate 將緩存條目存儲(chǔ)到 Redis中; 3)配置將緩存條目存儲(chǔ)在 Redis 服務(wù)器的緩存管理器 @Configuration @EnableCaching public class CachingConfig { @Bean public CacheManager cacheManager(RedisTemplate redisTemplate) { return new RedisCacheManager(redisTemplate); } @Bean public JedisConnectionFactory redisConnectionFactory() { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.afterPropertiesSet(); return jedisConnectionFactory; } @Bean public RedisTemplate<String, String> redisTemplate( RedisConnectionFactory redisCF) { RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>(); redisTemplate.setConnectionFactory(redisCF); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
【1.1.3】使用多個(gè)緩存管理器 1)intro to?CompositeCacheManager:CompositeCacheManager(復(fù)合型緩存管理器) 要通過(guò)一個(gè)或多個(gè)緩存管理器來(lái)進(jìn)行配置,它會(huì)迭代這些緩存管理器,以查找之前所緩存的值; 2)以下程序清單 展現(xiàn)了如何創(chuàng)建 CompositeCacheManager bean; @Bean public CacheManager cacheManager(net.sf.ehcache.CacheManager cm,javax.cache.CacheManager jcm) {CompositeCacheManager cacheManager = new CompositeCacheManager();List<CacheManager> managers = new ArrayList<CacheManager>();managers.add(new JCacheCacheManager(jcm));managers.add(new EhCacheCacheManager(cm))managers.add(new RedisCacheManager(redisTemplate()));cacheManager.setCacheManagers(managers);return cacheManager; } Attention)在配置完緩存管理器并啟用緩存后,就可以在 bean 方法上應(yīng)用緩存規(guī)則了;下面看一下如何使用 spring 的緩存注解來(lái)定義緩存邊界;
【2】 為方法添加注解以支持緩存 0)intro: spring 的緩存抽象在很大程度上是圍繞切面創(chuàng)建的 ;在spring中 啟用緩存時(shí),會(huì)創(chuàng)建一個(gè)切面,它觸發(fā)一個(gè)或多個(gè)spirng 的緩存注解; 1)下表列出了 spring 所提供的緩存注解:
【2.1】填充緩存 1)@Cacheable 和 @CachePut 都可以填充緩存,他們的工作方式略有差異(difference) d1)@Cacheable:首先在緩存中查找條目,如果找到了匹配的條目,那么就不會(huì)對(duì)方法進(jìn)行調(diào)用了;如果沒(méi)有找到匹配條目,方法會(huì)被調(diào)用并且返回值要放到緩存之中; d2)@CachePut:并不會(huì)在緩存中檢查匹配的值,目標(biāo)方法總是會(huì)被調(diào)用,并將返回值添加到緩存中;(干貨——@Cacheable 和 @CachePut 注解的作用) 2)@Cacheable 和 @CachePut共有的屬性如下:
對(duì)上表的分析(Analysis):在最簡(jiǎn)單的case下, 在?@Cacheable 和 @CachePut 的這些屬性中,只需要使用 value屬性 指定一個(gè)或多個(gè)緩存即可;
3)看個(gè)荔枝 @Cacheable("spittleCache") public Spittle findOne(long id) {try {return jdbcTemplate.queryForObject(SELECT_SPITTLE_BY_ID,new SpittleRowMapper(),id);} catch (EmptyResultDataAccessException e) {return null;} } 對(duì)以上代碼的分析(Analysis): A1)當(dāng)findOne()方法被調(diào)用時(shí),緩存切面會(huì)攔截調(diào)用并在緩存中查找之前以名?spittleCache 存儲(chǔ)的返回值; A2)緩存key 是傳遞到 findOne()方法中的id 參數(shù); A2.1)如果找到這個(gè)值的話(huà):那么方法不會(huì)再被調(diào)用; A2.2)如果沒(méi)有找到這個(gè)值的話(huà):那么就會(huì)調(diào)用這個(gè)方法,并將返回值放到緩存中,為下一次調(diào)用 findOne()方法做準(zhǔn)備;
4)以下代碼的problem+solution: 4.1)problem:@Cacheable的緩存作用只限于 JdbcSpittleRepository這個(gè)實(shí)現(xiàn)類(lèi)中,SpittleRepository的其他實(shí)現(xiàn)并沒(méi)有緩存功能,除非也為其添加上 @Cacheable注解; 4.2)solution:可以考慮將注解添加到 SpittleRepository的方法聲明上,而不是放在實(shí)現(xiàn)類(lèi)中(放在 頂層的接口中);(干貨——應(yīng)該將緩存注解Cacheable添加到Repository的接口聲明中) public interface SpitterRepository {@CachePut("spitterCache")Spitter save(Spitter spitter);@Cacheable("spitterCache")Spitter findByUsername(String username);@Cacheable("spitterCache")String findPassByUsername(String username); }
【2.1.1】 將值放到緩存中 1)intro: @CachePut注解:該注解的方法始終都會(huì)被調(diào)用,而且他的返回值也會(huì)放到緩存中。這提供了一種很便利的機(jī)制,能夠讓跟我們?cè)谡?qǐng)求前預(yù)先加載緩存;
2)@CachePut注解的荔枝: 當(dāng)一個(gè)client 調(diào)用了 save()方法保存 spitter 后,很可能馬上就會(huì)再次請(qǐng)求這條記錄。所以,當(dāng)save()方法 調(diào)用后,立即 將 spitter 塞到 緩存中是很有意義的。這樣當(dāng)其他人通過(guò) findOne() 方法進(jìn)行查找時(shí),它就已經(jīng)準(zhǔn)備就緒了;(干貨——@CachePut注解的作用) @CachePut("spitterCache") Spittle save(Spittle spittle);
【2.1.2】自定義緩存key 1)intro:Both @Cacheable and @CachePut 都有一個(gè)名為key的屬性,這個(gè)屬性能夠替換默認(rèn)的 key,這通過(guò)一個(gè)SpEL 表達(dá)式計(jì)算得到的; 2)為緩存編寫(xiě)SpEL 表達(dá)式的時(shí)候,spring 暴露了一些很有用的元數(shù)據(jù),下表列出了SpEL 中可用的 緩存元數(shù)據(jù)
對(duì)上表的分析(Analysis): A1)對(duì)于save()方法來(lái)說(shuō),我們需要的鍵是 所返回的 Spittle對(duì)象 的 id屬性; A2)表達(dá)式 #result 能夠得到返回的 Spittle,可以通過(guò)將 key 屬性設(shè)置為 #result.id 來(lái)引用id 屬性;代碼如下; @CachePut(value="spitterCache", key="#result.id") Spittle save(Spittle spittle);
【2.1.3】條件化緩存(某些case下,我們希望將緩存關(guān)閉) 1)intro:@Cacheable and @CachePut 提供了兩個(gè)屬性用以實(shí)現(xiàn)條件化緩存: unless and condition; 1.1)unless:如果unless屬性的 SpEL 表達(dá)式的值為 true,那么緩存方法返回的數(shù)據(jù)就不會(huì)放到緩存中; 1.2)condition:如果 condition 屬性的 SpEL 表達(dá)式的值為 false,那么對(duì)于這個(gè)方法緩存就會(huì)被禁用掉;
2)unless 和 condition 的差別(difference) d1)unless屬性:只能阻止將對(duì)象放進(jìn)緩存,但在這個(gè)方法調(diào)用的時(shí)候,依然會(huì)去緩存中進(jìn)行你查找,如果找到了匹配的值,就會(huì)返回找到的值;(干貨——unless是不準(zhǔn)進(jìn),但可以出) d2)condition屬性:一旦 condition的表達(dá)式結(jié)果為false,緩存就會(huì)被禁用,不進(jìn)也不出;(干貨——condition是不進(jìn)也不出) 3)看個(gè)荔枝:假設(shè)對(duì)于 message 屬性包含 ”NoCache“ 的 Spittle 對(duì)象,我們不想對(duì)其進(jìn)行緩存。為了阻止這樣的Spittle 對(duì)象被緩存起來(lái),可以這樣設(shè)置: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')") Spittle findOne(long id); 4)再看個(gè)荔枝:為了要對(duì) ID 小于 10 的Spittle 關(guān)閉緩存,可以在 @Cacheable 上使用 condition 屬性,如下所示: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')"condition="#id >= 10") Spittle findOne(long id);對(duì)以上代碼的分析(Analysis):? A1)如果findOne() 調(diào)用時(shí),參數(shù)值小于10,那么就不會(huì)在緩存中進(jìn)行查找:返回的 Spittle 也不會(huì)放入緩存中,就想這個(gè)方法沒(méi)有添加 @Cacheable 注解一樣; A2)如上例所示:unless屬性 的表達(dá)式能夠通過(guò) #result 引用返回值;之所以這么做是因?yàn)?unless屬性 只有在緩存方法有返回值時(shí) 才開(kāi)始發(fā)揮作用; A3)如上例所示:condition屬性: 肩負(fù)著在方法上禁用緩存的任務(wù),因此它不能等到方法返回時(shí)再確定是否該關(guān)閉緩存。這因?yàn)檫@它的表達(dá)式必須要在進(jìn)入方法前進(jìn)行計(jì)算,所以在condition屬性中不能引用 #result 返回值;(干貨——這里道出了 unless屬性和 condition屬性的有一大區(qū)別,即unless屬性的結(jié)果是 對(duì)方法調(diào)用后的引用,而condition屬性的結(jié)果是方法調(diào)用前的計(jì)算結(jié)果)
【2.2】移除緩存條目(使用 @CacheEvict 將緩存數(shù)據(jù)移除掉)(干貨——@CacheEvict 注解的作用) 1)intro:如果帶有 @CacheEvict注解的方法被調(diào)用的話(huà),那么會(huì)有一個(gè) 或 更多的條目會(huì)在緩存中移除; 2)在什么 case下 需要從緩存中移除內(nèi)容呢? 當(dāng)緩存值不再合法時(shí),我們應(yīng)該確保將其從緩存中移除,這樣的話(huà),后續(xù)的緩存命中就不會(huì)返回舊的 或 已經(jīng)布存在的值,其中一這樣的case 就是數(shù)據(jù)被刪除掉了。這樣的話(huà), SpittleRepository.remove()方法就是 使用 @CacheEvict 的絕佳選擇: @CacheEvict("spitterCache") void remove(long spittleId) Attention) A1)與 @Cacheable 和 @CachePut 不同:@CacheEvict 能夠應(yīng)用在 返回值 為 void 的 方法上,而 @Cacheable 和 @CachePut 需要非 void 的返回值;它將會(huì)作為放在緩存中的條目,因?yàn)?@CacheEvict 只是將條目從 緩存中移除,因此它可以放在任意的 方法上,甚至void 方法上; A2)對(duì)以上代碼的分析(Analysis):當(dāng)remove() 調(diào)用時(shí),會(huì)從 緩存中刪除一個(gè)條目,被刪除條目的key 與傳遞進(jìn)來(lái)的spittleId 的值要相等; A3)@CacheEvict有多個(gè)屬性,如下表所示:
【3】看個(gè)荔枝 1)?@Cacheable("spitterCache") 和 @CachePut("spitterCache")?注解的測(cè)試用例; public interface SpitterRepository {@CachePut("spitterCache")Spitter save(Spitter spitter);@Cacheable("spitterCache")Spitter findByUsername(String username);String findPassByUsername(String username);int getItemSum(); List<Spitter> findSpitters(int limit, int offset); } <?xml version="1.0" encoding="gbk"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="ehcache.xsd"><diskStore path="java.io.tmpdir" /><defaultCache maxElementsInMemory="10000" eternal="false"timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" /><cache name="spitterCache" maxElementsInMemory="10000"eternal="false" overflowToDisk="false" timeToIdleSeconds="900"timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LFU" /> </ehcache>
2)@CacheEvict注解移除緩存對(duì)象的測(cè)試用例; public interface SpitterRepository {@CachePut("spitterCache")Spitter save(Spitter spitter);@Cacheable("spitterCache")Spitter findByUsername(String username);String findPassByUsername(String username);int getItemSum();List<Spitter> findSpitters(int limit, int offset);@CacheEvict("spitterCache") // highlight line.int delete(String username); } @Overridepublic int delete(String username) { //defined in SpitterRepositoryImpl.javasql = "delete from t_spitter where username=?";int result = jdbc.update(sql, username);System.out.println("delete result = " + result);return result;} @RequestMapping(value = "/delete", method = RequestMethod.GET)public String removeSpitter( //defined in SpitterController.java.@RequestParam String username, @RequestParam(name="curpage") String curpage) {spitterRepository.delete(username);return "redirect:/spitter/paging?curpage=" + curpage;}
【4】使用XML 聲明緩存 1)why + reason: 1.1)why:為什么想要以 XML 的方式聲明緩存? 1.2)reasons: reason1)你可能會(huì)覺(jué)得 在 自己的源碼中添加 spring 的注解有點(diǎn)不太方便; reason2)你需要在沒(méi)有源碼的bean上應(yīng)用 緩存功能;
2)spring 的cache 命名空間提供了使用 XML 聲明緩存規(guī)則的方法:可以作為 面向注解緩存的替代方案,所以 cache 命名空間會(huì)與 spring 的 aop 命名空間結(jié)合起來(lái)使用,用來(lái)聲明緩存所應(yīng)用的切點(diǎn)在哪里; 3) 配置 XML 聲明的緩存,首先需要?jiǎng)?chuàng)建spring 配置文件,這個(gè)文件要包含 cache 和 aop 命名空間: <?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:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 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"> 4)cache 命名空間定義了在 spring xml 配置文件中聲明緩存的配置元素,下表列出了cache 命名空間所提供的所有元素:
5)下面的代碼展現(xiàn)了如何使用這些元素 為 SpittleRepository bean 配置緩存,其作用等同于 本章前面使用緩存注解的方式: <?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:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 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"> <aop:config> <aop:advisor advice-ref="cacheAdvice" <cache:annotation-driven> Enables annotation-driven caching. Equivalent to @EnableCaching in Java configuration. <cache:advice> Defines caching advice. Paired with <aop:advisor> to apply advice to a pointcut. <cache:caching> Defines a specific set of caching rules within the caching advice. <cache:cacheable> Designates a method as being cacheable. Equivalent to the @Cacheable annotation. <cache:cache-put> Designates a method as populating (but not considering) the cache. Equivalent to the @CachePut annotation. <cache:cache-evict> Designates a method as evicting one or more entries from the cache. Equivalent to the @CacheEvict annotation. Bind cache advice to a pointcut pointcut= "execution(* com.habuma.spittr.db.SpittleRepository.*(..))"/> </aop:config> <cache:advice id="cacheAdvice"> <cache:caching> <cache:cacheable cache="spittleCache" method="findRecent" /> <cache:cacheable cache="spittleCache" method="findOne" /> <cache:cacheable cache="spittleCache" method="findBySpitterId" /> <cache:cache-put cache="spittleCache" method="save" key="#result.id" /> <cache:cache-evict cache="spittleCache" method="remove" /> </cache:caching> </cache:advice> <bean id="cacheManager" class= "org.springframework.cache.concurrent.ConcurrentMapCacheManager" /> </beans>
【1】 啟用對(duì)緩存的支持 1)intro:spring對(duì) 緩存的支持有兩種方式: way1)注解驅(qū)動(dòng)的緩存; way2)XML 聲明的緩存
2)使用spring 的緩存抽象時(shí),最為通用的方式就是在方法上添加 @Cacheable 和 @CacheEvit注解; 2.1)在往bean上添加緩存注解的時(shí)候,必須要啟用 spring 對(duì)注解驅(qū)動(dòng)緩存的支持。如果使用 java配置的話(huà),可以在其中的一個(gè)配置類(lèi)上添加 @EnableCaching注解,這樣就能啟用注解驅(qū)動(dòng)緩存了。(干貨——@EnableCaching注解的作用) 2.2)java配置啟用注解驅(qū)動(dòng)的緩存,代碼如下所示: @Configuration @EnableCaching // 啟用緩存 public class CachingConfig {@Beanpublic CacheManager cacheManager() { // 聲明緩存管理器return new ConcurrentMapCacheManager();} } 2.3)xml 配置配置啟用注解驅(qū)動(dòng)的緩存,代碼如下所示: <?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:cache="http://www.springframework.org/schema/cache" 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"> <cache:annotation-driven /> <bean id="cacheManager" class= "org.springframework.cache.concurrent.ConcurrentMapCacheManager" /> </beans> 對(duì)以上代碼的分析(Analysis): A1)@EnableCaching and <cache:annotation-driven> 的工作方式是相同的;他們都會(huì)創(chuàng)建一個(gè)切面并觸發(fā) spring 緩存注解的切點(diǎn); A2) 根據(jù)所使用的 注解以及緩存的狀態(tài),這個(gè)切面會(huì)從緩存中獲取數(shù)據(jù),并將數(shù)據(jù)添加到緩存中或者從緩存中移除這個(gè)值; A3)以上的代碼還聲明了一個(gè) 緩存管理器的bean;(ConcurrentMapCacheManager)
【1.1】配置緩存管理器? 1)intro:spring3.1 內(nèi)置了 5個(gè) 緩存管理器實(shí)現(xiàn),如下所示:
- SimpleCacheManager
- NoOpCacheManager
- ConcurrentMapCacheManager
- CompositeCacheManager
- EhCacheCacheManager
- RedisCacheManager (from Spring Data Redis)
- GemfireCacheManager (from Spring Data GemFire)
【1.1.1】 使用 Ehcache 緩存 1)intro to Ehcache:Ehcache 是最為流行的緩存供應(yīng)商之一;spring 為其提供的緩存管理器 叫做 EhCacheCacheManager; 2)如何在java中對(duì)其配置(以java 配置的方式設(shè)置 EhCacheCacheManager) @Configuration @EnableCaching // 啟用緩存 public class CacheConfig {/** @Bean public CacheManager cacheManager() { // 聲明緩存管理器 return new* ConcurrentMapCacheManager(); }*/@Beanpublic EhCacheCacheManager cacheManager(CacheManager cm) {return new EhCacheCacheManager(cm);}@Bean public EhCacheManagerFactoryBean ehcache() { EhCacheManagerFactoryBean cacheFactory = new EhCacheManagerFactoryBean();cacheFactory.setConfigLocation(new ClassPathResource("com/spring/spittr/cache/ehcache.xml"));return cacheFactory; } } 對(duì)以上代碼的分析(Analysis):?我們需要?使用 EhCache的 CacheManager 來(lái)進(jìn)行注入;而spring提供了EhCacheManagerFactoryBean 來(lái)生成 EhCache 的 CacheManager;
3)除了在spring中配置 的bean,還需要針對(duì)EhCache 的配置。Ehcache 為 XML 定義了 自己的配置模式,需要在一個(gè) XML 文件中配置緩存,該文件需要符合 EhCache 所定義的模式; 3.1)在創(chuàng)建 EhCacheManagerFactoryBean,的過(guò)程中,需要告訴它 EhCache 配置文件在什么地方; 3.2)在這里,通過(guò)調(diào)用 setConfigLocation()方法,傳入 ClassPathResource,用來(lái)指明Ehcache XML 配置文件相對(duì)于根路徑的位置;(參見(jiàn)上一小節(jié)的代碼) 3.3)下面是一個(gè) ehcache.xml 文件的實(shí)例 <ehcache><cache name="spitterCache"maxBytesLocalHeap="50m"timeToLiveSeconds="100"></cache> </ehcache>
【1.1.2】 使用Redis 緩存 1)intro:Redis 作為 key-value存儲(chǔ),也非常適合 存儲(chǔ)緩存; 2)Redis 可以用來(lái)為 spring 緩存抽象機(jī)制存儲(chǔ)緩存條目:?spring data Redis 提供了 RedisCacheManager,這是 CacheManager 的一個(gè)實(shí)現(xiàn);RedisCacheManager 會(huì)與一個(gè) Redis 服務(wù)器協(xié)作,并通過(guò) RedisTemplate 將緩存條目存儲(chǔ)到 Redis中; 3)配置將緩存條目存儲(chǔ)在 Redis 服務(wù)器的緩存管理器 @Configuration @EnableCaching public class CachingConfig { @Bean public CacheManager cacheManager(RedisTemplate redisTemplate) { return new RedisCacheManager(redisTemplate); } @Bean public JedisConnectionFactory redisConnectionFactory() { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.afterPropertiesSet(); return jedisConnectionFactory; } @Bean public RedisTemplate<String, String> redisTemplate( RedisConnectionFactory redisCF) { RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>(); redisTemplate.setConnectionFactory(redisCF); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
【1.1.3】使用多個(gè)緩存管理器 1)intro to?CompositeCacheManager:CompositeCacheManager(復(fù)合型緩存管理器) 要通過(guò)一個(gè)或多個(gè)緩存管理器來(lái)進(jìn)行配置,它會(huì)迭代這些緩存管理器,以查找之前所緩存的值; 2)以下程序清單 展現(xiàn)了如何創(chuàng)建 CompositeCacheManager bean; @Bean public CacheManager cacheManager(net.sf.ehcache.CacheManager cm,javax.cache.CacheManager jcm) {CompositeCacheManager cacheManager = new CompositeCacheManager();List<CacheManager> managers = new ArrayList<CacheManager>();managers.add(new JCacheCacheManager(jcm));managers.add(new EhCacheCacheManager(cm))managers.add(new RedisCacheManager(redisTemplate()));cacheManager.setCacheManagers(managers);return cacheManager; } Attention)在配置完緩存管理器并啟用緩存后,就可以在 bean 方法上應(yīng)用緩存規(guī)則了;下面看一下如何使用 spring 的緩存注解來(lái)定義緩存邊界;
【2】 為方法添加注解以支持緩存 0)intro: spring 的緩存抽象在很大程度上是圍繞切面創(chuàng)建的 ;在spring中 啟用緩存時(shí),會(huì)創(chuàng)建一個(gè)切面,它觸發(fā)一個(gè)或多個(gè)spirng 的緩存注解; 1)下表列出了 spring 所提供的緩存注解:
【2.1】填充緩存 1)@Cacheable 和 @CachePut 都可以填充緩存,他們的工作方式略有差異(difference) d1)@Cacheable:首先在緩存中查找條目,如果找到了匹配的條目,那么就不會(huì)對(duì)方法進(jìn)行調(diào)用了;如果沒(méi)有找到匹配條目,方法會(huì)被調(diào)用并且返回值要放到緩存之中; d2)@CachePut:并不會(huì)在緩存中檢查匹配的值,目標(biāo)方法總是會(huì)被調(diào)用,并將返回值添加到緩存中;(干貨——@Cacheable 和 @CachePut 注解的作用) 2)@Cacheable 和 @CachePut共有的屬性如下:
對(duì)上表的分析(Analysis):在最簡(jiǎn)單的case下, 在?@Cacheable 和 @CachePut 的這些屬性中,只需要使用 value屬性 指定一個(gè)或多個(gè)緩存即可;
3)看個(gè)荔枝 @Cacheable("spittleCache") public Spittle findOne(long id) {try {return jdbcTemplate.queryForObject(SELECT_SPITTLE_BY_ID,new SpittleRowMapper(),id);} catch (EmptyResultDataAccessException e) {return null;} } 對(duì)以上代碼的分析(Analysis): A1)當(dāng)findOne()方法被調(diào)用時(shí),緩存切面會(huì)攔截調(diào)用并在緩存中查找之前以名?spittleCache 存儲(chǔ)的返回值; A2)緩存key 是傳遞到 findOne()方法中的id 參數(shù); A2.1)如果找到這個(gè)值的話(huà):那么方法不會(huì)再被調(diào)用; A2.2)如果沒(méi)有找到這個(gè)值的話(huà):那么就會(huì)調(diào)用這個(gè)方法,并將返回值放到緩存中,為下一次調(diào)用 findOne()方法做準(zhǔn)備;
4)以下代碼的problem+solution: 4.1)problem:@Cacheable的緩存作用只限于 JdbcSpittleRepository這個(gè)實(shí)現(xiàn)類(lèi)中,SpittleRepository的其他實(shí)現(xiàn)并沒(méi)有緩存功能,除非也為其添加上 @Cacheable注解; 4.2)solution:可以考慮將注解添加到 SpittleRepository的方法聲明上,而不是放在實(shí)現(xiàn)類(lèi)中(放在 頂層的接口中);(干貨——應(yīng)該將緩存注解Cacheable添加到Repository的接口聲明中) public interface SpitterRepository {@CachePut("spitterCache")Spitter save(Spitter spitter);@Cacheable("spitterCache")Spitter findByUsername(String username);@Cacheable("spitterCache")String findPassByUsername(String username); }
【2.1.1】 將值放到緩存中 1)intro: @CachePut注解:該注解的方法始終都會(huì)被調(diào)用,而且他的返回值也會(huì)放到緩存中。這提供了一種很便利的機(jī)制,能夠讓跟我們?cè)谡?qǐng)求前預(yù)先加載緩存;
2)@CachePut注解的荔枝: 當(dāng)一個(gè)client 調(diào)用了 save()方法保存 spitter 后,很可能馬上就會(huì)再次請(qǐng)求這條記錄。所以,當(dāng)save()方法 調(diào)用后,立即 將 spitter 塞到 緩存中是很有意義的。這樣當(dāng)其他人通過(guò) findOne() 方法進(jìn)行查找時(shí),它就已經(jīng)準(zhǔn)備就緒了;(干貨——@CachePut注解的作用) @CachePut("spitterCache") Spittle save(Spittle spittle);
【2.1.2】自定義緩存key 1)intro:Both @Cacheable and @CachePut 都有一個(gè)名為key的屬性,這個(gè)屬性能夠替換默認(rèn)的 key,這通過(guò)一個(gè)SpEL 表達(dá)式計(jì)算得到的; 2)為緩存編寫(xiě)SpEL 表達(dá)式的時(shí)候,spring 暴露了一些很有用的元數(shù)據(jù),下表列出了SpEL 中可用的 緩存元數(shù)據(jù)
對(duì)上表的分析(Analysis): A1)對(duì)于save()方法來(lái)說(shuō),我們需要的鍵是 所返回的 Spittle對(duì)象 的 id屬性; A2)表達(dá)式 #result 能夠得到返回的 Spittle,可以通過(guò)將 key 屬性設(shè)置為 #result.id 來(lái)引用id 屬性;代碼如下; @CachePut(value="spitterCache", key="#result.id") Spittle save(Spittle spittle);
【2.1.3】條件化緩存(某些case下,我們希望將緩存關(guān)閉) 1)intro:@Cacheable and @CachePut 提供了兩個(gè)屬性用以實(shí)現(xiàn)條件化緩存: unless and condition; 1.1)unless:如果unless屬性的 SpEL 表達(dá)式的值為 true,那么緩存方法返回的數(shù)據(jù)就不會(huì)放到緩存中; 1.2)condition:如果 condition 屬性的 SpEL 表達(dá)式的值為 false,那么對(duì)于這個(gè)方法緩存就會(huì)被禁用掉;
2)unless 和 condition 的差別(difference) d1)unless屬性:只能阻止將對(duì)象放進(jìn)緩存,但在這個(gè)方法調(diào)用的時(shí)候,依然會(huì)去緩存中進(jìn)行你查找,如果找到了匹配的值,就會(huì)返回找到的值;(干貨——unless是不準(zhǔn)進(jìn),但可以出) d2)condition屬性:一旦 condition的表達(dá)式結(jié)果為false,緩存就會(huì)被禁用,不進(jìn)也不出;(干貨——condition是不進(jìn)也不出) 3)看個(gè)荔枝:假設(shè)對(duì)于 message 屬性包含 ”NoCache“ 的 Spittle 對(duì)象,我們不想對(duì)其進(jìn)行緩存。為了阻止這樣的Spittle 對(duì)象被緩存起來(lái),可以這樣設(shè)置: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')") Spittle findOne(long id); 4)再看個(gè)荔枝:為了要對(duì) ID 小于 10 的Spittle 關(guān)閉緩存,可以在 @Cacheable 上使用 condition 屬性,如下所示: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')"condition="#id >= 10") Spittle findOne(long id);對(duì)以上代碼的分析(Analysis):? A1)如果findOne() 調(diào)用時(shí),參數(shù)值小于10,那么就不會(huì)在緩存中進(jìn)行查找:返回的 Spittle 也不會(huì)放入緩存中,就想這個(gè)方法沒(méi)有添加 @Cacheable 注解一樣; A2)如上例所示:unless屬性 的表達(dá)式能夠通過(guò) #result 引用返回值;之所以這么做是因?yàn)?unless屬性 只有在緩存方法有返回值時(shí) 才開(kāi)始發(fā)揮作用; A3)如上例所示:condition屬性: 肩負(fù)著在方法上禁用緩存的任務(wù),因此它不能等到方法返回時(shí)再確定是否該關(guān)閉緩存。這因?yàn)檫@它的表達(dá)式必須要在進(jìn)入方法前進(jìn)行計(jì)算,所以在condition屬性中不能引用 #result 返回值;(干貨——這里道出了 unless屬性和 condition屬性的有一大區(qū)別,即unless屬性的結(jié)果是 對(duì)方法調(diào)用后的引用,而condition屬性的結(jié)果是方法調(diào)用前的計(jì)算結(jié)果)
【2.2】移除緩存條目(使用 @CacheEvict 將緩存數(shù)據(jù)移除掉)(干貨——@CacheEvict 注解的作用) 1)intro:如果帶有 @CacheEvict注解的方法被調(diào)用的話(huà),那么會(huì)有一個(gè) 或 更多的條目會(huì)在緩存中移除; 2)在什么 case下 需要從緩存中移除內(nèi)容呢? 當(dāng)緩存值不再合法時(shí),我們應(yīng)該確保將其從緩存中移除,這樣的話(huà),后續(xù)的緩存命中就不會(huì)返回舊的 或 已經(jīng)布存在的值,其中一這樣的case 就是數(shù)據(jù)被刪除掉了。這樣的話(huà), SpittleRepository.remove()方法就是 使用 @CacheEvict 的絕佳選擇: @CacheEvict("spitterCache") void remove(long spittleId) Attention) A1)與 @Cacheable 和 @CachePut 不同:@CacheEvict 能夠應(yīng)用在 返回值 為 void 的 方法上,而 @Cacheable 和 @CachePut 需要非 void 的返回值;它將會(huì)作為放在緩存中的條目,因?yàn)?@CacheEvict 只是將條目從 緩存中移除,因此它可以放在任意的 方法上,甚至void 方法上; A2)對(duì)以上代碼的分析(Analysis):當(dāng)remove() 調(diào)用時(shí),會(huì)從 緩存中刪除一個(gè)條目,被刪除條目的key 與傳遞進(jìn)來(lái)的spittleId 的值要相等; A3)@CacheEvict有多個(gè)屬性,如下表所示:
【3】看個(gè)荔枝 1)?@Cacheable("spitterCache") 和 @CachePut("spitterCache")?注解的測(cè)試用例; public interface SpitterRepository {@CachePut("spitterCache")Spitter save(Spitter spitter);@Cacheable("spitterCache")Spitter findByUsername(String username);String findPassByUsername(String username);int getItemSum(); List<Spitter> findSpitters(int limit, int offset); } <?xml version="1.0" encoding="gbk"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="ehcache.xsd"><diskStore path="java.io.tmpdir" /><defaultCache maxElementsInMemory="10000" eternal="false"timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" /><cache name="spitterCache" maxElementsInMemory="10000"eternal="false" overflowToDisk="false" timeToIdleSeconds="900"timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LFU" /> </ehcache>
2)@CacheEvict注解移除緩存對(duì)象的測(cè)試用例; public interface SpitterRepository {@CachePut("spitterCache")Spitter save(Spitter spitter);@Cacheable("spitterCache")Spitter findByUsername(String username);String findPassByUsername(String username);int getItemSum();List<Spitter> findSpitters(int limit, int offset);@CacheEvict("spitterCache") // highlight line.int delete(String username); } @Overridepublic int delete(String username) { //defined in SpitterRepositoryImpl.javasql = "delete from t_spitter where username=?";int result = jdbc.update(sql, username);System.out.println("delete result = " + result);return result;} @RequestMapping(value = "/delete", method = RequestMethod.GET)public String removeSpitter( //defined in SpitterController.java.@RequestParam String username, @RequestParam(name="curpage") String curpage) {spitterRepository.delete(username);return "redirect:/spitter/paging?curpage=" + curpage;}
【4】使用XML 聲明緩存 1)why + reason: 1.1)why:為什么想要以 XML 的方式聲明緩存? 1.2)reasons: reason1)你可能會(huì)覺(jué)得 在 自己的源碼中添加 spring 的注解有點(diǎn)不太方便; reason2)你需要在沒(méi)有源碼的bean上應(yīng)用 緩存功能;
2)spring 的cache 命名空間提供了使用 XML 聲明緩存規(guī)則的方法:可以作為 面向注解緩存的替代方案,所以 cache 命名空間會(huì)與 spring 的 aop 命名空間結(jié)合起來(lái)使用,用來(lái)聲明緩存所應(yīng)用的切點(diǎn)在哪里; 3) 配置 XML 聲明的緩存,首先需要?jiǎng)?chuàng)建spring 配置文件,這個(gè)文件要包含 cache 和 aop 命名空間: <?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:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 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"> 4)cache 命名空間定義了在 spring xml 配置文件中聲明緩存的配置元素,下表列出了cache 命名空間所提供的所有元素:
5)下面的代碼展現(xiàn)了如何使用這些元素 為 SpittleRepository bean 配置緩存,其作用等同于 本章前面使用緩存注解的方式: <?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:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 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"> <aop:config> <aop:advisor advice-ref="cacheAdvice" <cache:annotation-driven> Enables annotation-driven caching. Equivalent to @EnableCaching in Java configuration. <cache:advice> Defines caching advice. Paired with <aop:advisor> to apply advice to a pointcut. <cache:caching> Defines a specific set of caching rules within the caching advice. <cache:cacheable> Designates a method as being cacheable. Equivalent to the @Cacheable annotation. <cache:cache-put> Designates a method as populating (but not considering) the cache. Equivalent to the @CachePut annotation. <cache:cache-evict> Designates a method as evicting one or more entries from the cache. Equivalent to the @CacheEvict annotation. Bind cache advice to a pointcut pointcut= "execution(* com.habuma.spittr.db.SpittleRepository.*(..))"/> </aop:config> <cache:advice id="cacheAdvice"> <cache:caching> <cache:cacheable cache="spittleCache" method="findRecent" /> <cache:cacheable cache="spittleCache" method="findOne" /> <cache:cacheable cache="spittleCache" method="findBySpitterId" /> <cache:cache-put cache="spittleCache" method="save" key="#result.id" /> <cache:cache-evict cache="spittleCache" method="remove" /> </cache:caching> </cache:advice> <bean id="cacheManager" class= "org.springframework.cache.concurrent.ConcurrentMapCacheManager" /> </beans>
總結(jié)
以上是生活随笔為你收集整理的spring(13)缓存数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ps文字怎么随意变化(ps文字怎么随意变
- 下一篇: ibatis(0)ibatis 与 my