gpt4 book ai didi

java - 如何使用Java Config手动添加Spring CacheInterceptor?

转载 作者:行者123 更新时间:2023-12-02 02:26:53 25 4
gpt4 key购买 nike

我正在尝试找出如何在第三方Java类上的方法调用中添加缓存。我正在为我的应用程序使用Spring Boot。

我尝试使用此类来实现缓存工作。

package test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheOperation;
import org.springframework.cache.interceptor.CacheProxyFactoryBean;
import org.springframework.cache.interceptor.CacheableOperation;
import org.springframework.cache.interceptor.NameMatchCacheOperationSource;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collection;
import java.util.Date;
import java.util.HashSet;

@SpringBootApplication
@EnableCaching
@Configuration
public class MyApp {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MyApp.class, args);
Greeter greeter = context.getBean(Greeter.class);

System.out.println(new Date() + " : " + greeter.getGreeting("Bob"));
System.out.println(new Date() + " : " +greeter.getGreeting("Fred"));

System.out.println(new Date() + " : " +greeter.getGreeting("Bob"));
System.out.println(new Date() + " : " +greeter.getGreeting("Fred"));

System.out.println(new Date() + " : " +greeter.getGreeting("Bob"));
System.out.println(new Date() + " : " +greeter.getGreeting("Fred"));
}

@Bean
public Greeter greeter() {
final NameMatchCacheOperationSource nameMatchCacheOperationSource = new NameMatchCacheOperationSource();
Collection<CacheOperation> cacheOperations = new HashSet<CacheOperation>();
cacheOperations.add(new CacheableOperation.Builder().build());
nameMatchCacheOperationSource.addCacheMethod("*", cacheOperations);

CacheProxyFactoryBean cacheProxyFactoryBean = new CacheProxyFactoryBean();
cacheProxyFactoryBean.setTarget(new MySlowGreeter());
cacheProxyFactoryBean.setProxyInterfaces(new Class[] {Greeter.class});
cacheProxyFactoryBean.setCacheOperationSources(nameMatchCacheOperationSource);
cacheProxyFactoryBean.afterPropertiesSet();
return (Greeter) cacheProxyFactoryBean.getObject();
}

interface Greeter {
String getGreeting(String name);
}

class MySlowGreeter implements Greeter {
public String getGreeting(String name) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello " + name;
}
}
}


希望是,我能够在Spring配置中创建一个Bean,该Bean将对 Greeter.getGreeting(..)的调用包装起来并返回缓存的结果(如果存在)。但是,不会进行缓存。

有任何想法吗?

最佳答案

好吧,我有更多信息给您。但是首先,我想在上面的代码中解决一些问题。

1)第一个问题涉及在应用程序o.s.cache.interceptor.CacheProxyFactoryBean类(即“ MyApp”)的“ greeter” @Bean定义中使用@Configuration

每当您使用Spring的FactoryBeans中的1个(例如CacheProxyFactoryBean)或自己实现时,从@Bean方法返回的是FactoryBean本身,而不是FactoryBean的乘积。因此,您将返回return factoryBean.getObject()而不是FactoryBean,就像这样...

@Bean
GreeterFactoryBean greeter() {
GreeterFactoryBean factoryBean = new GreeterFactoryBean();
factoryBean.set...
return factoryBean;
}


在这里, GreeterFactoryBean实现 o.s.beans.factory.FactoryBean

正如Spring的 Reference Documentation指出的那样,Spring容器知道要返回 FactoryBean的乘积(例如[Singleton] Greeter实例)而不是 FactoryBean本身,作为Spring容器中的“已定义” bean为此 @Bean方法。如果未使用 @Bean明确定义(例如 @Bean),则bean的名称将是 @Bean("Greeter")方法的名称。

如果 FactoryBean还实现了Spring的生命周期回调接口(例如 o.s.beans.factory.InitializingBeano.s.beans.factory.DisposableBean等),则Spring容器将知道在Spring容器的初始化过程中的“适当”时间调用这些生命周期回调。

