Is there any Sp开发者_运维技巧ring 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.
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 theInitializingBean
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.
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
}
}
In Spring 4.2+ you can now simply do:
@Component
class StartupHousekeeper {
@EventListener(ContextRefreshedEvent.class)
public void contextRefreshedEvent() {
//do whatever
}
}
With SpringBoot, we can execute a method on startup via @EventListener
annotation
@Component
public class LoadDataOnStartUp
{
@EventListener(ApplicationReadyEvent.class)
public void loadData()
{
// do something
}
}
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();
}
}
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");
}
}
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>
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
AppStartListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ApplicationReadyEvent){
System.out.print("ciao");
}
}
}
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
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) {
}
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);
}
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>
精彩评论