I am trying to hook into the creation of the context using a custom application listener like this
#Component
public class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {
#Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("Context started"); // this never happens
}
}
But the onApplicationEvent method never fires. If I use a different event such as ContextRefreshedEvent then it works just fine, but I need to hook into before it is created. Any advice? Thanks!
[Edit]
Editing answer adding more info because of the downvote.
The reason why you are not getting a callback by the listener is because you are not explicitly calling the LifeCycle start() method (JavaDoc).
This cascades down to your ApplicationContext normally via the AbstractApplicationContext on in Spring Boot case via the ConfigurableApplicationContext.
Example of working code below demonstrating how your callback would work (just explicitly call the start() method)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
applicationContext.start();
}
#Component
class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {
#Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("Context started");
}
}
}
The reason why I suggested below the ContextRefreshedEvent callback instead is because behind the scenes the refresh() code is getting invoked.
If you drill down the SpringApplication#run() method you'll eventually see it.
Again here's a working example of how this would work using the ContextRefreshedEvent:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Component
class ContextStartedListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("Context refreshed");
}
}
}
[Before Edit]
Change the Generic type to ContextRefreshedEvent instead and then it should work.
For more details read this article from the Spring Blog. Just to quote the part about the ContextRefreshedEvent:
[..]This allows MyListener to be notified when the context has refreshed
and one can use that to run arbitrary code when the application
context has fully started.[..]
Related
I'm trying to print a message after the application startup with #PostConstruct, but nothing is printed.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
I have read that #PostConstruct is Lazy. Does this mean that I need to do
something else for this to work?
You can also use #EventListener annotation to acheive what you what, if using #PostConstruct is not that important to you.
For example in your case, you can add following code in any class to listen for application startup event.
#EventListener
void onStartup(ServerStartupEvent event) {
println("Hey, I work from anywhere in project..")
}
Code shared above is in Groovy
Keep in mind, the event listener added in main application class is usually called first from what I have observed.
The problem (aka feature) is, as you already mentioned, the lazy loading.
I see two solutions:
You have to do something to cause that bean to be initialized.
Change the scope of the bean from #Singleton to #Context
Micronaut has a few built-in scopes (see https://docs.micronaut.io/latest/guide/index.html#scopes) and the documentation of #Context states (see https://docs.micronaut.io/latest/api/io/micronaut/context/annotation/Context.html)
Context scope indicates that the classes life cycle is bound to that of the BeanContext and it should be initialized and shutdown during startup and shutdown of the underlying BeanContext.
Micronaut by default treats all Singleton bean definitions as lazy and will only load them on demand. By annotating a bean with #Context you can ensure that the bean is loaded at the same time as the context.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import io.micronaut.context.annotation.Context;
#Context
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
See the project at https://github.com/jeffbrown/renansouzapostconstruct.
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/ServerService.java
package renansouzapostconstruct;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/DemoController.java
package renansouzapostconstruct;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.HttpStatus;
#Controller("/demo")
public class DemoController {
private ServerService serverService;
public DemoController(ServerService serverService) {
this.serverService = serverService;
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
}
When you start the app you won't see the message printed to standard out because the service bean won't have been initialized. Send a request to http://localhost:8080/demo/ and then you will see the message printed to stdout.
I hope that helps.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I have Java Application that changes a game board (2D). Now I want to have a JavaFx GUI to visualize the board.
Main:
package example;
import example.common.MyService;
import example.gui.GUI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan({"example"})
public class Main implements CommandLineRunner {
#Autowired
MyService myService;
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
GUI.launchApp(GUI.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println("gameloop or something");
System.out.println(myService.getSomething());
}
}
AbstractJavaFxApplicationSupport:
package example.gui;
import javafx.application.Application;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
#Component
public abstract class AbstractJavaFxApplicationSupport extends Application {
private static String[] savedArgs;
static ConfigurableApplicationContext applicationContext;
#Override
public void init() throws Exception {
super.init();
applicationContext = SpringApplication.run(getClass(), savedArgs);
applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
}
#Override
public void stop() throws Exception {
super.stop();
applicationContext.close();
}
public static void launchApp(Class<? extends AbstractJavaFxApplicationSupport> appClass, String[] args) {
AbstractJavaFxApplicationSupport.savedArgs = args;
Application.launch(appClass, args);
}
}
GUI:
package example.gui;
import example.common.MyService;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class GUI extends AbstractJavaFxApplicationSupport {
#Autowired
private MyService myService;
#Override
public void start(Stage primaryStage) throws Exception {
if (null == myService) {
throw new IllegalStateException("Service was not injected properly");
}
primaryStage.setTitle("Spring with JavaFX");
StackPane root = new StackPane();
root.getChildren().add(new Label("Hello World with " + myService.getSomething()));
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
}
}
MyService:
package example.common;
import org.springframework.stereotype.Component;
#Component
public class MyService {
public int getSomething() {
return 42;
}
}
Most JavaFx spring boot integrations are like the shown above: They prescribe the GUI as entry point for the application. If I run this example two individual applications are booted (obviously. Because there are two SpringApplication.run calls). If you want a Standalone GUI this is fine but for my usecase it is not.
What I really want is one boot and that they share the same context. How to archive this? I would be grateful if somebody could lead me in the right direction.
You need follow fix for your AbstractJavaFxApplicationSupport:
import javafx.application.Application;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
#Component
public abstract class AbstractJavaFxApplicationSupport extends Application {
static ConfigurableApplicationContext applicationContext;
#Override
public void init() throws Exception {
super.init();
applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
}
#Override
public void stop() throws Exception {
super.stop();
applicationContext.close();
}
public static void launchApp(Class<? extends AbstractJavaFxApplicationSupport> appClass, ConfigurableApplicationContext context, String[] args) {
applicationContext = context;
Application.launch(appClass, args);
}
}
So, for your example thats enough. You just pass context created before.
But at first, I don't think that you need make your application as component of context - I don't know how can you use it. At second, I think you will use fxml for your UI, and for this purposes you can use FxmlLoader. This loader has term Controller which means that this Controller will initialize all components (in terms of JavaFx) in this class. So, for dependency injection for this Controllers you can use method FxmlLoader.setControllerFactory(context::getBean);. But it will work only for this Controller's, not for some views or panels
How can I register/add a custom shutdown routine that shall fire when my Spring Boot application shuts down?
Scenario: I deploy my Spring Boot application to a Jetty servlet container (i.e., no embedded Jetty). My application uses Logback for logging, and I want to change logging levels during runtime using Logback's MBean JMX configurator. Its documentation states that to avoid memory leaks, on shutdown a specific LoggerContext shutdown method has to be called.
What are good ways to listen on Spring Boot shutdown events?
I have tried:
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext cac = SpringApplication.run(Example.class, args);
cac.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
#Override
public void onApplicationEvent(ContextClosedEvent event) {
logger.info("Do something");
}
});
}
but this registered listener does not get called when the application shuts down.
https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#features.spring-application.application-exit
Each SpringApplication will register a shutdown hook with the JVM to ensure that the ApplicationContext is closed gracefully on exit. All the standard Spring lifecycle callbacks (such as the DisposableBean interface, or the #PreDestroy annotation) can be used.
In addition, beans may implement the org.springframework.boot.ExitCodeGenerator interface if they wish to return a specific exit code when the application ends.
have you tried this as mentioned by #cfrick ?
#SpringBootApplication
#Slf4j
public class SpringBootShutdownHookApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootShutdownHookApplication.class, args);
}
#PreDestroy
public void onExit() {
log.info("###STOPing###");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
log.error("", e);;
}
log.info("###STOP FROM THE LIFECYCLE###");
}
}
Your listener is registered too late (that line will never be reached until the context has already closed). It should suffice to make it a #Bean.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#NotNull
#Bean
ServletListenerRegistrationBean<ServletContextListener> myServletListener() {
ServletListenerRegistrationBean<ServletContextListener> srb =
new ServletListenerRegistrationBean<>();
srb.setListener(new ExampleServletContextListener());
return srb;
}
}
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ExampleServletContextListener implements ServletContextListener {
#Override
public void contextInitialized(
ServletContextEvent sce) {
// Context Initialised
}
#Override
public void contextDestroyed(
ServletContextEvent sce) {
// Here - what you want to do that context shutdown
}
}
I have a similar use case, where I have to hold the server's shutdown process for some minutes, I have used the same approach mentioned in the question, the only change is instead of adding the listener after booting the service, I have added the listener (ContextClosedEvent) before running the application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners((ApplicationListener<ContextClosedEvent>) event -> {
log.info("Shutdown process initiated...");
try {
Thread.sleep(TimeUnit.MINUTES.toMillis(5));
} catch (InterruptedException e) {
log.error("Exception is thrown during the ContextClosedEvent", e);
}
log.info("Graceful Shutdown is processed successfully");
});
application.run(args);
}
}
Here is my Service class
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.stereotype.Service;
#Service
public class EmailService implements MessageHandler {
#Autowired
#Qualifier("receiveChannel")
private DirectChannel messageChannel;
private final Log logger = LogFactory
.getLog(EmailService.class);
public void init() {
logger.info("initializing...");
System.out.println("INIT");
messageChannel.subscribe(this);
}
#Override
public void handleMessage(Message<?> message) throws MessagingException {
logger.info("Message: " + message);
}
}
For the init I want to create a JUnit Test Case. How to write?
Here is what I have tried. But it is not working
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("classpath:webapptest")
#ContextConfiguration(locations = {"classpath:test-applicationcontext.xml"})
public class EmailServiceTest {
#Autowired
private EmailService emailService;
#Test(timeout=600000)
public void testEmailService() throws Exception {
emailService=Mockito.spy(emailService);
Mockito.doNothing().when(emailService).init();
}
}
In the console it is not printing the logger or print statements in the init() method.
What error I am doing? How to write the test case?
In your tests you havent called init(). It will not execute the Mockito.doNothing().when without you calling the init() method. As far as your code is concerned, the init() method is just a regular public method.
If you do want the init() method to be called after the class is instantiated, you would have to annotate it with a #PostConstruct annotation.
Your test should be something like this below
#Test(timeout=600000)
public void testEmailService() throws Exception {
.....
emailService.init();
}
You would have to call emailService.init() since you have created a spy; for the test to work. Currently you arent testing anything, just have a bunch of Mocks in your test method.
Also, a comprehensive test would be where you verify if the messageChannle.subscribe() method is called upon testing the init method.
You do want to tighten your test by verifying that the subscribe() method is called or not.
In a project we use annotions to define aspects.
Unfortunately I can't get eclipse to show a marker next to the advised methods.
In another project we use XML to define the aspects and eclipse shows markers.
Best I post some code to clarify:
First a bean to be advised:
package aop.test;
import org.springframework.stereotype.Service;
#Service
public class Worker {
public void work() {}
}
Then the aspect:
package aop.test;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
#Aspect
#Service
public class WorkerLogger {
#Before("execution(void aop.test.Worker.work())")
public void log() {
System.out.println("working...");
}
}
And finally a main method to prepare the ApplicationContext, get the bean and run the advised method:
package aop.test;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context;
context = new AnnotationConfigApplicationContext();
context.register(AnnotationAwareAspectJAutoProxyCreator.class);
context.scan("aop.test");
context.refresh();
context.getBean(Worker.class).work();
}
}
I tried this in eclipse using the STS plugin and the STS itself. I never get a red arrow next to work() indicating it is advised.
What am I missing?
Have you installed the STS plugin on your Eclipse? It should be available on the Help > Eclipse Marketplace