spring 无法将bean作为“Type”注入,因为它是实现以下内容的JDK动态代理:reactor.fn.Consumer

bwitn5fc  于 2023-06-04  发布在  Spring
关注(0)|答案(5)|浏览(254)

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

有什么想法可能出了差错吗?

kqqjbcuj

kqqjbcuj1#

在您的application class文件中,将其定义为Spring application,在它下面添加。

@SpringBootApplication
@EnableCaching(proxyTargetClass = true)
bqjvbblv

bqjvbblv2#

虽然另一个答案可以解决这个问题,但我认为更适合我来解释为什么应用proxyTargetClass = true可以解决这个问题。
首先,Spring作为一个框架,利用 * 代理 * 来为bean提供一些扩展功能,例如通过@Transactional的声明性事务,或者通过@Cacheable的方式进行缓存等。一般来说,有两种方法(*)Spring可以在bean之上创建代理:

  1. JDK动态代理
  2. CGLib代理
    Official documentation如果你感兴趣的话。
    如果bean的原始类实现了至少一个接口,Spring可以创建bean的JDK动态代理(当然,如果这个bean需要代理)。所以Spring基本上是在运行时创建这个接口的另一个实现,在原始类的顶部添加一些额外的逻辑。

问题出在哪里:如果bean是通过JDK动态代理的方式进行代理,那么就无法通过bean的原类注入该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)(注意这里的 true 值),您可以强制spring创建CGLIB代理(此规则仅适用于使用声明式事务管理的bean,即经由注解)。在CGLIB代理的情况下,Spring将尝试扩展原始类,并在运行时在生成的子类中添加额外的功能。在这种情况下,按类注入将起作用-因为bean扩展了类SomeService,所以

@Autowired
private SomeService someService;

会非常好用但是,一般来说,如果可能的话,按接口注入bean,而不是按类注入。在这种情况下,CGLIB和JDK动态代理都可以工作。因此,要注意spring可以使用的代理机制。希望有帮助,祝你今天愉快。

a8jjtwal

a8jjtwal3#

你可以给你的OrderHandlerConsumer类分配一个bean名,这样自动连接解析就更容易了。此外,不要用具体的类自动连接,试着用接口自动连接。以便您可以将@Service注解更改为,

@Service(value="orderHandlerConsumer")

并尝试使用接口类型进行自动连接,

@Autowire  
reactor.fn.Consumer orderHandlerConsumer;
wz3gfoph

wz3gfoph4#

请尝试如下自动装配

class Test{
    @Autowired
    private Consumer orderHandlerConsumer;
}
l7wslrjt

l7wslrjt5#

如果要代理的目标对象实现了至少一个接口,则将使用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();
    }
}

和Autowire的类如下。

@Autowired
private OrderHandlerConsumer orderHandlerConsumer;

相关问题