197

Is there any Spring 3 feature to execute some methods when the application starts for the first time? I know that I can do the trick of setting a method with @Scheduled annotation and it executes just after the startup, but then it will execute periodically.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Javi
  • 19,387
  • 30
  • 102
  • 135

13 Answers13

211

If by "application startup" you mean "application context startup", then yes, there are many ways to do this, the easiest (for singletons beans, anyway) being to annotate your method with @PostConstruct. Take a look at the link to see the other options, but in summary they are:

  • Methods annotated with @PostConstruct
  • afterPropertiesSet() as defined by the InitializingBean callback interface
  • A custom configured init() method

Technically, these are hooks into the bean lifecycle, rather than the context lifecycle, but in 99% of cases, the two are equivalent.

If you need to hook specifically into the context startup/shutdown, then you can implement the Lifecycle interface instead, but that's probably unnecessary.

Sarge
  • 2,367
  • 2
  • 23
  • 36
skaffman
  • 398,947
  • 96
  • 818
  • 769
  • 7
    I have yet to see an implementation of Lifecycle or SmartLifecycle after quite a bit of research. I know this is a year old, but skaffman if you have anything you can post that would be much appreciated. –  Jun 07 '11 at 01:50
  • 4
    The methods above get invoked before the entire application context has been created (eg. /before/ transaction demarcation has been setup). – Hans Westerbeek Mar 11 '15 at 12:37
  • I get a strange warning trying to use @PostConstruct in java 1.8: `Access restriction: The type PostConstruct is not accessible due to restriction on required library /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar` – encrest Jul 08 '15 at 21:36
  • 2
    There are important cases where _bean_ and _context_ lifecycle are very different. As @HansWesterbeek noted a bean can be setup before the context it depends on is fully ready. In my situation a bean depended on JMS - it was fully constructed, so its `@PostConstruct` method was called, but the JMS infra-structure it indirectly depended on was not yet fully wired up (and being Spring everything just silently failed). On switching to `@EventListener(ApplicationReadyEvent.class)` everything worked (`ApplicationReadyEvent` is Spring Boot specific for vanilla Spring see Stefan's answer). – George Hawkins Jun 20 '17 at 12:03
  • @Skaffman : what if my bean is not referred by any bean and I want to initialize the bean without being used anywhere – Sagar Kharab May 18 '18 at 03:47
  • @PostConstruct gets called twice, have you faced such issue? – iamrajshah Jan 16 '19 at 05:47
115

This is easily done with an ApplicationListener. I got this to work listening to Spring's ContextRefreshedEvent:

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class StartupHousekeeper implements ApplicationListener<ContextRefreshedEvent> {

  @Override
  public void onApplicationEvent(final ContextRefreshedEvent event) {
    // do whatever you need here 
  }
}

Application listeners run synchronously in Spring. If you want to make sure you're code is executed only once, just keep some state in your component.

UPDATE

Starting with Spring 4.2+ you can also use the @EventListener annotation to observe the ContextRefreshedEvent (thanks to @bphilipnyc for pointing this out):

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void contextRefreshedEvent() {
    // do whatever you need here 
  }
}
Community
  • 1
  • 1
Stefan Haberl
  • 9,812
  • 7
  • 72
  • 81
  • 1
    This worked for me too - perfect for one-time non-bean initialisation. – Rory Hunter Mar 30 '16 at 20:37
  • 10
    N.B. for those tempted to use `ContextStartedEvent` instead, it's harder to add the listener before the event fires. – OrangeDog Aug 10 '16 at 16:06
  • 2
    Howto call an @Autowired JPA repositopry into event? the repository is null. – e-info128 Nov 07 '16 at 23:58
  • Not working for me. I am using spring mvc 3. This mehod onApplicationEvent(___) does not get called when application starts. Any help. Here is my code @Component public class AppStartListener implements ApplicationListener { public void onApplicationEvent(final ContextRefreshedEvent event) { System.out.println("\n\n\nInside on application event"); } } – Vishwas Tyagi Mar 12 '17 at 19:49
  • @VishwasTyagi How do you start your container? Are you sure, your AppStartListener is part of your component scan? – Stefan Haberl Mar 13 '17 at 08:03
  • There are some more variations of the ContextRefreshedEvent in the answers to this question: https://stackoverflow.com/questions/3409605/calling-a-method-after-all-springbeans-and-applicationcontext-have-been-initiali – Jan H Jan 16 '18 at 07:47
  • @Stefan : how can I achieve that via xml. I can't use `@Component` as we haven't been using annotation based config in out project. – Sagar Kharab May 18 '18 at 04:05
  • Is it guaranteed that this listener invokes only once after startup? – gstackoverflow Aug 02 '18 at 14:33
  • I am using the updated version, it works. However, my debug break points aren't being caught. Any advice ? – Jalil.Jarjanazy Feb 20 '20 at 11:41
40

In Spring 4.2+ you can now simply do:

@Component
class StartupHousekeeper {

    @EventListener(ContextRefreshedEvent.class)
    public void contextRefreshedEvent() {
        //do whatever
    }
}
riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
20

With SpringBoot, we can execute a method on startup via @EventListener annotation

