![](/img/trans.png)
[英]How can I Instantiate interdependent lists of Beans in Spring using YML and Java Config?
[英]How can I manually add a Spring CacheInterceptor using Java Config?
我正在嘗試找出如何在第三方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)第一個問題涉及在應用程序@Configuration
類(即“ MyApp”)的“ greeter” @Bean
定義中使用oscache.interceptor.CacheProxyFactoryBean
。
每當你使用Spring的 1 FactoryBeans
(如CacheProxyFactoryBean
),或者實現你自己的,你從返回的內容@Bean
方法是FactoryBean
本身,而不是對產品FactoryBean
。 因此,您將返回FactoryBean
,而不是return factoryBean.getObject()
,就像這樣...
@Bean
GreeterFactoryBean greeter() {
GreeterFactoryBean factoryBean = new GreeterFactoryBean();
factoryBean.set...
return factoryBean;
}
在這里, GreeterFactoryBean
實現了osbeans.factory.FactoryBean
。
正如Spring的 參考文檔所指出的那樣, Spring容器知道要返回FactoryBean
(例如[ Singleton ] Greeter
實例)而不是 FactoryBean
本身,作為該@Bean
方法在Spring容器中的“定義” Bean。 如果未使用@Bean
明確定義,則bean的名稱@Bean
方法的名稱(例如@Bean("Greeter")
)。
如果FactoryBean
還實現Spring的生命周期回調接口(例如osbeans.factory.InitializingBean
或osbeans.factory.DisposableBean
等),則Spring容器將知道在Spring容器的初始化過程中的“適當”時間調用這些生命周期回調。
因此,沒有必要在“ greeter” @Bean
定義內調用CacheProxyFactoryBean.afterPropertiesSet()
或CacheProxyFactoryBean.getObject()
。 這樣做實際上違反了Spring容器的初始化約定,您可能會遇到過早的“初始化”問題,尤其是在提供的FactoryBean
實現其他Spring容器接口(例如BeanClassLoaderAware
或EnvironmentAware
等)的情況下。
小心!
2)其次,這不是您的示例所要解決的問題/問題,而是要意識到的問題。 您在此SO帖子中指出,您正在嘗試將“緩存”行為添加到第三方庫類。
僅當您能夠自己在應用程序中實例化第三方類(例如Greeter
)時,以上使用的方法才適用。
但是,如果第三方庫或框架代表您實例化該類,則由於配置了庫/框架(例如,認為JDBC Driver和Hibernate),您將無法在應用程序中向此類引入緩存行為,除非您可以使用加載時編織 (LTW)。 閱讀文檔以獲取更多詳細信息。
好了,到解決方案了 。
我編寫了一個測試來重現此問題並更好地了解Spring Framework內部發生的情況。 您可以在這里找到我完成的考試。
實際上, TestConfigurationOne
與您以編程方式創建緩存代理所采用的方法相同,它基於我在上面討論的內容進行了修改,並且還解決了我認為是核心Spring Framework中的錯誤的地方(注意:我正在使用Spring Framework 5.0.1.RELEASE
在我的測試中)。
為了使您的CacheProxyFactoryBean
配置方法起作用,我需要擴展CacheProxyFactoryBean
類 。 除了擴展 CacheProxyFactoryBean
,我還需要實現 SmartInitializingSingleton
接口和BeanFactoryAware
接口 ,其原因SmartInitializingSingleton
就會顯現出來。 完整的實現請參見9 。
在內部, Spring Framework的 oscache.interceptor.CacheProxyFactoryBean
使用 oscache.interceptor.CacheInterceptor
。 它還在此處和此處繼續“初始化”此CacheInterceptor
實例。 但是,由於CacheInterceptor
還通過擴展 CacheAspectSupport
間接實現 SmartInitializingSingleton
接口,因此這還不能完成初始化。 如果SmartInitializingSingleton
實施CacheInterceptor.afterSingletonsInstantiated()
方法不會被調用,則initialized
位永遠不會跳閘,任何緩存操作將不被緩存 ,導致緩存的操作被調用每一次 (因此,忽略任何介紹緩存行為) 。
這就是為什么我在測試類中擴展CacheProxyFactoryBean
來捕獲 “ mainInterceptor”(即CacheInterceptor
),然后在Spring容器的初始化階段的適當時候調用afterSingletonsInstantiated()
方法的確切原因,這就是為什么我的SmartCacheProxyFactoryBean
擴展程序實現SmartInitializingSingleton
,以委派給CacheInterceptor.afterSingletonsInstantiated()
方法。
此外, CacheInterceptor
是BeanFactoryAware
,並要求 春節 BeanFactory
執行其功能,因此原因,我檢查一下“mainInterceptor”,並設置BeanFactory
恰如其分, 在這里 。
我推薦的另一種方法是使用TestConfigurationTwo
。
在此配置中,我直接配置 Spring AOP咨詢(即CacheInterceptor
),並從@Bean
定義方法“ cacheInterceptor”返回它,該方法允許Spring容器適當地調用生命周期回調。
然后,在第三方類(即“ Greeter
”)的緩存代理創建中繼續使用此建議 。
您應該小心地將從“ cacheInterceptor
” bean定義創建的bean傳遞給“ greeter
” bean定義, 就像這樣 。 如果你要調用“ cacheInterceptor
從你的bean中” bean定義方法“ greeter
” bean定義,像這么多的用戶不適當的 (做例如 ),那么你會放棄Spring容器生命周期回調! 不要這樣! 造成這種情況的原因進行了說明這里 。
此外,要閱讀有關以編程方式創建代理的更多信息,您應該閱讀此書 。
好的,關於它的覆蓋。
我提供的測試類(即“ ProgrammaticCachingWithSpringIntegrationTests
”)。 隨時嘗試使用它,如果您有任何后續問題,請告訴我。
希望這可以幫助!
干杯!
您可以簡單地創建一個Adapter
/ Wrapper
類,該類委派給您要啟用緩存的基礎第三方類類型。 然后,將緩存行為添加到您的應用程序Adapter
/ Wrapper
類型。
這比嘗試確定適當的AOP切入點來攔截要在其中建議緩存的第三方類中的方法要容易得多。 當然,如果您沒有引用要在其中引入緩存的對象,則這種方法將行不通,在這種情況下,您將不得不求助於AOP。
從您的示例中,我得到的印象是您可能對此第三方對象實例具有引用???
如果沒有,請澄清一下,然后我可以為您提供AOP幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.