簡體   English   中英

如何使用Java Config手動添加Spring CacheInterceptor?

[英]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.InitializingBeanosbeans.factory.DisposableBean等),則Spring容器將知道在Spring容器的初始化過程中的“適當”時間調用這些生命周期回調。

因此,沒有必要在“ greeter” @Bean定義內調用CacheProxyFactoryBean.afterPropertiesSet()CacheProxyFactoryBean.getObject() 這樣做實際上違反了Spring容器的初始化約定,您可能會遇到過早的“初始化”問題,尤其是在提供的FactoryBean實現其他Spring容器接口(例如BeanClassLoaderAwareEnvironmentAware等)的情況下。

小心!

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()方法。

此外, CacheInterceptorBeanFactoryAware ,並要求 春節 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM