SpringBoot整合EhCache

文章目录

  • 前言
  • 构建SpringBoot工程
    • 导入依赖
    • 准备EhCache的配置项
    • 配置CachaManager
  • Cache注解使用
    • 基本使用
    • key的声明方式
      • Spel表达式语言实现
      • KeyGenerator实现
    • 缓存条件
      • condition
      • unless
      • condition&unless的优先级
      • sync
    • @CachePut
    • @CacheEvict
    • @Caching

前言

SpringBoot默认情况下是整合了EhCache的,但是默认整合的EhCache的2.x版本,本文依然整合EhCache的3.x版本。

构建SpringBoot工程

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starterartifactId>
    dependency>
    <dependency>
        <groupId>org.ehcachegroupId>
        <artifactId>ehcacheartifactId>
        <version>3.8.1version>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-cacheartifactId>
    dependency>
dependencies>

准备EhCache的配置项

# 准备EhCache基础配置项
ehcache:
  heap: 1000           # 堆内内存缓存个数
  off-heap: 10         # 对外内存存储大小 MB
  disk: 20             # 磁盘存储数据大小  MB
  diskDir: D:/data/    # 磁盘存储路径
  cacheNames:          # 基于CacheManager构建多少个缓存
    - user
    - item
    - card

引入配置文件中的配置项

@Component
@ConfigurationProperties(prefix = "ehcache")
public class EhCacheProps {

    private int heap;

    private int offheap;

    private int disk;

    private String diskDir;

    private Set<String> cacheNames;

}

配置CachaManager

@Configuration
@EnableCaching
public class EhCacheConfig {

    @Autowired
    private EhCacheProps ehCacheProps;

    @Bean
    public CacheManager ehCacheManager(){
        //1. 缓存名称
        Set<String> cacheNames = ehCacheProps.getCacheNames();

        //2. 设置内存存储位置和数量大小
        ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder()
                .heap(ehCacheProps.getHeap())
                .offheap(ehCacheProps.getOffheap(), MemoryUnit.MB)
                .disk(ehCacheProps.getDisk(),MemoryUnit.MB)
                .build();

        //3. 设置生存时间
        ExpiryPolicy expiry = ExpiryPolicyBuilder.noExpiration();

        //4. 设置CacheConfiguration
  		// baseObject是一个POJO类实现了序列化接口
        CacheConfiguration cacheConfiguration = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(String.class, BaseObject.class, resourcePools)
                .withExpiry(expiry)
                .build();

        //5. 设置磁盘存储的位置
        CacheManagerBuilder<PersistentCacheManager> cacheManagerBuilder =
                CacheManagerBuilder.newCacheManagerBuilder().with(CacheManagerBuilder.persistence(ehCacheProps.getDiskDir()));

        //6. 缓存名称设置好。
        for (String cacheName : cacheNames) {
            cacheManagerBuilder.withCache(cacheName,cacheConfiguration);
        }

        //7. 构建
        return cacheManagerBuilder.build();
    }
}

Cache注解使用

Cache注解是JSR规范中的,Spring支持这种注解。前面配置好关于CacheManager之后,就可以在Service层添加Cache注解,实现缓存使用,缓存更新,缓存清除。

基本使用

这个是查询缓存的注解,可以加在方法上,也可以加在类上(不建议添加在类上,这样很多细粒度配置就无法实现,比如@Transactional),可以在执行当前方法前,根据注解查看方法的返回内容是否已经被缓存,如果已经缓存,不需要执行业务代码,直接返回数据。如果没有命中缓存,正常执行业务代码,在执行完毕后,会将返回结果作为缓存,存储起来。

直接在Service层的方法上添加@Cacheable,注意,必须填写@Cacheable中的value或者cacheName属性

默认情况下,每次查询会基于Key(默认是方法的参数)去查看是否命中缓存

  • 如果命中缓存,直接返回
  • 如果未命中缓存,正常执行业务代码,基于方法返回结果做缓存

key的声明方式

key的声明方式有两种,一种是基于Spring的Expression Language去实现,另一种是基于编写类的方式动态的生成key

Spel表达式语言实现

@Override
@Cacheable(cacheNames = {"item"},key = "#id")    // 123
public String echo(String id,String... args) {
    System.out.println("查询数据库~");
    // itemMapper.findById(id);
    return id;
}

