How to run #PostConstruct non-blocking in Spring? - java

#PostConstruct
public void performStateChecks() {
throw new RuntimeException("test");
}
If I start a spring application with code above, it will prevent the application to start.
What I'm looking for is to execute a method directly after startup, but async. Means, it should not delay the startup, and it should not prevent the application to run even on failure.
How can I make the initialization async?

You can use EventListener instead of PostConstruct, it supports #Async:
#Service
public class MyService {
#Async
#EventListener(ApplicationStartedEvent.class)
public void performStateChecks() {
throw new RuntimeException("test");
}
}
Don't forget enable async support by #EnableAsync annotation
You can also use some other event, see inheritors of SpringApplicationEvent class

The easiest way I can see is by using EventListeners and async task executors.
Adding this code snippet would do the work:
#Component
public class AsyncStartupRunner {
#Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster =
new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
#EventListener(ApplicationReadyEvent.class)
public void executeAfterStartup() {
throw new RuntimeException("Oops");
}
}

There are multiple ways this can be done.
First, simple one-liner solution is to create and start a new thread;
#PostConstruct
public void performStateChecks() {
new Thread(() -> { throw new RuntimeException("test"); }).start();
}
The thrown exception only interrupts the separate thread and doesn't block or prevent the application startup. This is useful if you are not interested in result or outcome of the task. Note this is not recommended as it starts separate thread outside spring managed context.
Second is to use executor service and submit a task to it. Spring provides a default ThreadPoolTaskExecutor which can be used to submit the tasks. This will allow you to have access to the future object of the task and do something with it later on;
private final ThreadPoolTaskExecutor executor; // inject via constructor
#PostConstruct
public void performStateChecks() {
Future<?> future = executor.submit(() -> {
throw new RuntimeException("test");
});
// do something with the future later on
}
If you have multiple such methods and requirements for various services/classes etc then create a new AsyncService class to do the actual work and annotate those methods with #Async. inject the AsyncService wherever you need via constructor and then call the required method;
#EnableAsync
#Component
public class AsyncService {
#Async
public void doActualTest() {
throw new RuntimeException("test");
}
}
Then use it like this;
private final AsyncService asyncService; // make sure to inject this via constructor
#PostConstruct
public void performStateChecks() {
asyncService.doActualTest();
}

You can remove #PostConstruct from your method and let that method be a normal method. You can then manualy invoke it when the ApplicatioinContext is already loaded and the application has already started.
#SpringBootApplication
public class ServiceLauncher {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplication(ServiceLauncher.class).run(args);
try {
context.getBean(YourBean.class).performStateChecks(); // <-- this will run only after context initialization finishes
} catch (Exception e) {
//catch any exception here so it does not go down
}
}
}
}

Related

Spring: How to start the logic after initializing the app

I have implemented this logic
#Component
public class SomeUpdater {
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
private ExecutorService executorService;
#PostConstruct
public void init(){
executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
//some logic
applicationEventPublisher.publishEvent(new PurposeEvent(this, 5));
});
}
}
#Component
public class SomeClass {
#EventListener
public void update(PurposeEvent purposeEvent){
//update
}
}
But I can see from the logs that listener for update (PurposeEvent purposeEvent) is initialized later than the method applicationEventPublisher.publishEvent(new PurposeEvent(this, 5)); is called. How can I call logic after initialization? I tried using #EventListener for ContextRefreshedEvent, but as far as I understand, it can be called multiple times
You can you annotation #DependsOn which can force the Spring container to initialize one or more beans before the bean annotated with #DependsOn annotation.
Then you'll be sure that SomeClass will be initialized before SomeUpdater.
The long term solution would be:
Spring Boot startup hooks
Spring Event types, like ApplicationStartedEvent, ApplicationReadyEvent.
Useful links:
http://dolszewski.com/spring/running-code-on-spring-boot-startup/
http://javainfinite.com/spring-boot/springboot-applicationreadyevent-applicationfailedevent-contextrefreshedevent/
You can make use of spring events like below,
#EventListener(ApplicationReadyEvent.class)
public void init() {
final ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
//some logic
applicationEventPublisher.publishEvent(new PurposeEvent(this, 5));
});
}

