繁体   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