Spring Cache抽象-使用Java类注解的方式整合EhCache
- 概述
- 工程結構
- 源碼
概述
Spring Cache抽象-之緩存注解這篇博文中我們介紹了SpringCache抽象注解的使用方式
既然這是一個抽象,我們需要一個具體的緩存存儲實現。比價流行的有:基于JDK java.util.concurrent.ConcurrentMap的緩存,EhCache,Gemfire緩存,Caffeine,Guava緩存和兼容JSR-107的緩存等等。這里我們使用Ehcache來實現這個緩存。
同時,我們使用EhCacheManagerFactoryBean的configLocation屬性指定Ehcache的設置。如果未明確指定,則默認為ehcache.xml。
工程結構
以及EhCache的配置文件:
pom.xml 關鍵的依賴
<properties> <springframework.version>4.3.9.RELEASE</springframework.version><ehcache.version>2.10.4</ehcache.version> </properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${springframework.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${springframework.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${springframework.version}</version></dependency><!-- EHCache --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>${ehcache.version}</version></dependency><!-- SLF4J/Logback --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.7</version></dependency> </dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.2</version><configuration><source>1.7</source><target>1.7</target></configuration></plugin></plugins></build> </project>ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"monitoring="autodetect" dynamicConfig="true"><diskStore path="java.io.tmpdir" /><cache name="products" maxEntriesLocalHeap="100"maxEntriesLocalDisk="1000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600"memoryStoreEvictionPolicy="LFU" transactionalMode="off"><persistence strategy="localTempSwap" /></cache> </ehcache>我們設置一個名為’products’的緩存。
最多100個products將保存在內存[堆疊]存儲中,
最多1000個products將被保留在DiskStore中
指定的路徑為“java.io.tmpdir”,它指的是默認的臨時文件路徑。
如果product閑置超過5分鐘,壽命超過10分鐘,products緩存將會過期
實體類
package com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain;import java.io.Serializable;public class Product implements Serializable {private static final long serialVersionUID = 123L;private String name;private double price;/*** * * @Title:Product* * @Description:構造函數* * @param name* @param price*/public Product(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}}Product接口
package com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.service;import com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product;public interface ProductService {Product getByName(String name);Product updateProduct(Product product);void refreshAllProducts(); }接口實現類 及緩存配置
package com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.service;import java.util.ArrayList; import java.util.List;import org.apache.log4j.Logger; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service;import com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product;/*** * * @ClassName: ProductServiceImpl* * @Description:@Service標注的服務層* * @author: Mr.Yang* * @date: 2017年10月3日 下午5:22:30*/@Service("productService") public class ProductServiceImpl implements ProductService {private static final Logger logger = Logger.getLogger(ProductServiceImpl.class);private static List<Product> products;static {products = getDummyProducts();}@Cacheable(cacheNames = "products", key = "#name", condition = "#name != 'HTC'", unless = "#result==null")@Overridepublic Product getByName(String name) {logger.info("<!----------Entering getByName()--------------------->");for (Product product : products) {if (product.getName().equalsIgnoreCase(name)) {return product;}}return null;}@CachePut(cacheNames = "products", key = "#product.name", unless = "#result==null")@Overridepublic Product updateProduct(Product product) {logger.info("<!----------Entering updateProduct()--------------------->");for (Product p : products) {if (p.getName().equalsIgnoreCase(product.getName())) {p.setPrice(product.getPrice());return p;}}return null;}@CacheEvict(cacheNames = "products", allEntries = true)@Overridepublic void refreshAllProducts() {}private static List<Product> getDummyProducts() {products = new ArrayList<Product>();products.add(new Product("IPhone", 500));products.add(new Product("Samsung", 600));products.add(new Product("HTC", 800));return products;}}關鍵配置類 ,以及加載enhance
package com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.configuration;import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource;@EnableCaching @Configuration @ComponentScan(basePackages = "com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache") public class AppConfig {@Beanpublic CacheManager cacheManager() {return new EhCacheCacheManager(ehCacheCacheManager().getObject());}@Beanpublic EhCacheManagerFactoryBean ehCacheCacheManager() {EhCacheManagerFactoryBean factory = new EhCacheManagerFactoryBean();factory.setConfigLocation(new ClassPathResource("ehcache/ehcache.xml"));factory.setShared(true);return factory;} }單元測試
package com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache;import org.apache.log4j.Logger; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.AbstractApplicationContext;import com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.configuration.AppConfig; import com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product; import com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.service.ProductService;public class SpringCacheWithEhCacheTest {private static final Logger logger = Logger.getLogger(SpringCacheWithEhCacheTest.class);AbstractApplicationContext context = null;@Beforepublic void initContext() {context = new AnnotationConfigApplicationContext(AppConfig.class);}@Testpublic void test() {ProductService service = (ProductService) context.getBean("productService");logger.info("IPhone ->" + service.getByName("IPhone"));logger.info("IPhone ->" + service.getByName("IPhone"));logger.info("IPhone ->" + service.getByName("IPhone"));logger.info("HTC ->" + service.getByName("HTC"));logger.info("HTC ->" + service.getByName("HTC"));logger.info("HTC ->" + service.getByName("HTC"));Product product = new Product("IPhone", 550);service.updateProduct(product);logger.info("IPhone ->" + service.getByName("IPhone"));logger.info("IPhone ->" + service.getByName("IPhone"));logger.info("IPhone ->" + service.getByName("IPhone"));logger.info("Refreshing all products");service.refreshAllProducts();logger.info("IPhone [after refresh]->" + service.getByName("IPhone"));logger.info("IPhone [after refresh]->" + service.getByName("IPhone"));logger.info("IPhone [after refresh]->" + service.getByName("IPhone"));}@Afterpublic void releaseContext() {((AbstractApplicationContext) context).close();}}輸出結果分析
2017-10-03 20:54:55,026 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7bd1a567: startup date [Tue Oct 03 20:54:55 BOT 2017]; root of context hierarchy 2017-10-03 20:54:55,858 INFO [main] (EhCacheManagerFactoryBean.java:130) - Initializing EhCache CacheManager 2017-10-03 20:54:56,711 INFO [main] (ProductServiceImpl.java:40) - <!----------Entering getByName()---------------------> 2017-10-03 20:54:56,715 INFO [main] (SpringCacheWithEhCacheTest.java:32) - IPhone ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,716 INFO [main] (SpringCacheWithEhCacheTest.java:33) - IPhone ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,716 INFO [main] (SpringCacheWithEhCacheTest.java:34) - IPhone ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,717 INFO [main] (ProductServiceImpl.java:40) - <!----------Entering getByName()---------------------> 2017-10-03 20:54:56,717 INFO [main] (SpringCacheWithEhCacheTest.java:36) - HTC ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@68c06cac 2017-10-03 20:54:56,717 INFO [main] (ProductServiceImpl.java:40) - <!----------Entering getByName()---------------------> 2017-10-03 20:54:56,717 INFO [main] (SpringCacheWithEhCacheTest.java:37) - HTC ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@68c06cac 2017-10-03 20:54:56,718 INFO [main] (ProductServiceImpl.java:40) - <!----------Entering getByName()---------------------> 2017-10-03 20:54:56,718 INFO [main] (SpringCacheWithEhCacheTest.java:38) - HTC ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@68c06cac 2017-10-03 20:54:56,724 INFO [main] (ProductServiceImpl.java:52) - <!----------Entering updateProduct()---------------------> 2017-10-03 20:54:56,734 INFO [main] (SpringCacheWithEhCacheTest.java:43) - IPhone ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,735 INFO [main] (SpringCacheWithEhCacheTest.java:44) - IPhone ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,735 INFO [main] (SpringCacheWithEhCacheTest.java:45) - IPhone ->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,736 INFO [main] (SpringCacheWithEhCacheTest.java:47) - Refreshing all products 2017-10-03 20:54:56,741 INFO [main] (ProductServiceImpl.java:40) - <!----------Entering getByName()---------------------> 2017-10-03 20:54:56,741 INFO [main] (SpringCacheWithEhCacheTest.java:50) - IPhone [after refresh]->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,742 INFO [main] (SpringCacheWithEhCacheTest.java:51) - IPhone [after refresh]->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,742 INFO [main] (SpringCacheWithEhCacheTest.java:52) - IPhone [after refresh]->com.xgj.cache.springCacheAnno.CompleteDemoWithEhCache.domain.Product@1a8f392 2017-10-03 20:54:56,742 INFO [main] (AbstractApplicationContext.java:984) - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7bd1a567: startup date [Tue Oct 03 20:54:55 BOT 2017]; root of context hierarchy 2017-10-03 20:54:56,744 INFO [main] (EhCacheManagerFactoryBean.java:187) - Shutting down EhCache CacheManager查看ProductServiceImpl中的 getName方法中的@Cacheable注解可知
@Cacheable(cacheNames = "products", key = "#name", condition = "#name != 'HTC'", unless = "#result==null")HTC不緩存, 結果為空不緩存。
查看輸出,第一次查詢 IPhone Samsung HTC ,分別從慢速設備中加載, 當第二次第三次查詢IPhone Samsung ,可以看到 并沒有輸出
logger.info("<!----------Entering getByName()--------------------->");可知,其從緩存中加載。
因為不緩存HTC,所以每次查詢HTC都從會執行方法,從慢速設備中查詢。
當調用service.updateProduct(product); 我們使用的@CachePut注解更新緩存, 然后service.getByName(“IPhone”),緩存沒有被清空,所以依然是從緩存中獲取。
隨后,service.refreshAllProducts(); 將緩存全部清掉,再此查詢service.getByName(“IPhone”),然后再此查詢可以看到輸出了<!----------Entering getByName()--------------------->,緊接著的第二次第三次,是從緩存中獲取的數據.
源碼
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
總結
以上是生活随笔為你收集整理的Spring Cache抽象-使用Java类注解的方式整合EhCache的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cache抽象-缓存注解
- 下一篇: Spring Cache抽象-缓存管理器