Sharing an instance of a class across a spring boot application

I have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}

Autowiring multiple SimpleJobLaunchers

I have a class which I want to run jobs from asynchronously. To do this I have the following code:
#Resource
private SimpleJobLauncher jobLauncher;
#PostConstruct
public void initLauncher(){
jobLauncher.setTaskExecutor(taskExecutor());
}
However, there is a situation where I need to do this synchronously. So, what I have done is added the following variable:
#Resource
private SimpleJobLauncher synchronousJobLauncher;
Which I hoped wouldn't have the taskExecutor on it making it synchronous. I then pass the synchronousJobLauncher to the places where I want to do things synchronously. However, using the synchronousJobLauncher gives me the same error as when I use the asynchronous one which leads me to believe that you cannot autowire the same variable twice like I am trying to do. If I do not do the #PostConstruct part of the code, the synchronous part works like I expect but not the asynchronous part, even though they use, what I think are, different job launchers.
Any ideas? I have tried using the #Resource annotation instead of #Autowired.
I haven't used SimpleJobLaunchers, but normally, in Spring I always used the #Async annotation, which make asynchronous execution quite simple. All you have to do is add this annotation, #EnableAsync in any configuration file, just like the one below.
#Configuration
#EnableAsync
public class MvcConfig extends WebMvcConfigurerAdapter { ... }
Now, is all about adding the #Async to any method that will run asynchronously.
#Component
class AsyncTask {
#Async
public Future<String> run() throws InterruptedException {
return new AsyncResult<String>("return value");
}
}
And if you want to wait for the result, you can do the following.
public void callAsyncTask() {
try {
Future<String> future = asyncTask.run();
// Do some other things while the async task is running.
// future.get() will block the function and wait for the result.
String asyncResult = future.get();
} catch(InterruptedException e) {
e.printStackTrace();
// Do something else.
}
}
Hope this helps. I know is not directly what you were asking for, but maybe this solution can facilitate your problem.
You can create two job launchers (one synchronous and one asynchronous) then inject the one you want using a qualifier. By default, the SimpleJobLauncher uses a synchronous task executor, so you need to configure a task executor only for the asynchronous one. Here is an example:
#Bean
public SimpleJobLauncher synchronousJobLauncher() {
return new SimpleJobLauncher();
}
#Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
#Bean
public SimpleJobLauncher asynchronousJobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setTaskExecutor(threadPoolTaskExecutor());
return jobLauncher;
}
Then, inject the job launcher you want with a #Qualifier("synchronousJobLauncher") or #Qualifier("asynchronousJobLauncher") according to your needs.
Hope this helps.

How to properly destroy a Spring configuration class

