在Spring中启动时执行方法

当应用程序第一次启动时,Spring 3是否有执行某些方法的功能?我知道我可以用@Scheduled注释来设置一个方法,它会在启动后立即执行,但之后它会周期性地执行。

264557 次浏览

如果您所说的“应用程序启动”是指“应用程序上下文启动”,那么有有很多方法可以做到这一点。,最简单的(至少对于单例bean)是用@PostConstruct来注释您的方法。查看链接以了解其他选项,但总的来说,它们是:

  • 方法采用@PostConstruct
  • _由InitializingBean回叫接口定义的ABC_0
  • 自定义配置的init()方法

从技术上讲,它们是豆子生命周期的挂钩,而不是上下文生命周期的挂钩,但在99%的情况下,两者是等效的。

如果您需要明确地挂钩到上下文startup/shutdown,那么您可以实现Lifecycle接口,但这可能是不必要的。

这很容易通过ApplicationListener来实现。我通过收听Spring的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
}
}

应用程序侦听器在Spring中同步运行。如果你想确保你的代码只执行一次,只需在你的组件中保留一些状态。

更新

从Spring 4.2+开始,您还可以使用@EventListener注释来观察ContextRefreshedEvent(感谢@bphilipnyc指出这一点):

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
}
}

我们所做的是扩展org.springframework.web.context.ContextLoaderListener,以便在上下文启动时打印一些内容。

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


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

配置子类,然后在web.xml中:

<listener>
<listener-class>
com.mycomp.myapp.web.context.ContextLoaderListener
</listener-class>
</listener>

发布了另一个实现WebApplicationInitializer的解决方案,并在实例化任何Spring bean之前调用,以防有人有该用例。

使用Spring配置初始化默认语言环境和时区

对于在尝试引用@PostConstruct注释时收到警告的Java 1.8用户,我最终放弃了@Scheduled注释,如果您已经有一个具有FixedRate或FixedDelay的@Scheduled作业,则可以这样做。

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");
}


}

如果您使用的是Spring-Boot,这是最好的答案。

我觉得@PostConstruct和其他各种生命周期感叹词都是迂回的方式。这些可能会直接导致运行时问题,或者由于意外的bean/上下文生命周期事件而导致不太明显的缺陷。为什么不使用纯Java直接调用bean呢?您仍然以“ Spring方式”调用bean(例如:通过Spring AOP代理)。最棒的是,它是纯Java,没有比这更简单的了。不需要上下文侦听器或奇怪的调度程序。

@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();
}
}

如果要在应用程序完全运行之前配置Bean,可以使用@Autowired

@Autowired
private void configureBean(MyBean: bean) {
bean.setConfiguration(myConfiguration);
}

请注意,仅当您的runOnceOnStartup方法取决于 完全初始化的Spring上下文。例如:你想叫一把刀 有了事务界定

您还可以使用FixedDelay设置非常高的计划方法

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

这样做的好处是可以连接整个应用程序(事务、Dao等)

使用Spring任务名称空间将任务调度为运行一次中可见

AppStartListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ApplicationReadyEvent){
System.out.print("ciao");


}
}
}

在Spring 4.2+中,您现在可以简单地执行以下操作:

@Component
class StartupHousekeeper {


@EventListener(ContextRefreshedEvent.class)
public void contextRefreshedEvent() {
//do whatever
}
}

您可以在组件上使用@EventListener,它将在启动服务器并初始化所有Bean后被调用。

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {


}

对于位于包com.app.startup中的文件StartupHousekeeper.java

StartupHousekeeper.java中执行此操作:

@Component
public class StartupHousekeeper {


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

并在myDispatcher-servlet.java

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


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


</beans>

使用SpringBoot,我们可以在启动时通过@EventListener注释来执行方法

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