简体   繁体   中英

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

My Spring 4 application, which uses Reactor 2, fails to start with:

***************************
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.

The OrderHandlerConsumer is really simple:

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

Any ideas what might be going awry?

In your application class file where you define it as Spring application, add underneath it.

@SpringBootApplication
@EnableCaching(proxyTargetClass = true)

While the accepted answer will solve this issue, I think it will be more appropriate for me to explain why appling proxyTargetClass = true will fix this.

First of all, Spring, as a framework, utilizes proxing in order to supply the bean with some extended functionality, such as declaritive transactions via @Transactional , or caching by the means of @Cacheable and etc There are, in general, 2 ways(*) Spring can create proxy on top of your bean:

  1. Jdk dynamic proxing
  2. CGLib proxing

Offical documentation on this, in case you are interested.

Spring can create jdk dynamic proxy of the bean (in case proxing is required for this bean of course) if original class of the bean implements at least one interface. So spring basically create another implementation of this interface at runtime with some additional logic on top of original class.

What is the problem: if the bean is proxied by the means of jdk dynamic proxing , then you cannot inject this bean via its original class . So something like this:

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

wont work. Why? Well, becuase @Transactional tells Spring that it needs to create proxy of SomeService at runtime, and within @EnableTransactionManagement I specifically asked Spring to make it by the means of jdk dynamic proxy - spring will succeed, since jdk dynamic proxy can be created, but the problem is at runtime there is not bean of type SomeService , there is only a bean of type SomeInterface (by the way, if you inject service here not by the class, but by the interface - it will work, I assume you understand the reason by reading explaination above).

Solution: by applying @EnableTransactionManagement(proxyTargetClass = true) (notice true value here) you force spring to create CGlib proxy (this rule is applicable only for beans that utilize declarative transaction management, ie via annotations). In case of CgLib proxing, Spring will try to extend the original class, and add additional functionality at runtime in the generated child class. And in this case injection by class will work - because the bean extends class SomeService , so

@Autowired
private SomeService someService;

will work perfectly fine. But, in general, if possible, inject bean by interface, not by class . In this case both Cglib and jdk dynamic proxy will work. So, be aware of proxing mechanisms spring can use. Hope it helped, have a nice day.

You can assign a bean name to your OrderHandlerConsumer class so that Autowire resolution will be easier, Moreover, Instead of Autowiring with the concrete class, try to auto-wire with the interface. So that you can change @Service annotation to,

@Service(value="orderHandlerConsumer")

and try to Autowire with the interface type,

@Autowire  
reactor.fn.Consumer orderHandlerConsumer;

Please try autowiring as below

class Test{
    @Autowired
    private Consumer orderHandlerConsumer;
}

If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.

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

You can call it two ways.

1st way is without mentioning the proxy [with default proxy] , you can Autowire it by the interface like below.

@Autowired
private Consumer orderHandlerConsumer;

Spring AOP will create an instance for OrderHandlerConsumer .

2nd way is, mention the proxy in the bean as ScopedProxyMode.TARGET_CLASS . then you can Autowire an instance without the interface [based on the 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();
    }
}

and Autowire by the class like below.

@Autowired
private OrderHandlerConsumer orderHandlerConsumer;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM