作者:超级小豆丁\
起源:http://www.mydlq.club/article...

环境配置:

  • JDK 版本:1.8
  • Caffeine 版本:2.8.0
  • SpringBoot 版本:2.2.2.RELEASE

一、本地缓存介绍

缓存在日常开发中启动至关重要的作用,因为是存储在内存中,数据的读取速度是十分快的,能大量缩小对数据库的拜访,缩小数据库的压力。

之前介绍过 Redis 这种 NoSql 作为缓存组件,它可能很好的作为分布式缓存组件提供多个服务间的缓存,然而 Redis 这种还是须要网络开销,减少时耗。本地缓存是间接从本地内存中读取,没有网络开销,例如秒杀零碎或者数据量小的缓存等,比近程缓存更适合。

二、缓存组件 Caffeine 介绍

按 Caffeine Github 文档形容,Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官网放弃了 Guava,而应用了性能更优良的 Caffeine 作为默认缓存组件。

1、Caffeine 性能

能够通过下图观测到,在上面缓存组件中 Caffeine 性能是其中最好的。

2、Caffeine 配置阐明

参数类型形容
initialCapacityinteger初始的缓存空间大小
maximumSizelong缓存的最大条数
maximumWeightlong缓存的最大权重
expireAfterAccessduration最初一次写入或拜访后通过固定工夫过期
refreshAfterWriteduration最初一次写入后通过固定工夫过期
refreshAfterWriteduration创立缓存或者最近一次更新缓存后通过固定的工夫距离,刷新缓存
weakKeysboolean关上 key 的弱援用
weakValuesboolean关上 value 的弱援用
softValuesboolean关上 value 的软援用
recordStats-开发统计性能

留神:

  • weakValuessoftValues 不能够同时应用。
  • maximumSizemaximumWeight 不能够同时应用。
  • expireAfterWriteexpireAfterAccess 共事存在时,以 expireAfterWrite 为准。

3、软援用与弱援用

  • 软援用: 如果一个对象只具备软援用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间有余了,就会回收这些对象的内存。
  • 弱援用: 弱援用的对象领有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具备弱援用的对象,不论以后内存空间足够与否,都会回收它的内存
// 软援用Caffeine.newBuilder().softValues().build();// 弱援用Caffeine.newBuilder().weakKeys().weakValues().build();

三、SpringBoot 集成 Caffeine 两种形式

SpringBoot 有俩种应用 Caffeine 作为缓存的形式:

形式一: 间接引入 Caffeine 依赖,而后应用 Caffeine 办法实现缓存。

形式二: 引入 Caffeine 和 Spring Cache 依赖,应用 SpringCache 注解办法实现缓存。

上面将介绍下,这俩中集成形式都是如何实现的。

Spring Boot 根底就不介绍了,举荐看下这个教程:

https://github.com/javastacks...

四、SpringBoot 集成 Caffeine 形式一

1、Maven 引入相干依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.2.2.RELEASE</version>    </parent>    <groupId>mydlq.club</groupId>    <artifactId>springboot-caffeine-cache-example-1</artifactId>    <version>0.0.1</version>    <name>springboot-caffeine-cache-example-1</name>    <description>Demo project for Spring Boot Cache</description>    <properties>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>com.github.ben-manes.caffeine</groupId>            <artifactId>caffeine</artifactId>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

2、配置缓存配置类

