#Async in Spring doesn't work in Service class? - java

#Async method in #Service annotated class in standalone Spring Boot application doesn't run asynchronously. What am I doing wrong?
When I run the same method directly from main class (#SpringBootApplication annotated), it works. Example:
Main class
#SpringBootApplication
#EnableAsync
public class Application implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
// here when I call downloadAnSave() it runs asynchronously...
// but when I call downloadAnSave() via downloadAllImages() it does not run asynchronously...
}
}
and my service class (and here asynchronous behavior doesn't work):
#EnableAsync
#Service
public class ImageProcessorService implements IIMageProcessorService {
public void downloadAllImages(Run lastRun) {
// this method calls downloadAnSave() in loop and should run asynchronously....
}
#Async
#Override
public boolean downloadAnSave(String productId, String imageUrl) {
//
}
}

Calling async method from within the same class would trigger the original method and not the intercepted one.
You need to create another service with the async method, and call it from your service.
Spring creates a proxy for each service and component you create using the common annotations. Only those proxies contain the wanted behavior defined by the method annotations such as the Async. So, calling those method not via the proxy but by the original naked class would not trigger those behaviors.

Workaround is:
#EnableAsync
#Service("ip-service")
public class ImageProcessorService implements IIMageProcessorService {
#Autowired
#Qualifier("ip-service")
ImageProcessorService ipService;
public void downloadAllImages(Run lastRun) {
// this method calls downloadAnSave() in loop and should run asynchronously....
ipService.downloadAnSave(productId, imageUrl);
}
#Async
#Override
public boolean downloadAnSave(String productId, String imageUrl) {
//
}
}
With such approach you call method of proxy, not the class instance.
The same approach can be used with other tools working with proxies, e.g. #Transactional etc.

Related

Best way to call java interface methods after springboot application start up? [duplicate]

I want to run code after my spring-boot app starts to monitor a directory for changes.
I have tried running a new thread but the #Autowired services have not been set at that point.
I have been able to find ApplicationPreparedEvent, which fires before the #Autowired annotations are set. Ideally I would like the event to fire once the application is ready to process http requests.
Is there a better event to use, or a better way of running code after the application is live in spring-boot?
It is as simple as this:
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
System.out.println("hello world, I have just started up");
}
Tested on version 1.5.1.RELEASE
Try:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#SuppressWarnings("resource")
public static void main(final String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
context.getBean(Table.class).fillWithTestdata(); // <-- here
}
}
Have you tried ApplicationReadyEvent?
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
}
Code from: http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/
This is what the documentation mentions about the startup events:
...
Application events are sent in the following order, as your application runs:
An ApplicationStartedEvent is sent at the start of a run, but before
any processing except the registration of listeners and initializers.
An ApplicationEnvironmentPreparedEvent is sent when the Environment to be used in the context is known, but before the context
is created.
An ApplicationPreparedEvent is sent just before the refresh is started, but after bean definitions have been loaded.
An ApplicationReadyEvent is sent after the refresh and any related callbacks have been processed to indicate the application is ready to
service requests.
An ApplicationFailedEvent is sent if there is an exception on startup.
...
Why not just create a bean that starts your monitor on initialization, something like:
#Component
public class Monitor {
#Autowired private SomeService service
#PostConstruct
public void init(){
// start your monitoring in here
}
}
the init method will not be called until any autowiring is done for the bean.
The "Spring Boot" way is to use a CommandLineRunner. Just add beans of that type and you are good to go. In Spring 4.1 (Boot 1.2) there is also a SmartInitializingBean which gets a callback after everything has initialized. And there is SmartLifecycle (from Spring 3).
ApplicationReadyEvent is really only useful if the task you want to perform is not a requirement for correct server operation. Starting an async task to monitor something for changes is a good example.
If, however your server is in a 'not ready' state until the task is completed then it's better to implement SmartInitializingSingleton because you'll get the callback before your REST port has been opened and your server is open for business.
Don't be tempted to use #PostConstruct for tasks that should only happen once ever. You'll get a rude surprise when you notice it being called multiple times...
You can extend a class using ApplicationRunner , override the run() method and add the code there.
import org.springframework.boot.ApplicationRunner;
#Component
public class ServerInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments applicationArguments) throws Exception {
//code goes here
}
}
Use a SmartInitializingSingleton bean in spring > 4.1
#Bean
public SmartInitializingSingleton importProcessor() {
return () -> {
doStuff();
};
}
As alternative a CommandLineRunner bean can be implemented or annotating a bean method with #PostConstruct.
Best way to execute block of code after Spring Boot application started is using PostConstruct annotation.Or also you can use command line runner for the same.
1. Using PostConstruct annotation
#Configuration
public class InitialDataConfiguration {
#PostConstruct
public void postConstruct() {
System.out.println("Started after Spring boot application !");
}
}
2. Using command line runner bean
#Configuration
public class InitialDataConfiguration {
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println("CommandLineRunner running in the UnsplashApplication class...");
};
}
}
Providing an example for Dave Syer answer, which worked like a charm:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
#Override
public void run(String...args) throws Exception {
logger.info("Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.", Arrays.toString(args));
}
}
I really like the suggestion for usage of the EventListener annotation by #cahen (https://stackoverflow.com/a/44923402/9122660) since it is very clean. Unfortunately I could not get this to work in a Spring + Kotlin setup. What does work for Kotlin is adding the class as a method parameter:
#EventListener
fun doSomethingAfterStartup(event: ApplicationReadyEvent) {
System.out.println("hello world, I have just started up");
}
You have several choices:
Using CommandLineRunner or ApplicationRunner as a Bean definition:
Spring Boot executes these towards the end of the application startup process. In most circumstances, the CommandLineRunner will do the job. Following is an example of a CommandLineRunner implementation with Java 8:
#Bean
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World");
}
Note that the args is the String array of arguments. You can also provide an implementation of this interface and define it as a Spring Component:
#Component
public class MyCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Hello World");
}
}
You can use the ApplicationRunner if you need better argument management. ApplicationRunner takes an ApplicationArguments instance that has enhanced argument management options.
You can also order the CommandLineRunner and ApplicationRunner beans using Spring's #Order annotation:
#Bean
#Order(1)
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World, Order 1");
}
#Bean
#Order(2)
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World, Order 2");
}
Using Spring Boot's ContextRefreshedEvent:
Spring Boot publishes several events at startup. These events indicate the completion of a phase in the application startup process. You can listen to the ContextRefreshedEvent and execute custom code:
#EventListener(ContextRefreshedEvent.class)
public void execute() {
if(alreadyDone) {
return;
}
System.out.println("hello world");
}
ContextRefreshedEvent is published several times. Thus, ensure to put a check whether the code execution is already finished.
Try this one and it will run your code when the application context has fully started.
#Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
// EXECUTE YOUR CODE HERE
}
}
Spring boot provides an ApplicationRunner interface with a run() method to be invoked at application startup.
However, instead of raw String arguments passed to the callback method, we have an instance of the ApplicationArguments class.
#Component
public class AppStartupRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
//some logic here
}
}
just implement CommandLineRunner for spring boot application.
You need to implement run method,
public classs SpringBootApplication implements CommandLineRunner{
#Override
public void run(String... arg0) throws Exception {
// write your logic here
}
}
you can use #Component
#RequiredArgsConstructor
#Component
#Slf4j
public class BeerLoader implements CommandLineRunner {
//declare
#Override
public void run(String... args) throws Exception {
//some code here
}
If you mean running peace of code once after the application started, you can use the CommandLineRunner as below:
#SpringBootApplication
public class SpringBootApplication
implements CommandLineRunner {
private static Logger LOG = LoggerFactory
.getLogger(SpringBootConsoleApplication.class);
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(SpringBootConsoleApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
#Override
public void run(String... args) {
// enter code you want to run after app loaded here
LOG.info("EXECUTING : command line runner");
for (int i = 0; i < args.length; ++i) {
LOG.info("args[{}]: {}", i, args[i]);
}
}
}
Otherwise, you can use the DevTools dependency, which help you to run new codes without manually restarting the application.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
don't forget to add these codes to your pom.xml to avoid version warnings:
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
give it a thump up if this was helpful to you!
Best way you use CommandLineRunner or ApplicationRunner
The only difference between is run() method
CommandLineRunner accepts array of string and ApplicationRunner accepts ApplicationArugument.

Mockito - mock ApplicationContext

I have a Springboot application that looks up the bean from the ApplicationContext at runtime based on the input parameter passed by the user. For this method, I am trying to write Mockito test cases but it is not working and throws NullPointerException.
The class which bootstraps the application:
#SpringBootApplication
public class MyApplication {
private static ApplicationContext appContext;
public static void main(String[] args) {
appContext = SpringApplication.run(MyApplication.class, args);
}
public static ApplicationContext getApplicationContext() {
return appContext;
}
}
Class for which I am trying to write the test cases:
#Service
public class Mailbox {
#Autowired
MailProcessor processor;
public void processUserInput(Envelope object) {
processor.setCommand(MyApplication.getApplicationContext().getBean(object.getAction(), Command.class));
processor.allocateEnvelopes(object);
}
}
And my test case is as below:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class MailboxTest {
#Mock
MailProcessor processor;
#InjectMocks
Mailbox mailbox;
#Test
public void testProcessUserInput() {
Envelope message = new Envelope();
message.setAction("userAction");
message.setValue("userInput");
doNothing().when(processor).setCommand(any());
doNothing().when(processor).allocateEnvelopes(any());
mailbox.processUserInput(message);
Mockito.verify(processor).allocateEnvelopes(any());
}
}
Whenever I run the test cases it gives the NullPointerException at processor.setCommand(MyApplication.getApplicationContext().getBean(object.getAction(), Command.class)); in Mailbox class. How can I mock the ApplicationContext lookup? Am I missing any mocking step?
Spring wise your code doesn't look good, and in particular is not unit testable. I'll explain:
Your Mailbox service should not be aware of MyApplication at any level. It is an entry point of spring boot application and your business logic should not depend on that.
Its true that you can inject the application context directly into the class. See an example below. Another (more "old-school") option here is using ApplicationContextAware interface in the Mailbox service (see this example). However, its still a bad code IMO:
#Service
public class Mailbox {
private final ApplicationContext ctx;
...
public Mailbox(ApplicationContext ctx) {
this.ctx = ctx;
}
...
}
Even if you resolve it, in general its not a good idea to depend on the ApplicationContext as well. Because this way you become spring dependent and there is no reason to do that in the Mailbox class. The class will become unit testable though.
In terms of resolution:
In spring you can inject a Map<String, Command> into the mailbox (Its a built-in feature in spring) so that the key of the map will be a bean name, exactly an action of your envelop.
So here is the solution (simplified in places not relevant to injection, just to illustrate the idea):
public interface Command {
void execute();
}
#Component("delete") // note this "delete" word - it will be a key in the map in the Mailbox
public class DeleteMailCommand implements Command {
#Override
public void execute() {
System.out.println("Deleting email");
}
}
#Component("send")
public class SendMailCommand implements Command{
#Override
public void execute() {
System.out.println("Sending Mail");
}
}
Note, that all the commands must be driven by spring (which seems to be your case anyway).
Now, the Mailbox will look like this:
#Service
public class Mailbox {
private final Map<String, Command> allCommands;
private final MailProcessor processor;
// Note this map: it will be ["delete" -> <bean of type DeleteMailCommand>, "send" -> <bean of type SendMailCommand>]
public Mailbox(Map<String, Command> allCommands, MailProcessor mailProcessor) {
this.allCommands = allCommands;
this.processor = mailProcessor;
}
public void processUserInput(Envelope envelope) {
Command cmd = allCommands.get(envelope.getAction());
processor.executeCommand(cmd);
}
}
This solution is easily unit testable, because you can populate the map with mock commands if you wish and there is no need to deal with the application context.
Update
I took a look on your test now, and it's also not really good, sorry :)
#RunWith(MockitoJUnitRunner.class) is used to run unit tests (without spring at all). There is no point in placing this annotation in conjunction with #SpringBootTest which runs a full-fledged system test: starts the whole spring boot application, loads configurations and so forth.
So make sure what kind of tests you want to run and use the appropriate annotations.
Can't say for sure without debugging but it looks like MyApplication.getApplicationContext() is returning null.
Instead of storing it in a static variable you should try injecting the ApplicationContext in your #Service class where you need it:
#Autowired
private ApplicationContext appContext;
Try initializing mailbox object by injecting processor before first test.
mailbox = new Mailbox(processor);

Calling one GrpcService from other using dependency injection

I have two grpc service in same project built using spring boot. And I am trying to call Y service in X service using autowiring but I am not able to pass required StreamObserver to the Y service method.
Below is the code.
XService.java
#GrpcService
public class XService extends XServiceImplBase {
#Autowired
YService yService;
#Override
public void getInfo(RequestId request, StreamObserver<Info> responseObserver) {
// Not abe to pass the second argument here
yService.getData(request, <observer>);
}
}
YService.java
#GrpcService
public class YService extends YServiceImplBase {
#Override
public void getData(RequestId request, StreamObserver<Data> responseObserver) {
// Code Logic
}
}
Thanks in Advance!
Create a client with same name as server in application.properties and call with stub. Worked for me

How to make Aysnc annotation work in Spring Boot?

I created a simple spring boot application with scheduled (#Scheduled) task. In that scheduled task, I would like to call async function with #Async, but I can see it still runs on the scheduling thread without switch to another thread. I also tried to customise executor, but no luck. Here are some codes.
I also already enable async in main class
public class scheduledService {
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
AsyncService service = new AsyncService();
service.asyncCall();
service.asyncCall();
service.asyncCall();
asyncCall();
}
}
public class AsyncService {
#Async()
public void asyncCall(){
System.out.printly("Current thread -- {}",Thread.currentThread().getName()))
Thread.sleep(10000);
}
}
#Bean(name = "MyThreadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyThreadPoolExecutor-");
executor.initialize();
return executor;
}
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class ScheduledApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ScheduledApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
according to Baeldung:
#Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won't work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.
so you can put your async method in a service and use it from there
you need to autowire AsyncService, do not create new object like
AsyncService service = new AsyncService();
Also, annotate your scheduledService class with #Service or #Component
#Service
public class scheduledService {
#Autowired
private AsyncService service ;
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
service.asyncCall();
service.asyncCall();
service.asyncCall();
}
}
不要在同一个类中调用异步方法
Do not call asynchronous methods in the same class.
将异步任务单独放到一个类 并且在这个类上加上#Component
Put asynchronous tasks into a single class and add # component to this class
Use #EnableAsync on the top of class where you are creating async bean, not on the ScheduledApplication.

