Answers:
如果通过“应用程序启动”来表示“应用程序上下文启动”,那么可以,有很多方法可以做到这一点,最简单的方法(无论是对于Singletons bean还是)@PostConstruct
。看一下链接以查看其他选项,但概括而言,它们是:
@PostConstruct
afterPropertiesSet()
由InitializingBean
回调接口定义从技术上讲,它们是Bean生命周期的钩子,而不是上下文生命周期的钩子,但是在99%的情况下,两者是等效的。
如果您需要专门挂接到上下文启动/关闭中,则可以改为实现Lifecycle
接口,但这可能是不必要的。
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
@PostConstruct
调用了它的方法,但是它间接依赖的JMS基础结构尚未完全连接(并且在Spring中,所有操作都以失败告终)。切换到@EventListener(ApplicationReadyEvent.class)
一切正常后(ApplicationReadyEvent
Spring Boot专用于香草Spring,请参见Stefan的答案)。
使用可以轻松完成此操作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
}
}
ContextStartedEvent
,很难在事件触发之前添加侦听器。
在Spring 4.2+中,您现在可以简单地执行以下操作:
@Component
class StartupHousekeeper {
@EventListener(ContextRefreshedEvent.class)
public void contextRefreshedEvent() {
//do whatever
}
}
如果您正在使用spring-boot,这是最佳答案。
我觉得,@PostConstruct
以及其他各种生命周期的感叹是回旋的方式。这些可能直接导致运行时问题,或者由于意外的bean /上下文生命周期事件而导致的缺陷少于明显的缺陷。为什么不使用纯Java直接调用您的bean?您仍然以“春季方式”(例如:通过春季AoP代理)调用Bean。最棒的是,它是纯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();
}
}
main()
,例如使用应用程序框架(例如JavaServer Faces)时。
对于尝试引用@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");
}
}
我们所做的就是扩展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>
注意,仅当您的
runOnceOnStartup
方法依赖于完全初始化的spring上下文时才建议这样做。例如:您想通过交易划分来调用dao
您也可以使用带有fixedDelay设置的很高的计划方法
@Scheduled(fixedDelay = Long.MAX_VALUE)
public void runOnceOnStartup() {
dosomething();
}
这具有将整个应用程序连接起来的优点(事务,Dao等)。
@PostConstruct
?
发布了另一个实现WebApplicationInitializer的解决方案,该实例在实例化任何spring bean之前就已被调用,以防有人使用
AppStartListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ApplicationReadyEvent){
System.out.print("ciao");
}
}
}
您可以@EventListener
在组件上使用,在启动服务器并初始化所有bean之后将调用该组件。
@EventListener
public void onApplicationEvent(ContextClosedEvent event) {
}
对于StartupHousekeeper.java
位于package中的文件com.app.startup
,
在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>