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.
I have the below FeignClient:
#FeignClient(name="FooMS",fallback=CustomerFeign.CustomerFeignImpl.class)
public interface CustomerFeign {
#RequestMapping(value="/bar/{phoneNo}")
List<Long> getFriends(#PathVariable("phoneNo") Long phoneNo);
class CustomerFeignImpl implements CustomerFeign{
#Override
public List<Long> getFriends(Long phoneNo) {
return new ArrayList<Long>(108);
}
}
}
When the FooMS instance is down, I get a 500 error instead of the fallback being executed. Why is this happening?
adding #Component and feign.hystrix.enabled=true works fine
Tag your CustomerFeignImpl as a #Component or create a #Bean out of it.
This works for me with 2020.0.3:
In application.properties
feign.circuitbreaker.enabled=true
In pom.xml
<spring-cloud.version>2020.0.3</spring-cloud.version>
and
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Thank you, rostlvan!
I am outlining my implementation below:
I am using Spring Cloud version 2020.0.4 and the following configuration worked for me:
in pom.xml, I have these dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
Though I'm not sure if we need to have both openfeign and hystrix dependencies. Someone can validate that!
In my application.properties I have feign.circuitbreaker.enabled=true
In my Main Application class, I have
#SpringBootApplication
#EnableFeignClients
public class MySpringBootApplication{
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
}
And finally, my Feign Client, fallback and fallback factory:
UserServiceFeignClient.java
#FeignClient(name = "USER-SERVICE", fallbackFactory = UserServiceFallbackFactory.class)
public interface UserServiceFeignClient {
#GetMapping("/api/users/{userId}")
public ResponseEntity<User> getUser(#PathVariable String userId);
}
UserServiceFeignClientFallback.java
public class UserServiceFeignClientFallback implements UserServiceFeignClient{
#Override
public ResponseEntity<User> getUser(String userId) {
return ResponseEntity.ok().body(new User());
}
}
And, UserServiceFeignClientFallbackFactory.java:
#Component
public class UserServiceFallbackFactory implements FallbackFactory<UserServiceFeignClientFallback>{
#Override
public UserServiceFeignClientFallback create(Throwable cause) {
return new UserServiceFeignClientFallback();
}
}
Was facing the problem myself, until I stumbled upon the answer from #rostlvan
Connecting to my web socket end point seems to (randomly) hang more often than not.
Here is the code that registers the endpoint -
#Configuration
#EnableWebSocket
public class WebSocketEndpointConfigurer implements WebSocketConfigurer {
#Autowired
private WebSocketConfig webSocketConfig;
#Autowired
private WebSocketEndpoint webSocketEndpoint;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry
.addHandler(webSocketEndpoint, webSocketConfig.getEndpoint()).setAllowedOrigins("*");
}
}
And here is my websocket end point -
#Component
public class WebSocketEndpoint extends TextWebSocketHandler {
private static Logger logger = LoggerFactory.getNoAppLogger(WebSocketEndpoint.class);
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("Websocket connection created");
//do more things
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
//do things
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//do things
}
}
I'd say 50% of the time when i try to initiate the connection it is successful. The other 50% of the time it reaches the afterConnectionEstablished() method and I see my log message but it never returns to the browser and Chrome's debug tool says 'pending'.
If I restart my Spring Boot server sometimes it fixes it sometimes it doesn't, it is totally random. Am I missing something here?
Thanks in advance, this is really bugging me.
EDIT: I have tried this with multiple web socket testing clients and have the same results for all.
Figured it out...
Our corporate anti-virus introduced a bug that affects web sockets....
I have a simple method in a Service in a SpringBoot Application. I have the retry mechanism setup for that method using #Retryable.
I am trying integration tests for the method in the service and the retries are not happening when the method throws an exception. The method gets executed only once.
public interface ActionService {
#Retryable(maxAttempts = 3, backoff = #Backoff(delay = 2000))
public void perform() throws Exception;
}
#Service
public class ActionServiceImpl implements ActionService {
#Override
public void perform() throws Exception() {
throw new Exception();
}
}
#SpringBootApplication
#Import(RetryConfig.class)
public class MyApp {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApp.class, args);
}
}
#Configuration
#EnableRetry
public class RetryConfig {
#Bean
public ActionService actionService() { return new ActionServiceImpl(); }
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration( classes= {MyApp.class})
#IntegrationTest({"server.port:0", "management.port:0"})
public class MyAppIntegrationTest {
#Autowired
private ActionService actionService;
public void testAction() {
actionService.perform();
}
Your annotation #EnableRetry is at the wrong place, instead of putting it on the ActionService interface you should place it with a Spring Java based #Configuration class, in this instance with the MyApp class. With this change the retry logic should work as expected. Here is a blog post that I had written on if you are interested in more details - http://biju-allandsundry.blogspot.com/2014/12/spring-retry-ways-to-integrate-with.html
Biju Thanks for putting up a link for this, it helped me to resolve a lot of questions. The only thing that I had to seperately was I still had to add 'retryAdvice' as a bean in the spring xml based approach, Also had to make sure I had context annotation enabled and aspectj is available at classpath. After I added these below I could get it working.
<bean id="retryAdvice"
class="org.springframework.retry.interceptor.RetryOperationsInterceptor">
</bean>
<context:annotation-config />
<aop:aspectj-autoproxy />
I guess I am dealing with a bug in Glassfish 4; but I am not sure. Basically I am trying to inject a service into a ContainerRequestFilter; but I am getting an exception while trying. I can do injection in resources where I make rest calls, but I can't use injection for filters. There is a filed bug in glassfish jira: https://java.net/jira/browse/GLASSFISH-20597. I tried the workaround mentioned in there but it didn't solve my problem.
Here is the exception I get:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=Authenticator,parent=SecurityInterceptor,qualifiers
Here is the code that I am working with, do you have any idea what I am missing here?
#Provider
#PreMatching
public class SecurityInterceptor implements ContainerRequestFilter {
#Inject
private Authenticator authenticator; // can't inject this service here, but I can inject this to RequestScoped Resources
private static final Logger LOG = Logger.getLogger(SecurityInterceptor.class.getName());
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (!requestContext.getMethod().equals("OPTIONS")) {
String path = OyumBuFunctions.normalizeString(requestContext.getUriInfo().getPath());
String authToken = requestContext.getHeaderString(OyumbuRestHeaders.AUTH_TOKEN_HEADER);
if (!authenticator.isAuthTokenValid(authToken)) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
else{
LOG.log(Level.INFO, "authenticated");
}
}
}
}
I ran into a similar issue and came across this posting while searching for a solution. Fortunately I found a solution and thought to post it here. Note that this only works in JAX-RS 2.0 as it makes use of the DynamicFeature class
#Provider
public class AuthenticatorFeature implements DynamicFeature {
#Inject
private Autheticator authenticator;
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
context.register(new SecurityInterceptor(authenticator);
}
}
Binding the authenticator to the ContainerRequestFilter.
public class SecurityInterceptor implements ContainerRequestFilter {
Authenticator authenticator;
public SecurityInterceptor(Authenticator authenticator){
this.authenticator = authenticator;
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
authenticator.doSomething(...);
}
}
Some hopefully helpful readings
creating resource filters with jersey and jaxrs 2.0
Securing REST Resources with JAX-RS 2.0
As mentioned in this answer https://stackoverflow.com/a/23996225/1045081, giving #Path annotation to the service to be injected saves the situation. Still didn't understand how to solve it properly, but just writing it here so that it could be useful for someone who encounters a similar problem.