@Component
public class LoadDataOnStartUp
{   
    @EventListener(ApplicationReadyEvent.class)
    public void loadData()
    {
        // do something
    }
}

KAARTHIKEYAN
  • 201
  • 2
  • 3
15

If you are using spring-boot, this is the best answer.

I feel that @PostConstruct and other various life cycle interjections are round-about ways. These can lead directly to runtime issues or cause less than obvious defects due to unexpected bean/context lifecycle events. Why not just directly invoke your bean using plain Java? You still invoke the bean the 'spring way' (eg: through the spring AoP proxy). And best of all, it's plain java, can't get any simpler than that. No need for context listeners or odd schedulers.

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args);

        MyBean myBean = (MyBean)app.getBean("myBean");

        myBean.invokeMyEntryPoint();
    }
}
Zombies
  • 25,039
  • 43
  • 140
  • 225
  • 10
    This is a good idea in general but when starting up your spring application context from a integration test, main is never run! – Jonas Geiregat Aug 14 '15 at 07:58
  • @JonasGeiregat: Plus, there are other scenarios where there is no `main()` at all, for example when using an application framework (e.g. JavaServer Faces). – sleske Oct 06 '16 at 12:45
10

For Java 1.8 users that are getting a warning when trying to reference the @PostConstruct annotation, I ended up instead piggybacking off the @Scheduled annotation which you can do if you already have an @Scheduled job with fixedRate or fixedDelay.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling
@Component
public class ScheduledTasks {

private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTasks.class);

private static boolean needToRunStartupMethod = true;

    @Scheduled(fixedRate = 3600000)
    public void keepAlive() {
        //log "alive" every hour for sanity checks
        LOGGER.debug("alive");
        if (needToRunStartupMethod) {
            runOnceOnlyOnStartup();
            needToRunStartupMethod = false;
        }
    }

    public void runOnceOnlyOnStartup() {
        LOGGER.debug("running startup job");
    }

}
encrest
  • 1,757
  • 1
  • 17
  • 18
  • see also http://stackoverflow.com/questions/3564361/scheduling-tasks-to-run-once-using-the-spring-task-namespace – Joram Feb 05 '16 at 13:36
7

What we have done was extending org.springframework.web.context.ContextLoaderListener to print something when the context starts.

public class ContextLoaderListener extends org.springframework.web.context.ContextLoaderListener
{
    private static final Logger logger = LoggerFactory.getLogger( ContextLoaderListener.class );

    public ContextLoaderListener()
    {
        logger.info( "Starting application..." );
    }
}

Configure the subclass then in web.xml:

<listener>
    <listener-class>
        com.mycomp.myapp.web.context.ContextLoaderListener
    </listener-class>
</listener>
Wim Deblauwe
  • 25,113
  • 20
  • 133
  • 211
4

Attention, this is only advised if your runOnceOnStartup method depends on a fully initialized spring context. For example: you wan to call a dao with transaction demarcation

You can also use a scheduled method with fixedDelay set very high

@Scheduled(fixedDelay = Long.MAX_VALUE)
public void runOnceOnStartup() {
    dosomething();
}

This has the advantage that the whole application is wired up (Transactions, Dao, ...)

seen in Scheduling tasks to run once, using the Spring task namespace

Community
  • 1
  • 1
Joram
  • 3,166
  • 1
  • 22
  • 29
  • I don't see any advantage over using `@PostConstruct` ? – Wim Deblauwe Feb 29 '16 at 11:57
  • @WimDeblauwe depends on what you want to do in dosomething() calling an Autowired dao with Trasaction demarcation needs the whole context to be started up, not just this bean – Joram Mar 01 '16 at 13:15
  • 5
    @WimDeblauwe '@PostConstruct' method fires when the bean is initialized, the whole context might not be ready (fe Transaction management) – Joram Mar 02 '16 at 12:05
  • This is more elegant imo than post construct or any interfaces or events – aliopi Apr 27 '17 at 11:02
2
AppStartListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ApplicationReadyEvent){
            System.out.print("ciao");

        }
    }
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
dnocode
  • 1,928
  • 1
  • 18
  • 21
1

Posted another solution that implements WebApplicationInitializer and is called much before any spring bean is instantiated, in case someone has that use case

Initialize default Locale and Timezone with Spring configuration

Community
  • 1
  • 1
kisna
  • 2,869
  • 1
  • 25
  • 30
1

You can use @EventListener on your component, which will be invoked after the server is started and all beans initialized.

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}
krmanish007
  • 6,749
  • 16
  • 58
  • 100
0

If you want to configure a bean before your application is running fully, you can use @Autowired:

@Autowired
private void configureBean(MyBean: bean) {
    bean.setConfiguration(myConfiguration);
}
Cory Klein
  • 51,188
  • 43
  • 183
  • 243
0

For a file StartupHousekeeper.java located in package com.app.startup,

Do this in StartupHousekeeper.java:

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void keepHouse() {
    System.out.println("This prints at startup.");
  }
}

And do this in myDispatcher-servlet.java:

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <mvc:annotation-driven />
    <context:component-scan base-package="com.app.startup" />

</beans>
Cameron Hudson
  • 3,190
  • 1
  • 26
  • 38