import com.github.benmanes.caffeine.cache.Cache;import com.github.benmanes.caffeine.cache.Caffeine;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;@Configurationpublic class CacheConfig {    @Bean    public Cache<String, Object> caffeineCache() {        return Caffeine.newBuilder()                // 设置最初一次写入或拜访后通过固定工夫过期                .expireAfterWrite(60, TimeUnit.SECONDS)                // 初始的缓存空间大小                .initialCapacity(100)                // 缓存的最大条数                .maximumSize(1000)                .build();    }}

3、定义测试的实体对象

import lombok.Data;import lombok.ToString;@Data@ToStringpublic class UserInfo {    private Integer id;    private String name;    private String sex;    private Integer age;}

4、定义服务接口类和实现类

UserInfoService

import mydlq.club.example.entity.UserInfo;public interface UserInfoService {    /**     * 减少用户信息     *     * @param userInfo 用户信息     */    void addUserInfo(UserInfo userInfo);    /**     * 获取用户信息     *     * @param id 用户ID     * @return 用户信息     */    UserInfo getByName(Integer id);    /**     * 批改用户信息     *     * @param userInfo 用户信息     * @return 用户信息     */    UserInfo updateUserInfo(UserInfo userInfo);    /**     * 删除用户信息     *     * @param id 用户ID     */    void deleteById(Integer id);}

UserInfoServiceImpl

import com.github.benmanes.caffeine.cache.Cache;import lombok.extern.slf4j.Slf4j;import mydlq.club.example.entity.UserInfo;import mydlq.club.example.service.UserInfoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.util.StringUtils;import java.util.HashMap;@Slf4j@Servicepublic class UserInfoServiceImpl implements UserInfoService {    /**     * 模仿数据库存储数据     */    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();    @Autowired    Cache<String, Object> caffeineCache;    @Override    public void addUserInfo(UserInfo userInfo) {        log.info("create");        userInfoMap.put(userInfo.getId(), userInfo);        // 退出缓存        caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);    }    @Override    public UserInfo getByName(Integer id) {        // 先从缓存读取        caffeineCache.getIfPresent(id);        UserInfo userInfo = (UserInfo) caffeineCache.asMap().get(String.valueOf(id));        if (userInfo != null){            return userInfo;        }        // 如果缓存中不存在,则从库中查找        log.info("get");        userInfo = userInfoMap.get(id);        // 如果用户信息不为空,则退出缓存        if (userInfo != null){            caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);        }        return userInfo;    }    @Override    public UserInfo updateUserInfo(UserInfo userInfo) {        log.info("update");        if (!userInfoMap.containsKey(userInfo.getId())) {            return null;        }        // 取旧的值        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());        // 替换内容        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {            oldUserInfo.setAge(userInfo.getAge());        }        if (!StringUtils.isEmpty(oldUserInfo.getName())) {            oldUserInfo.setName(userInfo.getName());        }        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {            oldUserInfo.setSex(userInfo.getSex());        }        // 将新的对象存储,更新旧对象信息        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);        // 替换缓存中的值        caffeineCache.put(String.valueOf(oldUserInfo.getId()),oldUserInfo);        return oldUserInfo;    }    @Override    public void deleteById(Integer id) {        log.info("delete");        userInfoMap.remove(id);        // 从缓存中删除        caffeineCache.asMap().remove(String.valueOf(id));    }}

5、测试的 Controller 类

import mydlq.club.example.entity.UserInfo;import mydlq.club.example.service.UserInfoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;@RestController@RequestMappingpublic class UserInfoController {    @Autowired    private UserInfoService userInfoService;    @GetMapping("/userInfo/{id}")    public Object getUserInfo(@PathVariable Integer id) {        UserInfo userInfo = userInfoService.getByName(id);        if (userInfo == null) {            return "没有该用户";        }        return userInfo;    }    @PostMapping("/userInfo")    public Object createUserInfo(@RequestBody UserInfo userInfo) {        userInfoService.addUserInfo(userInfo);        return "SUCCESS";    }    @PutMapping("/userInfo")    public Object updateUserInfo(@RequestBody UserInfo userInfo) {        UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);        if (newUserInfo == null){            return "不存在该用户";        }        return newUserInfo;    }    @DeleteMapping("/userInfo/{id}")    public Object deleteUserInfo(@PathVariable Integer id) {        userInfoService.deleteById(id);        return "SUCCESS";    }}

五、SpringBoot 集成 Caffeine 形式二

1、Maven 引入相干依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.2.2.RELEASE</version>    </parent>    <groupId>mydlq.club</groupId>    <artifactId>springboot-caffeine-cache-example-2</artifactId>    <version>0.0.1</version>    <name>springboot-caffeine-cache-example-2</name>    <description>Demo project for Spring Boot caffeine</description>    <properties>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <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>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

2、配置缓存配置类

@Configurationpublic class CacheConfig {    /**     * 配置缓存管理器     *     * @return 缓存管理器     */    @Bean("caffeineCacheManager")    public CacheManager cacheManager() {        CaffeineCacheManager cacheManager = new CaffeineCacheManager();        cacheManager.setCaffeine(Caffeine.newBuilder()                // 设置最初一次写入或拜访后通过固定工夫过期                .expireAfterAccess(60, TimeUnit.SECONDS)                // 初始的缓存空间大小                .initialCapacity(100)                // 缓存的最大条数                .maximumSize(1000));        return cacheManager;    }}

3、定义测试的实体对象

@Data@ToStringpublic class UserInfo {    private Integer id;    private String name;    private String sex;    private Integer age;}

4、定义服务接口类和实现类

服务接口

import mydlq.club.example.entity.UserInfo;public interface UserInfoService {    /**     * 减少用户信息     *     * @param userInfo 用户信息     */    void addUserInfo(UserInfo userInfo);    /**     * 获取用户信息     *     * @param id 用户ID     * @return 用户信息     */    UserInfo getByName(Integer id);    /**     * 批改用户信息     *     * @param userInfo 用户信息     * @return 用户信息     */    UserInfo updateUserInfo(UserInfo userInfo);    /**     * 删除用户信息     *     * @param id 用户ID     */    void deleteById(Integer id);}

服务实现类

import lombok.extern.slf4j.Slf4j;import mydlq.club.example.entity.UserInfo;import mydlq.club.example.service.UserInfoService;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import org.springframework.util.StringUtils;import java.util.HashMap;@Slf4j@Service@CacheConfig(cacheNames = "caffeineCacheManager")public class UserInfoServiceImpl implements UserInfoService {    /**     * 模仿数据库存储数据     */    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();    @Override    @CachePut(key = "#userInfo.id")    public void addUserInfo(UserInfo userInfo) {        log.info("create");        userInfoMap.put(userInfo.getId(), userInfo);    }    @Override    @Cacheable(key = "#id")    public UserInfo getByName(Integer id) {        log.info("get");        return userInfoMap.get(id);    }    @Override    @CachePut(key = "#userInfo.id")    public UserInfo updateUserInfo(UserInfo userInfo) {        log.info("update");        if (!userInfoMap.containsKey(userInfo.getId())) {            return null;        }        // 取旧的值        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());        // 替换内容        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {            oldUserInfo.setAge(userInfo.getAge());        }        if (!StringUtils.isEmpty(oldUserInfo.getName())) {            oldUserInfo.setName(userInfo.getName());        }        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {            oldUserInfo.setSex(userInfo.getSex());        }        // 将新的对象存储,更新旧对象信息        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);        // 返回新对象信息        return oldUserInfo;    }    @Override    @CacheEvict(key = "#id")    public void deleteById(Integer id) {        log.info("delete");        userInfoMap.remove(id);    }}

5、测试的 Controller 类

import mydlq.club.example.entity.UserInfo;import mydlq.club.example.service.UserInfoService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;@RestController@RequestMappingpublic class UserInfoController {    @Autowired    private UserInfoService userInfoService;    @GetMapping("/userInfo/{id}")    public Object getUserInfo(@PathVariable Integer id) {        UserInfo userInfo = userInfoService.getByName(id);        if (userInfo == null) {            return "没有该用户";        }        return userInfo;    }    @PostMapping("/userInfo")    public Object createUserInfo(@RequestBody UserInfo userInfo) {        userInfoService.addUserInfo(userInfo);        return "SUCCESS";    }    @PutMapping("/userInfo")    public Object updateUserInfo(@RequestBody UserInfo userInfo) {        UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);        if (newUserInfo == null){            return "不存在该用户";        }        return newUserInfo;    }    @DeleteMapping("/userInfo/{id}")    public Object deleteUserInfo(@PathVariable Integer id) {        userInfoService.deleteById(id);        return "SUCCESS";    }}

参考地址:

https://www.jianshu.com/p/c72...\
https://www.cnblogs.com/ricki...\
https://github.com/my-dlq/blo...

近期热文举荐:

1.Java 15 正式公布, 14 个新个性,刷新你的认知!!

2.终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3.我用 Java 8 写了一段逻辑,共事直呼看不懂,你试试看。。

4.吊打 Tomcat ,Undertow 性能很炸!!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!