无法将 bean 作为'Type'注入,因为它是一个实现的 JDK 动态代理:reactor.fn.Consumer



我的Spring 4应用程序使用Reactor 2,无法从以下位置开始:

***************************
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();
}
}

任何想法可能会出错吗?

虽然另一个答案可以解决这个问题,但我认为我解释为什么应用proxyTargetClass = true会解决这个问题更合适。

首先,Spring作为一个框架,利用代理为bean提供一些扩展功能,例如通过@Transactional的声明式事务,或通过@Cacheable的方式缓存等。一般来说,有两种方式(*)Spring可以在你的bean上创建代理:

  1. JDK 动态代理
  2. CGLib 代理

如果您有兴趣,可以对此进行官方文档。

Spring 可以创建 bean 的 JDK 动态代理(当然,如果这个 bean 需要代理),如果 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 应用程序,在其下方添加。

@SpringBootApplication
@EnableCaching(proxyTargetClass = true)

您可以为 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

你可以用两种方式来称呼它。

第一种方法是不提及代理[使用默认代理],您可以通过如下所示的界面自动连接它。

@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();
}
}

和自动连线的类,如下所示。

@Autowired
private OrderHandlerConsumer orderHandlerConsumer;

最新更新