Spring @Async method inside a Service

2020-06-01 06:30发布

问题:

I have this Service bean with a sync method calling an internal async method:

@Service
public class MyService {

    public worker(){
        asyncJob();
    }

    @Async
    asyncJob(){
        ...
    }

}

The trouble is that the asyncJob is not really call 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 init(){
        mySelf=(MyService)cnt.getBean("myService");
    }


    public worker(){
        mySelf.asyncJob();
    }

    @Async
    asyncJob(){
        ...
    }

}

It fails. Again no async call.

So I tryed to divide it in two beans:

@Service
public class MyService {
    @Autowired
    MyAsyncService myAsyncService;
    public worker(){
        myAsyncService.asyncJob();
    }
}

@Service
public class MyAsyncService {

    @Async
    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 worker(){
        myAsyncService.asyncJob();
    }
}

@Service
public class MyAsyncService {

    @Async
    public asyncJob(){
        ...
    }

}

But in this case it is a service job... why I can not call it from a Service?

回答1:

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
    }

}


回答2:

I solved the third method (divide it in two beans) switching Async method modifier to public

@Service
public class MyService {
    @Autowired
    MyAsyncService myAsyncService;
    public worker(){
        myAsyncService.asyncJob();
    }
}

@Service
public class MyAsyncService {

    @Async
    public asyncJob(){ // switched to public
        ...
    }

}