Springの起動時にメソッドを実行する


176

アプリケーションの初回起動時にいくつかのメソッドを実行するSpring 3の機能はありますか?@Scheduledアノテーションを使用してメソッドを設定するトリックを実行でき、起動直後に実行されますが、その後は定期的に実行されます。


1
@Scheduledのトリックは何ですか?それがまさに私が欲しいものです!
chrismarx

回答:


185

「アプリケーションの起動」が「アプリケーションコンテキストの起動」を意味する場合、そうです。これを行う方法たくさんあります。最も簡単な方法は(とにかくシングルトンBeanの場合)、でメソッドに注釈を付けることです@PostConstruct。リンクを見て他のオプションを確認しますが、要約すると次のとおりです。

  • アノテーションが付けられたメソッド @PostConstruct
  • afterPropertiesSet()InitializingBeanコールバックインターフェイスで定義されているとおり
  • カスタム構成されたinit()メソッド

技術的には、これらはコンテキストライフサイクルではなくBeanライフサイクルへのフックですが、99%の場合、2つは同等です。

特にコンテキストの起動/シャットダウンにフックする必要がある場合はLifecycle代わりにインターフェース実装できますが、それはおそらく不要です。


7
かなりの調査の結果、LifecycleまたはSmartLifecycleの実装をまだ見ていません。私はこれが1歳であることを知っています、しかしあなたが投稿できる何かがあればskaffmanは大歓迎です。

4
上記のメソッドは、アプリケーションコンテキスト全体が作成される前に呼び出されます(例:/ before /トランザクション境界が設定されている)。
Hans Westerbeek、2015年

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
encrest

2
Beanコンテキストのライフサイクルが大きく異なる重要なケースがあります。@HansWesterbeekが述べたように、依存するコンテキストが完全に準備される前にBeanをセットアップできます。私の状況では、BeanはJMSに依存していました-完全に構築されていたため、その@PostConstructメソッドが呼び出されましたが、間接的に依存しているJMSインフラストラクチャはまだ完全に接続されていません(そして、すべてが静かに失敗しただけです)。機能する@EventListener(ApplicationReadyEvent.class)すべてのものに切り替える際に(ApplicationReadyEventバニラスプリングに固有のスプリングブートについては、Stefanの回答を参照してください)。
ジョージホーキンス

@Skaffman:自分のBeanがどのBeanからも参照されておらず、どこにも使用されずにBeanを初期化したい場合
Sagar Kharab

104

これはで簡単に行えApplicationListenerます。私はこれを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 
  }
}

アプリケーションリスナーは、Springで同期的に実行されます。コードが1度だけ実行されるようにしたい場合は、コンポーネントの状態を維持してください。

更新

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
これは私にとっても機能しました。1回限りのBean以外の初期化に最適です。
ロリーハンター

9
ContextStartedEvent代わりに使用したい人のために注意してください、イベントが発生する前にリスナーを追加することは困難です。
OrangeDog 2016

2
@Autowired JPAレポジトリをイベントに呼び出す方法は?リポジトリがnullです。
e-info128 2016年

私のために働いていません。Spring mvc 3を使用しています。このメソッドonApplicationEvent(___)は、アプリケーションの起動時に呼び出されません。助けて。これが私のコードです@Component public class AppStartListener implements ApplicationListener <ContextRefreshedEvent> {public void onApplicationEvent(final ContextRefreshedEvent event){System.out.println( "\ n \ n \ nInside on application event"); }}
Vishwas Tyagi 2017年

@VishwasTyagiコンテナをどのように開始しますか?AppStartListenerはコンポーネントスキャンの一部ですか?
Stefan Haberl 2017年

38

Spring 4.2以降では、次の操作を簡単に実行できます。

@Component
class StartupHousekeeper {

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

このリスナーが起動後に1回だけ呼び出されることが保証されていますか?
gstackoverflow

いいえ、上記の私の回答を参照してください。リスナーが何らかの状態を維持して、それが初めて実行されているかどうかを確認します
Stefan Haberl

13

スプリングブートを使用している場合、これが最良の答えです。

@PostConstructと他のさまざまなライフサイクル間投詞はラウンドアラウンド方法だと思います。これらは、ランタイムの問題に直接つながるか、予期しないBean /コンテキストライフサイクルイベントが原因で、明らかではない欠陥を引き起こす可能性があります。単純なJavaを使用してBeanを直接呼び出すだけではどうですか。それでも、Beanを「春の方法」で呼び出します(例:春の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();
    }
}

5
これは一般的には良いアイデアですが、統合テストからSpringアプリケーションのコンテキストを起動すると、mainは実行されません!
Jonas Geiregat、2015

@JonasGeiregat:さらに、main()アプリケーションフレームワーク(JavaServer Facesなど)を使用する場合など、まったくないシナリオもあります。
sleske 2016年

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タスクの名前空間を使用して、タスクを1回実行するようスケジュールする


を使用するよりも利点はありません@PostConstructか?
Wim Deblauwe 2016

@WimDeblauweは、dosomething()で何をしたいかによって異なります。Trasaction境界でAutowired daoを呼び出すには、このBeanだけでなく、コンテキスト全体を起動する必要があります
Joram

5
@WimDeblauwe '@PostConstruct'メソッドは、Beanが初期化されると起動します。コンテキスト全体が準備できていない可能性があります(トランザクション管理)
Joram

これは芋ポスト構築物または任意のインタフェースやイベントよりもエレガントです
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パッケージにあるファイルの場合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>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.