因此,没有必要在“ greeter” CacheProxyFactoryBean.afterPropertiesSet()定义内调用 CacheProxyFactoryBean.getObject()@Bean。这样做实际上违反了Spring容器的初始化约定,因此您可能会遇到过早的“初始化”问题,尤其是如果提供的 FactoryBean实现了其他Spring容器接口(例如 BeanClassLoaderAwareEnvironmentAware等),则尤其如此。 )。

小心!

2)其次,这不是您的示例所要解决的问题/问题,而是要意识到的问题。您在此SO帖子中指出,您正在尝试将“缓存”行为添加到第三方库类。

仅当您能够自己在应用程序中实例化第三方类(例如 Greeter)时,以上使用的方法才适用。

但是,如果第三方库或框架代表您实例化该类,则由于配置了库/框架(例如,认为JDBC Driver和Hibernate),您将无法在应用程序中向此类引入缓存行为,除非您使用的 Load-Time Weaving(LTW)。阅读文档以获取更多详细信息。

好了,到解决方案上。

我编写了一个测试来重现此问题并更好地了解Spring框架内部发生的情况。您可以找到我完成的测试 here

TestConfigurationOne实际上与您采用的相同方法
以编程方式创建缓存代理,并根据我在上文中所讨论的内容进行修改,并解决我认为是核心Spring Framework中的错误的地方(注意:我在测试中使用的是Spring Framework 5.0.1.RELEASE)。

为了使您的配置方法与 CacheProxyFactoryBean一起使用,我需要 extend the CacheProxyFactoryBean class。除了 extendingCacheProxyFactoryBean之外,我还需要对 implementSmartInitializingSingleton interface进行 BeanFactoryAware interface,其原因很快就会显现出来。有关 9,请参见 complete implementation

在内部,Spring Framework的 o.s.cache.interceptor.CacheProxyFactoryBeano.s.cache.interceptor.CacheInterceptormaking use。它还将“初始化”此 CacheInterceptor实例 herehere。但是,由于 CacheInterceptor也通过 implements SmartInitializingSingleton间接地 extending CacheAspectSupport接口,所以这还不能完成初始化。如果从未调用 SmartInitializingSingleton实现的 CacheInterceptor.afterSingletonsInstantiated() method,则永远不会触发 initialized bit,并且不会执行任何可缓存的操作 will not be cached,从而导致 cacheable operation being invoked every single time(因此,忽略任何引入的缓存行为)。

这就是为什么我在测试类中将 CacheProxyFactoryBean扩展到 capture“ mainInterceptor”(即 CacheInterceptor),然后在Spring容器的初始化阶段的适当时候调用 afterSingletonsInstantiated() method的确切原因,这就是为什么我的 SmartCacheProxyFactoryBean扩展实现 SmartInitializingSingleton委托给 CacheInterceptor.afterSingletonsInstantiated()方法的原因。

另外, CacheInterceptorBeanFactoryAware,而 requires是Spring BeanFactory来执行其功能,因此,我检查此“ mainInterceptor”并适当设置 BeanFactory的原因是 here

我建议的另一种方法是使用 TestConfigurationTwo

在此配置中,我直接是 configuring Spring AOP建议(即 CacheInterceptor),是从 @Bean定义方法“ cacheInterceptor”返回的,该方法允许Spring容器适当地调用生命周期回调。

然后,进入第三方类的 use this Advice中的 cache proxy creation(即“ Greeter”)。

您应该小心地将从“ cacheInterceptor” bean定义创建的bean传递到“ greeter” bean定义 like so。如果要从bean“ cacheInterceptor” bean定义中调用“ greeter” bean定义方法,就像许多用户不恰当地执行( for example)一样,那么您将放弃Spring容器生命周期回调!不要这样! here说明了其原因。

另外,要阅读有关以编程方式创建代理的更多信息,您应该阅读 this

好的,关于它的覆盖。

我提供的测试类(即“ ProgrammaticCachingWithSpringIntegrationTests”)。随时尝试使用它,如果您有任何后续问题,请告诉我。

希望这可以帮助!

干杯!

关于java - 如何使用Java Config手动添加Spring CacheInterceptor?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47665485/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com