Spring Cache抽象-使用Java类注解的方式整合EhCache

简介: Spring Cache抽象-使用Java类注解的方式整合EhCache

概述


Spring Cache抽象-之缓存注解这篇博文中我们介绍了SpringCache抽象注解的使用方式


既然这是一个抽象,我们需要一个具体的缓存存储实现。比价流行的有:基于JDK java.util.concurrent.ConcurrentMap的缓存,EhCache,Gemfire缓存,Caffeine,Guava缓存和兼容JSR-107的缓存等等。这里我们使用Ehcache来实现这个缓存。


同时,我们使用EhCacheManagerFactoryBean的configLocation属性指定Ehcache的设置。如果未明确指定,则默认为ehcache.xml。


工程结构


20171004084326746.jpg


以及EhCache的配置文件:


20171004084352785.jpg

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")
    @Override
    public 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")
    @Override
    public 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)
    @Override
    public 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 {
    @Bean
    public CacheManager cacheManager() {
        return new EhCacheCacheManager(ehCacheCacheManager().getObject());
    }
    @Bean
    public 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;
    @Before
    public void initContext() {
        context = new AnnotationConfigApplicationContext(AppConfig.class);
    }
    @Test
    public 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"));
    }
    @After
    public 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

相关文章
|
1月前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
50 0
|
16天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
51 7
|
21天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
38 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
21天前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
59 5
|
23天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
36 4
|
28天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
44 8
|
23天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
69 2
|
23天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
40 2
|
26天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
31 2
|
27天前
|
JSON Java 数据格式
springboot常用注解
@RestController :修饰类,该控制器会返回Json数据 @RequestMapping(“/path”) :修饰类,该控制器的请求路径 @Autowired : 修饰属性,按照类型进行依赖注入 @PathVariable : 修饰参数,将路径值映射到参数上 @ResponseBody :修饰方法,该方法会返回Json数据 @RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中 @Controller@Service@Compont: 将类注册到ioc容器
下一篇
DataWorks