这种方式要基于Spel实现,但是Spel用的不多,单独为了这种操作熟悉Spel成本蛮高的,而且功能并不丰富,所以更推荐第二种方式,编写类的方式设置key的生成策略

KeyGenerator实现

这种方式需要在Spring容器中构建KeyGenerator实现类,基于注解配置进去即可

设置key的生成策略。

@Configuration
public class CacheKeyGenerator {

    @Bean(name = "itemKeyGenerator")
    public KeyGenerator itemKeyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName() + params[0];
            }
        };
    }
}

设置bean name到keyGenerator中

@Override
@Cacheable(cacheNames = {"item"},keyGenerator = "itemKeyGenerator")
public String echo(String id,String... args) {
    System.out.println("查询数据库~");
    // itemMapper.findById(id);
    return id;
}

缓存条件

在执行方法前后,判断当前数据是否需要缓存,所以一般基础参数的判断。

  • 条件为true代表缓存(condition)
  • 条件为false代表缓存(unless)

都可以基于Spel编写条件表达式

condition

在执行方法前,决定是否需要缓存

可以在condition中编写Spel,只要条件为true,既代表当前数据可以缓存

@Override
@Cacheable(cacheNames = {"item"},condition = "#id.equals(\"123\")")
public String echo(String id) {
    System.out.println("查询数据库~");
    // itemMapper.findById(id);
    return id;
}

unless

执行方法之后,决定是否需要缓存

unless也可以编写Spel,条件为false时,代表数据可以缓存,如果为true,代表数据不需要缓存

@Override
@Cacheable(cacheNames = {"item"},unless = "#result.equals(\"123\")")
public String echo(String id) {
    System.out.println("查询数据库~");
    // itemMapper.findById(id);
    return id;
}

更多的其实还是在执行查询前,来判断数据是否需要缓存。如果真的需要做,也是避免诡异的操作。

比如Service在出现异常结果时,返回-1,那么这种-1,就不需要缓存。

condition&unless的优先级

condition和unless都是代表是都需要缓存数据。

如果同时设置condition和unless。

  • condition,unless
  • true,false:unless代表不缓存,那就不缓存
  • true,true:都代表缓存,那就缓存
  • false,false:都不让缓存, 那就不缓存
  • false,true:condition代表不缓存数据,那就不缓存

condition和unless没有优先级之分,他的优先级在于,不缓存的优先级高于缓存。

sync

缓存击穿问题。

当多个线程并发访问一个Service方法时,发现当前方法没有缓存数据,此时会让一个线程去执行业务代码查询数据,扔到缓存中,后面线程再查询缓存

可以设置sync属性为true,代表当执行Service方法时,发现缓存没数据,那么就需要去竞争锁资源去执行业务代码,后续线程等待前置线程执行完,再去直接查询缓存即可

@Override
@Cacheable(cacheNames = {"item"},sync = true)
public String echo(String id) {
    System.out.println("查询数据库~");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return id;
}

@CachePut

@CachePut注解是在写数据之后,更新缓存的数据

在增删改的操作上追加@CachePut注解,会根据key去重置指定的缓存。

细节点就在于对标上查询方法的key

@Override
@CachePut(cacheNames = "item",key = "#item.id")
public String write(Item item) {
    // 写id为123的数据
    System.out.println("123被改成456");
    return "456";
}

@CacheEvict

@CacheEvict是用来清除缓存的,可以根据注解里的cacheNames和key来清除指定缓存,也可以清除整个cacheNames中的全部缓存

清除指定缓存

@Override
@CacheEvict(value = "item")
public void clear(String id) {
    System.out.println("清除缓存成功!");
}

清除全部缓存

@Override
@CacheEvict(value = "item",allEntries = true)
public void clearAll() {
    System.out.println("清除item中的全部缓存~!");
}

如果执行清除缓存过程中,业务代码出现异常,会导致无法正常清除缓存,可以设置一个属性来保证在方法业务执行之前,就将缓存正常清除beforeInvocation设置为true

@Override
@CacheEvict(value = "item",allEntries = true,beforeInvocation = true)
public void clearAll() {
    int i = 1 / 0;
    System.out.println("方法执行前,清除item中的全部缓存~!");
}

@Caching

一个组合数据,可以基于Caching实现@Cacheable,@CachePut以及@CacheEvict三个注解

你可能感兴趣的:(Java,spring,boot,spring,java)