spring(13)缓存数据
生活随笔
收集整理的這篇文章主要介紹了
spring(13)缓存数据
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】README
1)本文部分文字描述轉自:“Spring In Action(中/英文版)”,旨在review ?“spring(13)緩存數據”?的相關知識;
2)緩存:緩存可以存儲經常會用到的信息,這樣每次需要的時候,這些info 都是立即可用的;
3)for complate source code, please visit ?https://github.com/pacosonTang/SpringInAction/tree/master/spring13;
【1】 啟用對緩存的支持 1)intro:spring對 緩存的支持有兩種方式: way1)注解驅動的緩存; way2)XML 聲明的緩存
2)使用spring 的緩存抽象時,最為通用的方式就是在方法上添加 @Cacheable 和 @CacheEvit注解; 2.1)在往bean上添加緩存注解的時候,必須要啟用 spring 對注解驅動緩存的支持。如果使用 java配置的話,可以在其中的一個配置類上添加 @EnableCaching注解,這樣就能啟用注解驅動緩存了。(干貨——@EnableCaching注解的作用) 2.2)java配置啟用注解驅動的緩存,代碼如下所示: @Configuration @EnableCaching // 啟用緩存 public class CachingConfig {@Beanpublic CacheManager cacheManager() { // 聲明緩存管理器return new ConcurrentMapCacheManager();} } 2.3)xml 配置配置啟用注解驅動的緩存,代碼如下所示: <?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> 對以上代碼的分析(Analysis): A1)@EnableCaching and <cache:annotation-driven> 的工作方式是相同的;他們都會創建一個切面并觸發 spring 緩存注解的切點; A2) 根據所使用的 注解以及緩存的狀態,這個切面會從緩存中獲取數據,并將數據添加到緩存中或者從緩存中移除這個值; A3)以上的代碼還聲明了一個 緩存管理器的bean;(ConcurrentMapCacheManager)
【1.1】配置緩存管理器? 1)intro:spring3.1 內置了 5個 緩存管理器實現,如下所示:
【1.1.1】 使用 Ehcache 緩存 1)intro to Ehcache:Ehcache 是最為流行的緩存供應商之一;spring 為其提供的緩存管理器 叫做 EhCacheCacheManager; 2)如何在java中對其配置(以java 配置的方式設置 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; } } 對以上代碼的分析(Analysis):?我們需要?使用 EhCache的 CacheManager 來進行注入;而spring提供了EhCacheManagerFactoryBean 來生成 EhCache 的 CacheManager;
3)除了在spring中配置 的bean,還需要針對EhCache 的配置。Ehcache 為 XML 定義了 自己的配置模式,需要在一個 XML 文件中配置緩存,該文件需要符合 EhCache 所定義的模式; 3.1)在創建 EhCacheManagerFactoryBean,的過程中,需要告訴它 EhCache 配置文件在什么地方; 3.2)在這里,通過調用 setConfigLocation()方法,傳入 ClassPathResource,用來指明Ehcache XML 配置文件相對于根路徑的位置;(參見上一小節的代碼) 3.3)下面是一個 ehcache.xml 文件的實例 <ehcache><cache name="spitterCache"maxBytesLocalHeap="50m"timeToLiveSeconds="100"></cache> </ehcache>
【1.1.2】 使用Redis 緩存 1)intro:Redis 作為 key-value存儲,也非常適合 存儲緩存; 2)Redis 可以用來為 spring 緩存抽象機制存儲緩存條目:?spring data Redis 提供了 RedisCacheManager,這是 CacheManager 的一個實現;RedisCacheManager 會與一個 Redis 服務器協作,并通過 RedisTemplate 將緩存條目存儲到 Redis中; 3)配置將緩存條目存儲在 Redis 服務器的緩存管理器 @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】使用多個緩存管理器 1)intro to?CompositeCacheManager:CompositeCacheManager(復合型緩存管理器) 要通過一個或多個緩存管理器來進行配置,它會迭代這些緩存管理器,以查找之前所緩存的值; 2)以下程序清單 展現了如何創建 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 方法上應用緩存規則了;下面看一下如何使用 spring 的緩存注解來定義緩存邊界;
【2】 為方法添加注解以支持緩存 0)intro: spring 的緩存抽象在很大程度上是圍繞切面創建的 ;在spring中 啟用緩存時,會創建一個切面,它觸發一個或多個spirng 的緩存注解; 1)下表列出了 spring 所提供的緩存注解:
【2.1】填充緩存 1)@Cacheable 和 @CachePut 都可以填充緩存,他們的工作方式略有差異(difference) d1)@Cacheable:首先在緩存中查找條目,如果找到了匹配的條目,那么就不會對方法進行調用了;如果沒有找到匹配條目,方法會被調用并且返回值要放到緩存之中; d2)@CachePut:并不會在緩存中檢查匹配的值,目標方法總是會被調用,并將返回值添加到緩存中;(干貨——@Cacheable 和 @CachePut 注解的作用) 2)@Cacheable 和 @CachePut共有的屬性如下:
對上表的分析(Analysis):在最簡單的case下, 在?@Cacheable 和 @CachePut 的這些屬性中,只需要使用 value屬性 指定一個或多個緩存即可;
3)看個荔枝 @Cacheable("spittleCache") public Spittle findOne(long id) {try {return jdbcTemplate.queryForObject(SELECT_SPITTLE_BY_ID,new SpittleRowMapper(),id);} catch (EmptyResultDataAccessException e) {return null;} } 對以上代碼的分析(Analysis): A1)當findOne()方法被調用時,緩存切面會攔截調用并在緩存中查找之前以名?spittleCache 存儲的返回值; A2)緩存key 是傳遞到 findOne()方法中的id 參數; A2.1)如果找到這個值的話:那么方法不會再被調用; A2.2)如果沒有找到這個值的話:那么就會調用這個方法,并將返回值放到緩存中,為下一次調用 findOne()方法做準備;
4)以下代碼的problem+solution: 4.1)problem:@Cacheable的緩存作用只限于 JdbcSpittleRepository這個實現類中,SpittleRepository的其他實現并沒有緩存功能,除非也為其添加上 @Cacheable注解; 4.2)solution:可以考慮將注解添加到 SpittleRepository的方法聲明上,而不是放在實現類中(放在 頂層的接口中);(干貨——應該將緩存注解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注解:該注解的方法始終都會被調用,而且他的返回值也會放到緩存中。這提供了一種很便利的機制,能夠讓跟我們在請求前預先加載緩存;
2)@CachePut注解的荔枝: 當一個client 調用了 save()方法保存 spitter 后,很可能馬上就會再次請求這條記錄。所以,當save()方法 調用后,立即 將 spitter 塞到 緩存中是很有意義的。這樣當其他人通過 findOne() 方法進行查找時,它就已經準備就緒了;(干貨——@CachePut注解的作用) @CachePut("spitterCache") Spittle save(Spittle spittle);
【2.1.2】自定義緩存key 1)intro:Both @Cacheable and @CachePut 都有一個名為key的屬性,這個屬性能夠替換默認的 key,這通過一個SpEL 表達式計算得到的; 2)為緩存編寫SpEL 表達式的時候,spring 暴露了一些很有用的元數據,下表列出了SpEL 中可用的 緩存元數據
對上表的分析(Analysis): A1)對于save()方法來說,我們需要的鍵是 所返回的 Spittle對象 的 id屬性; A2)表達式 #result 能夠得到返回的 Spittle,可以通過將 key 屬性設置為 #result.id 來引用id 屬性;代碼如下; @CachePut(value="spitterCache", key="#result.id") Spittle save(Spittle spittle);
【2.1.3】條件化緩存(某些case下,我們希望將緩存關閉) 1)intro:@Cacheable and @CachePut 提供了兩個屬性用以實現條件化緩存: unless and condition; 1.1)unless:如果unless屬性的 SpEL 表達式的值為 true,那么緩存方法返回的數據就不會放到緩存中; 1.2)condition:如果 condition 屬性的 SpEL 表達式的值為 false,那么對于這個方法緩存就會被禁用掉;
2)unless 和 condition 的差別(difference) d1)unless屬性:只能阻止將對象放進緩存,但在這個方法調用的時候,依然會去緩存中進行你查找,如果找到了匹配的值,就會返回找到的值;(干貨——unless是不準進,但可以出) d2)condition屬性:一旦 condition的表達式結果為false,緩存就會被禁用,不進也不出;(干貨——condition是不進也不出) 3)看個荔枝:假設對于 message 屬性包含 ”NoCache“ 的 Spittle 對象,我們不想對其進行緩存。為了阻止這樣的Spittle 對象被緩存起來,可以這樣設置: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')") Spittle findOne(long id); 4)再看個荔枝:為了要對 ID 小于 10 的Spittle 關閉緩存,可以在 @Cacheable 上使用 condition 屬性,如下所示: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')"condition="#id >= 10") Spittle findOne(long id);對以上代碼的分析(Analysis):? A1)如果findOne() 調用時,參數值小于10,那么就不會在緩存中進行查找:返回的 Spittle 也不會放入緩存中,就想這個方法沒有添加 @Cacheable 注解一樣; A2)如上例所示:unless屬性 的表達式能夠通過 #result 引用返回值;之所以這么做是因為 unless屬性 只有在緩存方法有返回值時 才開始發揮作用; A3)如上例所示:condition屬性: 肩負著在方法上禁用緩存的任務,因此它不能等到方法返回時再確定是否該關閉緩存。這因為這它的表達式必須要在進入方法前進行計算,所以在condition屬性中不能引用 #result 返回值;(干貨——這里道出了 unless屬性和 condition屬性的有一大區別,即unless屬性的結果是 對方法調用后的引用,而condition屬性的結果是方法調用前的計算結果)
【2.2】移除緩存條目(使用 @CacheEvict 將緩存數據移除掉)(干貨——@CacheEvict 注解的作用) 1)intro:如果帶有 @CacheEvict注解的方法被調用的話,那么會有一個 或 更多的條目會在緩存中移除; 2)在什么 case下 需要從緩存中移除內容呢? 當緩存值不再合法時,我們應該確保將其從緩存中移除,這樣的話,后續的緩存命中就不會返回舊的 或 已經布存在的值,其中一這樣的case 就是數據被刪除掉了。這樣的話, SpittleRepository.remove()方法就是 使用 @CacheEvict 的絕佳選擇: @CacheEvict("spitterCache") void remove(long spittleId) Attention) A1)與 @Cacheable 和 @CachePut 不同:@CacheEvict 能夠應用在 返回值 為 void 的 方法上,而 @Cacheable 和 @CachePut 需要非 void 的返回值;它將會作為放在緩存中的條目,因為 @CacheEvict 只是將條目從 緩存中移除,因此它可以放在任意的 方法上,甚至void 方法上; A2)對以上代碼的分析(Analysis):當remove() 調用時,會從 緩存中刪除一個條目,被刪除條目的key 與傳遞進來的spittleId 的值要相等; A3)@CacheEvict有多個屬性,如下表所示:
【3】看個荔枝 1)?@Cacheable("spitterCache") 和 @CachePut("spitterCache")?注解的測試用例; 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注解移除緩存對象的測試用例; 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)你可能會覺得 在 自己的源碼中添加 spring 的注解有點不太方便; reason2)你需要在沒有源碼的bean上應用 緩存功能;
2)spring 的cache 命名空間提供了使用 XML 聲明緩存規則的方法:可以作為 面向注解緩存的替代方案,所以 cache 命名空間會與 spring 的 aop 命名空間結合起來使用,用來聲明緩存所應用的切點在哪里; 3) 配置 XML 聲明的緩存,首先需要創建spring 配置文件,這個文件要包含 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)下面的代碼展現了如何使用這些元素 為 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】 啟用對緩存的支持 1)intro:spring對 緩存的支持有兩種方式: way1)注解驅動的緩存; way2)XML 聲明的緩存
2)使用spring 的緩存抽象時,最為通用的方式就是在方法上添加 @Cacheable 和 @CacheEvit注解; 2.1)在往bean上添加緩存注解的時候,必須要啟用 spring 對注解驅動緩存的支持。如果使用 java配置的話,可以在其中的一個配置類上添加 @EnableCaching注解,這樣就能啟用注解驅動緩存了。(干貨——@EnableCaching注解的作用) 2.2)java配置啟用注解驅動的緩存,代碼如下所示: @Configuration @EnableCaching // 啟用緩存 public class CachingConfig {@Beanpublic CacheManager cacheManager() { // 聲明緩存管理器return new ConcurrentMapCacheManager();} } 2.3)xml 配置配置啟用注解驅動的緩存,代碼如下所示: <?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> 對以上代碼的分析(Analysis): A1)@EnableCaching and <cache:annotation-driven> 的工作方式是相同的;他們都會創建一個切面并觸發 spring 緩存注解的切點; A2) 根據所使用的 注解以及緩存的狀態,這個切面會從緩存中獲取數據,并將數據添加到緩存中或者從緩存中移除這個值; A3)以上的代碼還聲明了一個 緩存管理器的bean;(ConcurrentMapCacheManager)
【1.1】配置緩存管理器? 1)intro:spring3.1 內置了 5個 緩存管理器實現,如下所示:
- SimpleCacheManager
- NoOpCacheManager
- ConcurrentMapCacheManager
- CompositeCacheManager
- EhCacheCacheManager
- RedisCacheManager (from Spring Data Redis)
- GemfireCacheManager (from Spring Data GemFire)
【1.1.1】 使用 Ehcache 緩存 1)intro to Ehcache:Ehcache 是最為流行的緩存供應商之一;spring 為其提供的緩存管理器 叫做 EhCacheCacheManager; 2)如何在java中對其配置(以java 配置的方式設置 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; } } 對以上代碼的分析(Analysis):?我們需要?使用 EhCache的 CacheManager 來進行注入;而spring提供了EhCacheManagerFactoryBean 來生成 EhCache 的 CacheManager;
3)除了在spring中配置 的bean,還需要針對EhCache 的配置。Ehcache 為 XML 定義了 自己的配置模式,需要在一個 XML 文件中配置緩存,該文件需要符合 EhCache 所定義的模式; 3.1)在創建 EhCacheManagerFactoryBean,的過程中,需要告訴它 EhCache 配置文件在什么地方; 3.2)在這里,通過調用 setConfigLocation()方法,傳入 ClassPathResource,用來指明Ehcache XML 配置文件相對于根路徑的位置;(參見上一小節的代碼) 3.3)下面是一個 ehcache.xml 文件的實例 <ehcache><cache name="spitterCache"maxBytesLocalHeap="50m"timeToLiveSeconds="100"></cache> </ehcache>
【1.1.2】 使用Redis 緩存 1)intro:Redis 作為 key-value存儲,也非常適合 存儲緩存; 2)Redis 可以用來為 spring 緩存抽象機制存儲緩存條目:?spring data Redis 提供了 RedisCacheManager,這是 CacheManager 的一個實現;RedisCacheManager 會與一個 Redis 服務器協作,并通過 RedisTemplate 將緩存條目存儲到 Redis中; 3)配置將緩存條目存儲在 Redis 服務器的緩存管理器 @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】使用多個緩存管理器 1)intro to?CompositeCacheManager:CompositeCacheManager(復合型緩存管理器) 要通過一個或多個緩存管理器來進行配置,它會迭代這些緩存管理器,以查找之前所緩存的值; 2)以下程序清單 展現了如何創建 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 方法上應用緩存規則了;下面看一下如何使用 spring 的緩存注解來定義緩存邊界;
【2】 為方法添加注解以支持緩存 0)intro: spring 的緩存抽象在很大程度上是圍繞切面創建的 ;在spring中 啟用緩存時,會創建一個切面,它觸發一個或多個spirng 的緩存注解; 1)下表列出了 spring 所提供的緩存注解:
【2.1】填充緩存 1)@Cacheable 和 @CachePut 都可以填充緩存,他們的工作方式略有差異(difference) d1)@Cacheable:首先在緩存中查找條目,如果找到了匹配的條目,那么就不會對方法進行調用了;如果沒有找到匹配條目,方法會被調用并且返回值要放到緩存之中; d2)@CachePut:并不會在緩存中檢查匹配的值,目標方法總是會被調用,并將返回值添加到緩存中;(干貨——@Cacheable 和 @CachePut 注解的作用) 2)@Cacheable 和 @CachePut共有的屬性如下:
對上表的分析(Analysis):在最簡單的case下, 在?@Cacheable 和 @CachePut 的這些屬性中,只需要使用 value屬性 指定一個或多個緩存即可;
3)看個荔枝 @Cacheable("spittleCache") public Spittle findOne(long id) {try {return jdbcTemplate.queryForObject(SELECT_SPITTLE_BY_ID,new SpittleRowMapper(),id);} catch (EmptyResultDataAccessException e) {return null;} } 對以上代碼的分析(Analysis): A1)當findOne()方法被調用時,緩存切面會攔截調用并在緩存中查找之前以名?spittleCache 存儲的返回值; A2)緩存key 是傳遞到 findOne()方法中的id 參數; A2.1)如果找到這個值的話:那么方法不會再被調用; A2.2)如果沒有找到這個值的話:那么就會調用這個方法,并將返回值放到緩存中,為下一次調用 findOne()方法做準備;
4)以下代碼的problem+solution: 4.1)problem:@Cacheable的緩存作用只限于 JdbcSpittleRepository這個實現類中,SpittleRepository的其他實現并沒有緩存功能,除非也為其添加上 @Cacheable注解; 4.2)solution:可以考慮將注解添加到 SpittleRepository的方法聲明上,而不是放在實現類中(放在 頂層的接口中);(干貨——應該將緩存注解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注解:該注解的方法始終都會被調用,而且他的返回值也會放到緩存中。這提供了一種很便利的機制,能夠讓跟我們在請求前預先加載緩存;
2)@CachePut注解的荔枝: 當一個client 調用了 save()方法保存 spitter 后,很可能馬上就會再次請求這條記錄。所以,當save()方法 調用后,立即 將 spitter 塞到 緩存中是很有意義的。這樣當其他人通過 findOne() 方法進行查找時,它就已經準備就緒了;(干貨——@CachePut注解的作用) @CachePut("spitterCache") Spittle save(Spittle spittle);
【2.1.2】自定義緩存key 1)intro:Both @Cacheable and @CachePut 都有一個名為key的屬性,這個屬性能夠替換默認的 key,這通過一個SpEL 表達式計算得到的; 2)為緩存編寫SpEL 表達式的時候,spring 暴露了一些很有用的元數據,下表列出了SpEL 中可用的 緩存元數據
對上表的分析(Analysis): A1)對于save()方法來說,我們需要的鍵是 所返回的 Spittle對象 的 id屬性; A2)表達式 #result 能夠得到返回的 Spittle,可以通過將 key 屬性設置為 #result.id 來引用id 屬性;代碼如下; @CachePut(value="spitterCache", key="#result.id") Spittle save(Spittle spittle);
【2.1.3】條件化緩存(某些case下,我們希望將緩存關閉) 1)intro:@Cacheable and @CachePut 提供了兩個屬性用以實現條件化緩存: unless and condition; 1.1)unless:如果unless屬性的 SpEL 表達式的值為 true,那么緩存方法返回的數據就不會放到緩存中; 1.2)condition:如果 condition 屬性的 SpEL 表達式的值為 false,那么對于這個方法緩存就會被禁用掉;
2)unless 和 condition 的差別(difference) d1)unless屬性:只能阻止將對象放進緩存,但在這個方法調用的時候,依然會去緩存中進行你查找,如果找到了匹配的值,就會返回找到的值;(干貨——unless是不準進,但可以出) d2)condition屬性:一旦 condition的表達式結果為false,緩存就會被禁用,不進也不出;(干貨——condition是不進也不出) 3)看個荔枝:假設對于 message 屬性包含 ”NoCache“ 的 Spittle 對象,我們不想對其進行緩存。為了阻止這樣的Spittle 對象被緩存起來,可以這樣設置: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')") Spittle findOne(long id); 4)再看個荔枝:為了要對 ID 小于 10 的Spittle 關閉緩存,可以在 @Cacheable 上使用 condition 屬性,如下所示: @Cacheable(value="spitterCache"unless="#result.message.contains('NoCache')"condition="#id >= 10") Spittle findOne(long id);對以上代碼的分析(Analysis):? A1)如果findOne() 調用時,參數值小于10,那么就不會在緩存中進行查找:返回的 Spittle 也不會放入緩存中,就想這個方法沒有添加 @Cacheable 注解一樣; A2)如上例所示:unless屬性 的表達式能夠通過 #result 引用返回值;之所以這么做是因為 unless屬性 只有在緩存方法有返回值時 才開始發揮作用; A3)如上例所示:condition屬性: 肩負著在方法上禁用緩存的任務,因此它不能等到方法返回時再確定是否該關閉緩存。這因為這它的表達式必須要在進入方法前進行計算,所以在condition屬性中不能引用 #result 返回值;(干貨——這里道出了 unless屬性和 condition屬性的有一大區別,即unless屬性的結果是 對方法調用后的引用,而condition屬性的結果是方法調用前的計算結果)
【2.2】移除緩存條目(使用 @CacheEvict 將緩存數據移除掉)(干貨——@CacheEvict 注解的作用) 1)intro:如果帶有 @CacheEvict注解的方法被調用的話,那么會有一個 或 更多的條目會在緩存中移除; 2)在什么 case下 需要從緩存中移除內容呢? 當緩存值不再合法時,我們應該確保將其從緩存中移除,這樣的話,后續的緩存命中就不會返回舊的 或 已經布存在的值,其中一這樣的case 就是數據被刪除掉了。這樣的話, SpittleRepository.remove()方法就是 使用 @CacheEvict 的絕佳選擇: @CacheEvict("spitterCache") void remove(long spittleId) Attention) A1)與 @Cacheable 和 @CachePut 不同:@CacheEvict 能夠應用在 返回值 為 void 的 方法上,而 @Cacheable 和 @CachePut 需要非 void 的返回值;它將會作為放在緩存中的條目,因為 @CacheEvict 只是將條目從 緩存中移除,因此它可以放在任意的 方法上,甚至void 方法上; A2)對以上代碼的分析(Analysis):當remove() 調用時,會從 緩存中刪除一個條目,被刪除條目的key 與傳遞進來的spittleId 的值要相等; A3)@CacheEvict有多個屬性,如下表所示:
【3】看個荔枝 1)?@Cacheable("spitterCache") 和 @CachePut("spitterCache")?注解的測試用例; 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注解移除緩存對象的測試用例; 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)你可能會覺得 在 自己的源碼中添加 spring 的注解有點不太方便; reason2)你需要在沒有源碼的bean上應用 緩存功能;
2)spring 的cache 命名空間提供了使用 XML 聲明緩存規則的方法:可以作為 面向注解緩存的替代方案,所以 cache 命名空間會與 spring 的 aop 命名空間結合起來使用,用來聲明緩存所應用的切點在哪里; 3) 配置 XML 聲明的緩存,首先需要創建spring 配置文件,這個文件要包含 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)下面的代碼展現了如何使用這些元素 為 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>
總結
以上是生活随笔為你收集整理的spring(13)缓存数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ps文字怎么随意变化(ps文字怎么随意变
- 下一篇: ibatis(0)ibatis 与 my