在Spring启动时执行方法


176

首次启动应用程序时,是否有任何Spring 3功能可以执行某些方法?我知道我可以做一些技巧来设置带有@Scheduled注释的方法,并且该方法会在启动后立即执行,但随后会定期执行。


1
@Scheduled的诀窍是什么?那正是我想要的!
chrismarx 2015年

Answers:


185

如果通过“应用程序启动”来表示“应用程序上下文启动”,那么可以,有很多方法可以做到这一点,最简单的方法(无论是对于Singletons bean还是)@PostConstruct。看一下链接以查看其他选项,但概括而言,它们是:

  • 用注释的方法 @PostConstruct
  • afterPropertiesSet()InitializingBean回调接口定义
  • 定制配置的init()方法

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

如果您需要专门挂接到上下文启动/关闭中,则可以改为实现Lifecycle接口,但这可能是不必要的。


7
经过大量研究,我还没有看到Lifecycle或SmartLifecycle的实现。我知道这已经一岁了,但是如果您有任何可以发表的东西,请向skaffman致谢。

4
在创建整个应用程序上下文之前(例如/ before /事务划分已设置),将调用上述方法。
汉斯·韦斯特贝克

我在Java 1.8中尝试使用@PostConstruct时收到奇怪的警告: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
占用

2
在重要的情况下,bean上下文的生命周期非常不同。正如@HansWesterbeek所指​​出的,可以在它所依赖的上下文完全准备好之前设置一个bean。在我的情况下,Bean依赖于JMS-它是完全构建的,因此@PostConstruct调用了它的方法,但是它间接依赖的JMS基础结构尚未完全连接(并且在Spring中,所有操作都以失败告终)。切换到@EventListener(ApplicationReadyEvent.class)一切正常后(ApplicationReadyEventSpring Boot专用于香草Spring,请参见Stefan的答案)。
乔治·霍金斯

@Skaffman:如果我的bean没有被任何bean引用,并且我想初始化bean而不用在任何地方
怎么办

104

使用可以轻松完成此操作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 
  }
}

1
这对我也起作用-非常适合一次性非bean初始化。
罗里·亨特

9
注意:对于那些倾向于使用的人ContextStartedEvent,很难在事件触发之前添加侦听器。
OrangeDog

2
如何将@Autowired JPA存储库称为事件?存储库为空。
e-info128 '16

不为我工作。我正在使用spring mvc3。启动应用程序时不会调用此方法onApplicationEvent(___)。任何帮助。这是我的代码@Component公共类AppStartListener实现了ApplicationListener <ContextRefreshedEvent> {public void onApplicationEvent(最终ContextRefreshedEvent事件){System.out.println(“ \ n \ n \ nInside on application event”); }
Vishwas Tyagi

@VishwasTyagi如何启动容器?您确定AppStartListener是组件扫描的一部分吗?
Stefan Haberl

38

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

@Component
class StartupHousekeeper {

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

是否保证此侦听器在启动后仅调用一次?
gstackoverflow

不,请参阅上面的答案。在您的侦听器中保留一些状态以检查它是否是第一次执行
Stefan Haberl

13

如果您正在使用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();
    }
}

5
通常,这是一个好主意,但是从集成测试启动spring应用程序上下文时,main永远不会运行!
乔纳斯·吉雷加特

@JonasGeiregat:另外,在其他情况下根本没有main(),例如使用应用程序框架(例如JavaServer Faces)时。
sleske '16

9

对于尝试引用@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");
    }

}


7

我们所做的就是扩展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>

7

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

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

4

注意,仅当您的runOnceOnStartup方法依赖于完全初始化的spring上下文时才建议这样做。例如:您想通过交易划分来调用dao

您也可以使用带有fixedDelay设置的很高的计划方法

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

这具有将整个应用程序连接起来的优点(事务,Dao等)。

看到计划任务运行一次,使用Spring任务命名空间


我看不出有什么优势@PostConstruct
Wim Deblauwe,

@WimDeblauwe取决于您要在dosomething()中执行的操作调用具有Trasaction分界的Autowired dao需要启动整个上下文,而不仅仅是这个bean
Joram

5
@WimDeblauwe'@PostConstruct'方法在初始化bean时触发,整个上下文可能未准备好(fe事务管理)
Joram

这比后构造或任何接口或事件更优雅的imo
aliopi


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

        }
    }
}

2
ApplicationReadyEvent在春季启动中而不是春季3中
John Mercier

0

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

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

0

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

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}

0

对于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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.