Case 1
Let's consider the following Spring configuration:
#Configuration
public class MyConf1 {
#Bean
public Foo getFoo() {
// Foo class is defined as part of an external lib.
return new Foo();
}
#Bean
public Bar getBar() {
return new Bar(getFoo());
}
}
For some reasons, I need to invoke a Foo's method (i.e. myFoo.shutdown();) when MyConf1 is destroyed.
Is there any way to perform this operation without retrieving the bean instance directly from the application context (via ApplicationContext.getBean())?
Case 2
Again, let's consider a second Spring configuration class:
#Configuration
public class MyConf2 {
#Bean
public ScheduledJob scheduledJob() {
Timer jobTimer = new Timer(true);
return new ScheduledJob(jobTimer);
}
}
This time, I need to invoke jobTimer.cancel() before destroying MyConf2. Indeed, I can instantiate jobTimer outside scheduledJob(), or making it a method's parameter, as scheduledJob(Timer jobTimer).
It will then be possible to define a proper destroyer method for MyConf2. However, I would like to know if there are other ways to proceed.
Any good suggestion?
Note: Foo, Bar, Timer, ScheduledJob classes are defined externally. Thus, there is no possibility to explicitly define an inner destroy method. As assumption, I can modify only MyConf1 and MyConf2.
I would suggest defining a destroy() method (annotated with #PreDestroy) in Foo class
Similarly, modify ScheduledJob class like
public class ScheduledJob {
private Timer timer;
public ScheduledJob(Timer timer){
this.timer = timer;
}
#PreDestroy
public void destroy(){
timer.cancel();
}
}
And add destroyMethod param in #Bean
#Configuration
public class MyConf2 {
#Bean(destroyMethod = "destroy")
public ScheduledJob scheduledJob() {
Timer jobTimer = new Timer(true);
return new ScheduledJob(jobTimer);
}
}
Please see the following page http://forum.spring.io/forum/spring-projects/container/48426-postconstruct-and-predestroy-in-javaconfig
DisposableBean should help you with case #1.
You can implement the DestructionAwareBeanPostProcessor interface that can adds a before-destruction callback when the bean is destroy.In that interface,the method postProcessBeforeDestruction is do that,see the following:
#Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
System.out.println("before destory:"+bean);
}
#Override
public boolean requiresDestruction(Object bean) {
return true;
}
Pay attention to that the method requiresDestruction should return true,otherwise the method postProcessBeforeDestruction will not call when bean should destroy.
And i have a test:
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:application-main.xml");
applicationContext.registerShutdownHook();
}
The postProcessBeforeDestruction really call when the bean is destroy.The output is :
before destory:com.zhuyiren.spring.learn.util.Handler#55141def
before destory:com.zhuyiren.spring.learn.controller.TestControleler#47eaca72
before destory:com.zhuyiren.spring.learn.service.impl.TestServiceImpl#7b2bbc3
before destory:com.zhuyiren.spring.learn.service.impl.TwoServiceImpl#48f2bd5b
before destory:com.zhuyiren.spring.learn.controller.ConverConroller#72967906
before destory:org.springframework.context.event.DefaultEventListenerFactory#1a482e36
before destory:org.springframework.context.event.EventListenerMethodProcessor#77fbd92c

How to shutdown ThreadPoolExecutor in spring bean when tomcat is shutdown?

I create a threadpoolexecutor in a spring bean, so I need to shutdown this executor when tomcat is shutdown.
public class PersistBO implements InitializingBean {
private ThreadPoolExecutor executer;
public void shutdownExecutor() {
executer.shutdown();
}
#Override
public void afterPropertiesSet() throws Exception {
taskQueue = new LinkedBlockingQueue<Runnable>(queueLength);
executer = new ThreadPoolExecutor(minThread, maxThread, threadKeepAliveTime,
TimeUnit.SECONDS, taskQueue, new ThreadPoolExecutor.DiscardOldestPolicy());
}
I have searched solution on google and get a result. That is to add a shutdownhook to java.lang.runtime. However, the java docs says java.lang.Runtime#shutdownHook is called when the last non-daemon thread exits. So it is a dead lock. Is there any solution to shutdown executor in spring bean?
I guess lifecycle of the executor should depend on lifecycle of your application, not Tomcat as a whole. You can stop your application while Tomcat is still running, therefore Runtime.shutdownHook() is not applicable.
Since you already use Spring and its InitializingBean for initialization, you can use DispasableBean to perform cleanup when application context is being closed:
public class PersistBO implements InitializingBean, DisposableBean {
public void destroy() {
shutdownExecutor();
}
...
}
Use the Runtime to add a shutdown hook. Here's a very good tutorial by Heinz Kabutz: http://www.roseindia.net/javatutorials/hooking%20_into_the_shutdown_call.shtml
You can implement your own javax.servlet.ServletContextListener to be notified when your application is being shutdown and shutdown the pool from the listener.
Use the #Predestroy annotation on your shutdown method on the bean. this will result in spring calling this method when context is shutting down
Check if there is some executor service has a thread running in background. you can shutdown an executor by calling executor.shutdownNow().
also see http://taranmeet.com/jvm-not-shutting-down-on-spring-context-close/
Here is how to start and stop a thread in Spring bean.
#PostConstruct
public void init() {
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("myspringbean-thread-%d").build();
executorService = Executors.newSingleThreadExecutor(factory);
executorService.execute(new Runnable() {
#Override
public void run() {
try {
// do something
System.out.println("thread started");
} catch (Exception e) {
logger.error("error: ", e);
}
}
});
executorService.shutdown();
}
#PreDestroy
public void beandestroy() {
if(executorService != null){
executorService.shutdownNow();
}
}

Categories