该bean无法作为“Type”注入,因为它是实现:reactor.fn.Consumer的JDK动态代理。

2022-09-01 09:00:33

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

任何想法可能出错?


答案 1

在将其定义为 Spring 应用程序的应用程序类文件中,在其下方添加。

@SpringBootApplication
@EnableCaching(proxyTargetClass = true)

答案 2

虽然接受的答案将解决这个问题,但我认为对我来说,解释为什么appling会解决这个问题会更合适。proxyTargetClass = true

首先,作为一个框架,Spring利用代理来为Bean提供一些扩展功能,例如通过的声明性事务,或通过和e.t.c进行缓存。一般来说,Spring可以通过2种方式(*)在您的Bean上创建代理:@Transactional@Cacheable

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

不起作用。为什么?好吧,因为告诉Spring它需要在运行时创建代理,并且在我特别要求Spring通过jdk动态代理来制作它 - spring会成功,因为可以创建jdk动态代理,但问题是在运行时没有类型的bean,只有一个类型的bean(顺便说一句, 如果你在这里注入服务不是通过类,而是通过接口 - 它会起作用,我假设你通过阅读上面的解释来理解原因)。@TransactionalSomeService@EnableTransactionManagementSomeServiceSomeInterface

解决方案:通过应用(注意此处的真正值),您可以强制 spring 创建 CGlib 代理(此规则仅适用于使用声明性事务管理的 Bean,即通过注释)。在CgLib代理的情况下,Spring将尝试扩展原始类,并在运行时在生成的子类中添加其他功能。在这种情况下,按类注入将起作用 - 因为bean扩展了类,所以@EnableTransactionManagement(proxyTargetClass = true)SomeService

@Autowired
private SomeService someService;

将完美地工作。但是,一般来说,如果可能的话,按接口而不是按类注入bean。在这种情况下,Cglib和jdk动态代理都可以工作。因此,请注意弹簧可以使用的代理机制。希望它有帮助,祝你有美好的一天。


推荐