SpringBoot如何整合咖啡因?

我们之前已经学习了咖啡因,本地缓存性能之王,也提到了springboot本地缓存默认使用的也是咖啡因,让我们看看今天的咖啡因如何与springbootintegrated通信。集成有两种方法:一种是我们直接引入ecaffeinedependency,然后使用ecaffeinmethod实现缓存。等同于使用本地api introecaffeineandspring Cacherely on,使用espringcacheannotated方法

介绍

我们以前学过咖啡因 咖啡因,本地缓存性能之王,也提到过弹簧靴默认情况下使用的本地缓存也是咖啡因让我们看看今天咖啡因如何与弹簧靴整合。

集成的咖啡因

咖啡因而且弹簧靴有两种整合方式:

  • 一种是我们直接介绍咖啡因依赖,然后使用咖啡因方法实现缓存。相当于使用本机api
  • 介绍咖啡因而且春天的缓存依赖,使用弹簧缓存带注释的方法实现缓存。弹簧缓存帮助我们封装咖啡因
    Pom文件导入

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-缓存</artifactId>
    </dependency>
    <dependency>
    <groupId>com.github.ben-manes.咖啡因</groupId>
    <artifactId>咖啡因</artifactId>
    <version>2.6.0</version>
    </dependency>
    第一种方法

    首先配置缓存,建立一个缓存对象,以及后续的添加、删除和关于缓存的查询都基于此缓存对象。

    @Configuration
    public class 缓存Config {
    @Bean
    public 缓存<String, Object> 咖啡因缓存() {
    return 咖啡因.新Builder()
    // Set a fixed time to expire after the last write or access
    .expireAfterWrite(60, TimeUnit.SECONDS)
    // Initial 缓存 space size
    .initialCapacity(100)
    // The maximum number 的 缓存d items
    .maximumSize(1000)
    .build();
    }

    第一种方法我们就不一一介绍了,基本用一下咖啡因缓存根据您自己的业务,操作以下方法

    以这种方式使用,它对代码是侵入性的。

    第二种方式
  • 它需要在SpingBoot启动类上进行标记EnableCaching注意,这个东西和许多框架是一样的,例如,我们集成达博还需要标记@EnableDubbole笔记等。

      @弹簧靴Application
    @EnableCaching
    public class DemoApplication {
    public static void main(String[] args) {
    春天Application.run(DemoApplication.class, args);
    }
  • 存在application.yml配置我们的缓存类型,过期时间,缓存策略等。

    spring:
    pr的iles:
    active: dev
    缓存:
    type: CAFFEINE
    咖啡因:
    spec: maximumSize=500,expireAfterAccess=600s

    如果我们不习惯使用这种配置方式,当然也可以使用JavaConfig替换配置文件的配置方法。

    @Configuration
    public class 缓存Config {
    @Bean
    public 缓存Manager 缓存Manager() {
    咖啡因缓存Manager 缓存Manager = 新 咖啡因缓存Manager();
    缓存Manager.set咖啡因(咖啡因.新Builder()
    // Set a fixed time to expire after the last write or access
    .expireAfterAccess(600, TimeUnit.SECONDS)
    // Initial 缓存 space size
    .initialCapacity(100)
    // The maximum number 的 缓存d items
    .maximumSize(500));
    return 缓存Manager;
    }

    接下来是如何在代码中使用这个缓存

    @Override
    @缓存Put(价值 = "user", 关键 = "#userDTO.id")
    public UserDTO save(UserDTO userDTO) {
    userRepository.save(userDTO);
    return userDTO;
    }
    @Override
    @缓存Evict(价值 = "user", 关键 = "#id")//2
    public void remove(Long id) {
    logger.info("Deleted the data 缓存 whose id 而且 关键 are " + id + "");
    }
    @Override
    @缓存able(价值 = "user",关键 = "#id")
    public UserDTO getUserById(Long id) {
    return userRepository.find一个(id);
    }

    在上面的代码中,我们可以看到有几个注释@缓存Put, @缓存Evict, @缓存able
    我们只需要在方法上标记这些注释,就可以使用缓存。让我们分别介绍这些注释。

    @缓存able

    @缓存able它可以标记在类上或方法上。当它在类上被标记时,就意味着这个类上的所有方法都将支持缓存。相同的
    当它作用于一个方法时,它表示该方法支持缓存。例如,在我们上面的代码中getUserById在这种方法中,第一次缓存中没有数据,因此我们将进行查询DB,但是第二个查询不会执行DB查询,但直接从缓存获取结果并返回。

    值属性
  • @缓存able价值必须指定该属性,该属性指示当前方法的返回值将缓存在何处缓存On,对应于缓存这个名字。

    关键
  • @缓存able关键有两种方法,一种是指定我们自己的显示关键,也有一种默认生成策略,默认生成策略为简单键Generator这个类,这个生成关键方法比较简单,我们可以看看它的源代码:

    public static Object generateKey(Object... params) {
    // If the method has no parameter 关键, it is a 新 简单键()
    if (params.length == 0) {
    return 简单键.EMPTY;
    }
    // If the method has only one parameter 关键 is the current parameter
    if (params.length == 1) {
    Object param = params[0];
    if (param != 零 && !param.getClass().isArray()) {
    return param;
    }
    }
    // If the 关键 has multiple parameters, the 关键 is 新 简单键, but the hashCode 而且 Equals methods 的 这 简单键 object are rewritten according to the parameters passed in by the method.
    return 新 简单键(params);
    }

    上面的代码还是很容易理解的,可以分为三种情况:

  • 如果方法没有参数,则新使用全局空简单键对象作为关键
  • 该方法只有一个参数,并且当前参数被用作关键
  • 方法参数大于1一,就一个简单键对象作为关键简单键当使用传入的参数重写时简单键hashCode而且=方法,
    至于你为什么要重写它,就照着做吧地图使用自定义对象作为关键什么时候必须重写hashCode而且=方法的原理是一样的,因为caffein也在…的帮助下ConcurrentHash地图履行,

    总结

    从上面的代码中,我们可以发现默认生成关键它只与我们传入的参数相关。如果我们在一个类中有多个不带参数的方法,然后我们使用默认的缓存生成策略,缓存将丢失。
    或者缓存会互相覆盖,也可能是这样ClassCastException因为它们都使用相同的关键. 例如,以下代码将抛出异常(ClassCastException)

    @缓存able(价值 = "user")
    public UserDTO getUser() {
    UserDTO userDTO = 新 UserDTO();
    userDTO.setUserName("Java Finance");
    return userDTO;
    }
    @缓存able(价值 = "user")
    public UserDTO2 getUser1() {
    UserDTO2 userDTO2 = 新 UserDTO2();
    userDTO2.setUserName2("javajr.cn");
    return userDTO2;
    }

    因此,一般不建议使用默认的缓存生成关键策略。如果我们必须使用它,我们最好自己重写它,带来方法名,等等。类似于以下代码:

    @Component
    public class MyKeyGenerator extends 简单键Generator {
    @Override
    public Object generate(Object target, Method 方法, Object... params) {
    Object generate = super.generate(target, 方法, params);
    String format = MessageFormat.format("{0}{1}{2}", method.toGenericString(), generate);
    return format;
    }
    自定义键

    我们可以通过春天EL表达式来指定我们的关键. 这里的EL表达式可以使用方法参数及其对应的属性。
    在使用方法参数时,可以直接使用“#参数名称& # 8220;或# 8221;#p参数索引“这是我们的建议:

    @缓存able(价值="user", 关键="#id")
    public UserDTO getUserById(Long id) {
    UserDTO userDTO = 新 UserDTO();
    userDTO.setUserName("java finance");
    return userDTO;
    }
    @缓存able(价值="user", 关键="#p0")
    public UserDTO getUserById1(Long id) {
    return 零;
    }
    @缓存able(价值="user", 关键="#userDTO.id")
    public UserDTO getUserById2(UserDTO userDTO) {
    return 零;
    }
    @缓存able(价值="user", 关键="#p0.id")
    public UserDTO getUserById3(UserDTO userDTO) {
    return 零;
    }
    @缓存Put

    @缓存Put指定的属性是和@缓存able是一样的,但两者之间有区别,@缓存Put被标记的方法不会首先查询缓存是否有值,而是每次都会先执行该方法,然后返回结果,结果也会被缓存。

    ![Insert picture description here]()

为什么是这样一个过程?我们可以去看看它的源代码。关键代码在这一行。

        缓存.ValueWrapper 缓存命中 = find缓存dItem(上下文.get(缓存ableOperation.class));

当我们用这个方法@缓存able当注释上下文里面会放缓存ableOperation将它添加进去,只有当context .get(缓存ableOperation.class)获取的内容不是空的时候,它才会从缓存中获取内容,否则缓存命中会直接返回. 至于上下文何时加入缓存ableOperation,让我们看一下# 弹簧缓存AnnotationParser parse缓存Annotations这种方法会理解。具体的源代码将不显示,感兴趣的人可以自己浏览。

@缓存Evict

删除缓存中的数据。它的用法与前面两个注释类似,都有值和键属性。应该注意的是,它还有一个附加属性beforeInvocation

  • beforeInvocation注意,该属性的默认值为假,这意味着在调用方法之前不会删除缓存,只有在成功执行方法之后才会删除缓存。设置为真正的如果调用该方法,则将在调用该方法之前删除缓存。成功执行该方法之后,还将调用以删除缓存,这是双重删除。如果方法执行异常,则不会删除缓存。
  • allEntrie是否清除所有缓存内容,默认值为,如指定为真正的,所有缓存将在方法调用后立即清除
@Caching

这是一个组合注释,集成了上述三个注释,有三个属性:可缓存,放置和驱逐,用于指定@缓存able@缓存Put而且@缓存Evict

总结

第二种方法是侵入式的,它的实现原理比较简单,就是通过方面的方法拦截器实现,拦截所有的方法,它的核心代码如下:它看起来像我们的业务代码。感兴趣的朋友也可以看看。

if (上下文.isSynchronized()) {
缓存OperationContext context = 上下文.get(缓存ableOperation.class).iterator().next();
if (isConditionPassing(context, 缓存OperationExpressionEvaluator.NO_RESULT)) {
Object 关键 = generateKey(context, 缓存OperationExpressionEvaluator.NO_RESULT);
缓存 缓存 = context.get缓存s().iterator().next();
try {
return wrap缓存Value(方法, 缓存.get(关键, () -> unwrapReturnValue(invokeOperation(invoker))));
}
catch (缓存.ValueRetrievalException ex) {
// The invoker wraps any Throwable in a ThrowableWrapper instance so we
// can just make sure that one bubbles up the stack.
throw (缓存OperationInvoker.ThrowableWrapper) ex.getCause();
}
}
else {
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}
// Process any early evictions
// Whether the beforeInvocation attribute is 真正的, if it is 真正的, delete the 缓存
process缓存Evicts(上下文.get(缓存EvictOperation.class), 真正的,
缓存OperationExpressionEvaluator.NO_RESULT);
// Check if we have a 缓存d item matching the conditions
缓存.ValueWrapper 缓存命中 = find缓存dItem(上下文.get(缓存ableOperation.class));
// Collect puts from any @缓存able miss, if no 缓存d item is found
List<缓存PutRequest> 缓存PutRequests = 新 LinkedList<>();
if (缓存命中 == 零) {
collectPutRequests(上下文.get(缓存ableOperation.class),
缓存OperationExpressionEvaluator.NO_RESULT, 缓存PutRequests);
}
Object 缓存Value;
Object returnValue;
if (缓存命中 != 零 && !has缓存Put(上下文)) {
// If there are no put requests, just use the 缓存 hit
缓存Value = 缓存命中.get();
returnValue = wrap缓存Value(方法, 缓存Value);
}
else {
// Invoke the method if we don't have a 缓存 hit
returnValue = invokeOperation(invoker);
缓存Value = unwrapReturnValue(returnValue);
}
// Collect any explicit @缓存Puts
collectPutRequests(上下文.get(缓存PutOperation.class), 缓存Value, 缓存PutRequests);
// Process any collected put requests, either from @缓存Put or a @缓存able miss
for (缓存PutRequest 缓存PutRequest : 缓存PutRequests) {
缓存PutRequest.apply(缓存Value);
}
// Process any late evictions
process缓存Evicts(上下文.get(缓存EvictOperation.class), 假, 缓存Value);
return returnValue;
}

完成

  • 由于我的无知,难免会出错。如果您发现了错误,请留言指出给我,我会改正的。
  • 如果你觉得文章还不错,你的转发、分享、欣赏、点赞、评论都是对我最大的鼓励。
  • 谢谢你的阅读,非常欢迎,谢谢你的关注。

站在巨人的肩膀上摘苹果:
https://www.cnblogs.com/fashf& # 8230; !评论

JAVA

IDEA配置Git项目的运行环境(非maven)(idea怎么配置git环境)

2023-1-18 13:22:21

JAVA

4 .你必须了解Redis集群解决方案及其优缺点(了解五大人格特质理论的内容以及其优缺点)

2023-1-18 13:24:16

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索