Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use @Async with @Scheduled annotation in springboot?

I want to use @scheduled with @Async annotation but when I start the server I get this exception and if I remove @Async annotation it works fine. Any help would be appreciated.

@Component
public class NService  {

@Scheduled(fixedDelay =70*100)
@Async
public void someMethod() throws SQLException {

    //some Processing
}
}

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.IllegalStateException: Need to invoke method 'recordHeartBeat' declared on target class 'NodeStatusService', but not found in any interface(s) of the exposed proxy type. Either pull the method up to an interface or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.
at org.springframework.core.MethodIntrospector.selectInvocableMethod(MethodIntrospector.java:135)
at org.springframework.aop.support.AopUtils.selectInvocableMethod(AopUtils.java:130)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.processScheduled(ScheduledAnnotationBeanPostProcessor.java:341)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization(ScheduledAnnotationBeanPostProcessor.java:324)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:423)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
... 20 common frames omitted
like image 872
Harmanpreet Singh Avatar asked Jun 16 '18 12:06

Harmanpreet Singh


Video Answer


1 Answers

The error gives an advice what to do:

The method 'recordHeartBeat' declared on target class 'NodeStatusService', but not found in any interface(s) of the exposed proxy type. Either pull the method up to an interface or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.

To function correctly, @Async annotation requires to create a proxy (wrapper) for your class. The execution sequence will be:

Caller -> Proxy Method -> Your class method

Spring can create proxies automatically using 2 ways:

1) By implementing an interface

If a class implements an interface, Spring can create a proxy class that will implement the interface and injects it in the execution path. This is the first part of the recommendation in the error message:

Either pull the method up to an interface

To follow this way you need to create an interface with the public void recordHeartBeat() throws SQLException and implement your interface in the class, for example:

public interface HeartBeater {
  void recordHeartBeat() throws SQLException;
}

public class NodeStatusService implements NodeStatus implements HeartBeater {
....
}

2) By using CGLIB to create a byte-code proxy

If a class doesn't implement interfaces that declare a method with @Async annotation, Spring can create a byte-code proxy using CGLIB. It uses byte-code manipulation to change the call sequence.

This is the second part of the recommendation in the error message:

switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration

You can enable proxy target class by adding an annotation to the configuration bean:

@EnableAspectJAutoProxy(proxyTargetClass = true)

See the documentation for examples: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/EnableAspectJAutoProxy.html

like image 143
Pavel Molchanov Avatar answered Oct 31 '22 22:10

Pavel Molchanov