当前位置: 首页>后端>正文

Spring本地缓存的使用方法

都有哪些Spring Cache

我们现在在用的Spring Cache,可以直接看Spring Boot提供的缓存枚举类,有如下这些:

public enum CacheType {
    GENERIC, // 使用的SimpleCacheManager(自己手动指定Cache,可任意类型Cache实现哦)
    JCACHE, // 使用org.springframework.cache.jcache.JCacheCacheManager
    EHCACHE, // 使用org.springframework.cache.ehcache.EhCacheCacheManager
    HAZELCAST, // 使用com.hazelcast.spring.cache.HazelcastCacheManager
    INFINISPAN, // 使用org.infinispan.spring.provider.SpringEmbeddedCacheManager
    COUCHBASE, // 使用com.couchbase.client.spring.cache.CouchbaseCacheManager
    REDIS, // 使用org.springframework.data.redis.cache.RedisCacheManager,依赖于RedisTemplate进行操作
    CAFFEINE, // 使用org.springframework.cache.caffeine.CaffeineCacheManager
    @Deprecated
    GUAVA, // 使用org.springframework.cache.guava.GuavaCacheManager,已经过期不推荐使用了
    SIMPLE, // 使用ConcurrentMapCacheManager
    NONE; // 使用NoOpCacheManager,表示禁用缓存
}

EhCache:一个纯Java的进程内缓存框架,所以也是基于本地缓存的。(注意EhCache2.x和EhCache3.x相互不兼容)。
Redis:分布式缓存,只有Client-Server(C\S)模式,Java一般使用Jedis/Luttuce来操纵。
Hazelcast:基于内存的数据网格。虽然它基于内存,但是分布式应用程序可以使用Hazelcast进行分布式缓存、同步、集群、处理、发布/订阅消息等。
Guava:它是Google Guava工具包中的一个非常方便易用的本地化缓存实现,基于LRU(最近最少使用)算法实现,支持多种缓存过期策略。在Spring5.X以后的版本已经将他标记为过期了。
Caffeine:是使用Java8对Guava缓存的重写版本,在Spring5中将取代了Guava,支持多种缓存过期策略。
SIMPLE:使用ConcurrentMapCacheManager,因为不支持缓存过期时间,所以做本地缓存基本不考虑该方式。

本地缓存的性能

关于分布式缓存,我们需要后面会专门讨论Redis的用法,这里只看本地缓存。性能从高到低,依次是Caffeine,Guava,ConcurrentMapCacheManager,其中Caffeine在读写上都快了Guava近一倍。

本地缓存的用法

这里我们只讨论在Spring Boot里面怎么整合使用Caffeine和EhCache。

Caffeine的整合使用

主要有以下几个步骤:

1)加依赖包:

<dependency> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency> 
<dependency> 
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

2)配置缓存:
这里有两种方法,通过文件配置或者在配置类里面配置,先看一下文件配置,我们可以写一个properties文件,内容像这样:

spring: 
 cache:
   type: CAFFEINE
   cache-names:
     - getPersonById
     - name2
 caffeine:
     spec: maximumSize=500,expireAfterWrite=5s

然后还要在主类中加上@EnableCaching注解:

@SpringBootApplication 
@EnableScheduling 
@EnableCaching 
public class MySpringBootApplication 

另外一种更灵活的方法是在配置类中配置:

package com.xjj.config; 

import java.util.ArrayList; 
import java.util.concurrent.TimeUnit; 
import org.springframework.cache.CacheManager; 
import org.springframework.cache.annotation.EnableCaching; 
import org.springframework.cache.caffeine.CaffeineCache; 
import org.springframework.cache.support.SimpleCacheManager; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.Primary; 
import com.github.benmanes.caffeine.cache.Caffeine; 

/**
 * Cache配置类,用于缓存数据
 */ 
@Configuration 
@EnableCaching 
public class CacheConfig { 
    public static final int DEFAULT_MAXSIZE = 50000;
    public static final int DEFAULT_TTL = 10;

   /**
   * 定義cache名称、超时时长(秒)、最大容量
   * 每个cache缺省:10秒超时、最多缓存50000条数据,需要修改可以在构造方法的 
  参数中指定。
   */
    public enum Caches{
        getPersonById(5), //有效期5秒 
        getSomething, //缺省10秒 
        getOtherthing(300, 1000), //5分钟,最大容量1000
 
        Caches() {
        }

        Caches(int ttl) {
            this.ttl = ttl;
        }

        Caches(int ttl, int maxSize) {
            this.ttl = ttl;
            this.maxSize = maxSize;
        }

        private int maxSize=DEFAULT_MAXSIZE; //最大数量 
        private int ttl=DEFAULT_TTL; //过期时间(秒) 
        public int getMaxSize() {
            return maxSize;
        }
        public int getTtl() {
            return ttl;
        }
    }

    /**
    * 创建基于Caffeine的Cache Manager
    * @return
    */
    @Bean
    @Primary
    public CacheManager caffeineCacheManager() {
    SimpleCacheManager cacheManager = new SimpleCacheManager();
    ArrayList<CaffeineCache> caches = new ArrayList<CaffeineCache>();

    for(Caches c : Caches.values()){
        caches.add(new CaffeineCache(c.name(),
        Caffeine.newBuilder().recordStats().expireAfterWrite(c.getTtl(), 
        TimeUnit.SECONDS).maximumSize(c.getMaxSize()).build()));
    }

    cacheManager.setCaches(caches);
    return cacheManager;
   }
} 

应用类:

package com.xjj.service; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.cache.annotation.Cacheable; 
import org.springframework.stereotype.Service; 
import com.xjj.dao.PersonDAO; 
import com.xjj.entity.Person; 

@Service 
public class PersonService { 
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private PersonDAO personDao;
   /**
   * 根据id获取Person对象,使用缓存
   * @param id
   * @return Person对象
   */
   @Cacheable(value="getPersonById", sync=true)
   public Person getPersonById(int id){
       logger.debug("getting data from database, personId={}", id);
       return personDao.getPersonById(id);
   }
}

测试类:

@Autowired 
PersonService personService; 

@Test 
public void localCacheTest() throws JsonProcessingException, InterruptedException{ 
   System.out.println("第一次:"); //从数据库中获取 
   Person p = personService.getPersonById(2);
   logger.info("1st time: {}", objectMapper.writeValueAsString(p));

   System.out.println("第二次:"); //从缓存中获取 
   p = personService.getPersonById(2);
   logger.info("2nd time: {}", objectMapper.writeValueAsString(p));
   Thread.sleep(5000);

   System.out.println("第三次:"); //5秒钟超时后,从数据库中获取 
   p = personService.getPersonById(2);
   logger.info("3rd time: {}", objectMapper.writeValueAsString(p));

   System.out.println("第四次:"); //从缓存中获取 
   p = personService.getPersonById(2);
   logger.info("4th time: {}", objectMapper.writeValueAsString(p));
} 
ehcache的整合使用

导入依赖包,分为2.x版本和3.x版本。
其中2.x版本做如下导入:

<dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
   <version>2.10.6</version>
</dependency>

3.x版本做如下导入:

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.7.1</version>
</dependency> 

导包完成后,我们使用JCacheManagerFactoryBean + ehcache.xml的方式配置:

@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {

    @Bean
    public JCacheManagerFactoryBean cacheManagerFactoryBean() throws IOException {
        JCacheManagerFactoryBean factoryBean = new JCacheManagerFactoryBean();
        // 配置全部写在ehcache.xml这个配置文件内
        factoryBean.setCacheManagerUri(new ClassPathResource("ehcache.xml").getURI());
        return factoryBean;
    }

    @Bean
    public CacheManager cacheManager(javax.cache.CacheManager cacheManager) {
        // 它必须要包装一个javax.cache.CacheManager,也就是Eh107CacheManager才行
        JCacheCacheManager cacheCacheManager = new JCacheCacheManager();
        // 方式一:使用`JCacheManagerFactoryBean` + xml配置文件的方式
        cacheCacheManager.setCacheManager(cacheManager);

        return cacheCacheManager;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
 xmlns='http://www.ehcache.org/v3'
 xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.1.xsd">

    <!-- <service>
    <jsr107:defaults>
    <jsr107:cache name="demoCache" template="heap-cache"/>
    </jsr107:defaults>
    </service> -->
    <cache-template name="heap-cache">
        <resources>
            <heap unit="entries">2000</heap>
            <offheap unit="MB">100</offheap>
        </resources>
    </cache-template>

    <!-- 注意:这个cache必须手动配置上,它并不会动态生成 -->
    <cache alias="demoCache" uses-template="heap-cache">
        <expiry>
            <ttl unit="seconds">40</ttl>
        </expiry>
    </cache>
</config>

参考资料:

https://blog.csdn.net/f641385712/article/details/94982916

http://www.360doc.com/content/17/1017/20/16915_695800687.shtml


https://www.xamrdz.com/backend/3m31940057.html

相关文章: