Reputation: 9506
I have this service bean with a sync method calling the internal async method:
@Service
public class MyService {
public worker() {
asyncJob();
}
@Async
void asyncJob() {
...
}
}
The trouble is that the asyncJob
is not really called in async way.
I found that this doesn't work because an internal call skips the AOP proxy.
So I try to self-refer the bean:
@Service
public class MyService {
MyService mySelf;
@Autowired
ApplicationContext cnt;
@PostConstruct
public void init() {
mySelf=(MyService)cnt.getBean("myService");
}
public void worker() {
mySelf.asyncJob();
}
@Async
void asyncJob() {
...
}
}
It fails. Again no async call.
So I tried to divide it in two beans:
@Service
public class MyService {
@Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
@Service
public class MyAsyncService {
@Async
void asyncJob() {
...
}
}
Fails again.
The only working way is to call it from a Controller Bean:
@Controller
public class MyController {
@Autowired
MyAsyncService myAsyncService;
@RequestMapping("/test")
public void worker() {
myAsyncService.asyncJob();
}
}
@Service
public class MyAsyncService {
@Async
public void asyncJob() {
...
}
}
But in this case it is a service job. Why I cannot call it from a service?
Upvotes: 24
Views: 22373
Reputation: 591
In my case, it was easier to remove the @Async
annotation and use the taskExecutor
directly to submit my task:
Before
@Async("taskExecutor")
private Future<U> executerEnAsync(
final T pInput) {
final U resultat = this.appelerBS(pInput);
return new AsyncResult<U>(resultat);
}
After
@Autowired
private AsyncTaskExecutor taskExecutor;
private Future<U> executerEnAsync(
final T pInput) {
final Future<U> future = taskExecutor.submit(new Callable<U>() {
@Override
public U call() {
final U resultat = appelerBS(pInput);
return resultat;
}
});
return future;
}
Upvotes: 0
Reputation: 9506
I solved the third method (divide it in two beans) changing the async method's access modifier to public
:
@Service
public class MyService {
@Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
@Service
public class MyAsyncService {
@Async
public void asyncJob() { // switched to public
...
}
}
Upvotes: 11
Reputation: 19970
Found a really nice way to solve this (with java8) in the case where you have a lot of various things you want to both sync and async. Instead of creating a separate XXXAsync
service for each 'synchronous' service, create a generic async service wrapper:
@Service
public class AsyncService {
@Async
public void run(final Runnable runnable) {
runnable.run();
}
}
and then use it as such:
@Service
public class MyService {
@Autowired
private AsyncService asyncService;
public void refreshAsync() {
asyncService.run(this::refresh);
}
public void refresh() {
// my business logic
}
public void refreshWithParamsAsync(String param1, Integer param2) {
asyncService.run(() -> this.refreshWithParams(param1, param2));
}
public void refreshWithParams(String param1, Integer param2) {
// my business logic with parameters
}
}
Upvotes: 46