Call a method after Spring Boot app starts

I have a Java Spring Boot application which has a Scheduler which calls a async task from a Service The task takes a few minutes (usually 3-5mins) to complete.
The same async method from the Service can also be called trough a UI Application, by calling the API from the Spring Boot Controller.
Code:
Scheduler
#Component
public class ScheduledTasks {
#Autowired
private MyService myService;
#Scheduled(cron = "0 0 */1 * * ?")
public void scheduleAsyncTask() {
myService.doAsync();
}
}
Service
#Service
public class MyService {
#Async("threadTaskExecutor")
public void doAsync() {
//Do Stuff
}
}
Controller
#CrossOrigin
#RestController
#RequestMapping("/mysrv")
public class MyController {
#Autowired
private MyService myService;
#CrossOrigin
#RequestMapping(value = "/", method = RequestMethod.POST)
public void postAsyncUpdate() {
myService.doAsync();
}
}
The scheduler runs the async task every hour, but a user can also run it manually from the UI.
But, I do not want the async method to run again if it is already in the middle of execution.
In order to do that, I have created a table in DB which contains a flag which goes on when the method is running and then it is turned off after the method completes.
Something like this in my service class:
#Autowired
private MyDbRepo myDbRepo;
#Async("threadTaskExecutor")
public void doAsync() {
if (!myDbRepo.isRunning()) {
myDbRepo.setIsRunning(true);
//Do Stuff
myDbRepo.setIsRunning(false);
} else {
LOG.info("The Async task is already running");
}
}
Now, the problem is that the flag sometimes gets stuck due to various reasons (app restarting, some other application error etc.)
So, I want to reset the flag in DB each time the spring boot application is deployed and whenever is restarts.
How can I do that? Is there some way to run a method just after the Spring Boot Application starts, from where I can call a method from my Repo to un set the flags in the database?
Check for the #PostConstruct for e.g here https://www.baeldung.com/running-setup-logic-on-startup-in-spring
If you want to do some stuff after whole application booted and ready use below sample from
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
} // class
If it is enough to hook after a single bean creating use #PostConstruct as suggested by #loan M
In your particular case , you need to reset the database after application deployment, so the best way for you to do that is to use the Spring CommandLineRunner .
Spring boot provides a CommanLineRunner interface with a callback run() method which can be invoked at application startup
after the Spring application context is instantiated.
CommandLineRunner beans can be defined within the same application context and can be ordered using the #Ordered interface or #Order
annotation.
#Component
public class CommandLineAppStartupRunnerSample implements CommandLineRunner {
private static final Logger LOG =
LoggerFactory.getLogger(CommandLineAppStartupRunnerSample .class);
#Override
public void run(String...args) throws Exception {
LOG.info("Run method is executed");
//Do something here
}
}
Answer referred from site : https://www.baeldung.com/running-setup-logic-on-startup-in-spring

Categories

Resources