簡體   English   中英

無法將 bean 作為“類型”注入,因為它是一個 JDK 動態代理,它實現了:reactor.fn.Consumer

[英]The bean could not be injected as a 'Type' because it is a JDK dynamic proxy that implements: reactor.fn.Consumer

我使用 Reactor 2 的 Spring 4 應用程序無法啟動:

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'orderHandlerConsumer' could not be injected as a 'fm.data.repository.OrderHandlerConsumer' because it is a JDK dynamic proxy that implements:
    reactor.fn.Consumer


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.

OrderHandlerConsumer非常簡單:

@Service
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OrderHandlerConsumer implements Consumer<Event<OrderEnvelope>> {
    @Override
    public void accept(Event<OrderEnvelope> event) {
        event.getData().getLatch().countDown();
    }
}

有什么想法可能會出錯嗎?

在您將其定義為 Spring 應用程序的應用程序類文件中,在其下方添加。

@SpringBootApplication
@EnableCaching(proxyTargetClass = true)

雖然接受的答案將解決這個問題,但我認為解釋為什么應用proxyTargetClass = true會解決這個問題對我來說更合適。

首先,Spring 作為一個框架,利用代理來為 bean 提供一些擴展功能,例如通過@Transactional進行聲明性事務,或者通過@Cacheable等方式進行緩存。一般來說,有兩種方式( *) Spring 可以在你的 bean 之上創建代理:

  1. jdk動態代理
  2. CGLib 代理

如果您有興趣,請提供有關此的官方文檔

如果 bean 的原始類實現了至少一個接口,Spring 可以創建 bean 的 jdk 動態代理(當然,如果這個 bean 需要代理)。 所以spring基本上在運行時創建了這個接口的另一個實現,在原始類之上有一些額外的邏輯。

問題是什么:如果 bean 是通過 jdk 動態代理代理的,那么你不能通過它的原始類注入這個 bean 所以是這樣的:

@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = false)
public class StackoverflowApplication {

    @Autowired private SomeService service;

    public static void main(String[] args) {
        SpringApplication.run(StackoverflowApplication.class, args);
    }
}

@Service
class SomeService implements SomeInterface {
    
    @Override
    @Transactional
    public void handle() { }
}

interface SomeInterface {
    void handle();
}

不會工作。 為什么? 好吧,因為@Transactional告訴Spring它需要在運行時創建SomeService的代理,並且在@EnableTransactionManagement我特別要求Spring通過jdk動態代理的方式來制作它-spring會成功,因為可以創建jdk動態代理,但是問題是在運行時沒有SomeService類型的 bean,只有SomeInterface類型的 bean(順便說一下,如果你在這里注入服務不是通過類,而是通過接口 - 它會起作用,我假設你理解原因通過閱讀上面的解釋)。

解決方案:通過應用@EnableTransactionManagement(proxyTargetClass = true) (注意這里的真值)你強制spring創建CGlib代理(這個規則只適用於使用聲明性事務管理的bean,即通過注釋)。 在 CgLib 代理的情況下,Spring 將嘗試擴展原始類,並在運行時在生成的子類中添加額外的功能。 在這種情況下,按類注入將起作用- 因為 bean 擴展了類SomeService ,所以

@Autowired
private SomeService someService;

會很好地工作。 但是,一般來說,如果可能的話,按接口注入 bean,而不是按類 在這種情況下,Cglib 和 jdk 動態代理都可以工作。 因此,請注意 spring 可以使用的代理機制。 希望對您有所幫助,祝您有美好的一天。

您可以為 OrderHandlerConsumer 類分配一個 bean 名稱,這樣 Autowire 解析會更容易,此外,嘗試使用接口自動連接,而不是使用具體類進行自動連接。 這樣您就可以將 @Service 注釋更改為,

@Service(value="orderHandlerConsumer")

並嘗試使用接口類型自動裝配,

@Autowire  
reactor.fn.Consumer orderHandlerConsumer;

請嘗試如下自動裝配

class Test{
    @Autowired
    private Consumer orderHandlerConsumer;
}

如果要代理的目標對象至少實現一個接口,則將使用 JDK 動態代理。 目標類型實現的所有接口都將被代理。 如果目標對象沒有實現任何接口,那么將創建一個 CGLIB 代理。

https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html

你可以用兩種方式稱呼它。

第一種方法是不提及代理 [with default proxy] ,您可以通過如下界面自動裝配它。

@Autowired
private Consumer orderHandlerConsumer;

Spring AOP 將為OrderHandlerConsumer創建一個實例。

第二種方法是,將 bean 中的代理稱為ScopedProxyMode.TARGET_CLASS 那么你可以自動裝配一個沒有接口的實例[基於類]。

@Service
@Order(Ordered.HIGHEST_PRECEDENCE)
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class OrderHandlerConsumer implements Consumer<Event<OrderEnvelope>> {
    @Override
    public void accept(Event<OrderEnvelope> event) {
        event.getData().getLatch().countDown();
    }
}

和 Autowire 的類,如下所示。

@Autowired
private OrderHandlerConsumer orderHandlerConsumer;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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