[SpringBoot]プロジェクトの開始時に指定されたコードを実行するいくつかの方法

Springは構成ファイルを動的にロード(更新)します

序文

Springアプリケーションサービスを開始するときは、事前にデータを読み込んで初期化作業を行う必要があります。例:一時ファイルの削除、キャッシュされた情報のクリア、構成ファイルの読み取り、データベース接続の取得。これらのタスクは、起動後に1回だけ実行する必要があります。

0. ApplicationListener

コンテナの更新が成功したということは、すべてのBeanの初期化が完了したことを意味します。コンテナが更新された後、Springはコンテナに実装されたすべてのApplicationListener<ContextRefreshedEvent>BeanonApplicationEventメソッドを呼び出し、アプリケーションは监听容器初始化完成事件この目標を達成できます。

@Component
@Slf4j
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        log.info("Increment counter");
        counter++;
    }
}

エラーが発生しやすいポイント
Webコンテナを使用する場合は特に注意が必要です。Webプロジェクト(Spring MVCなど)では、システムに2つのコンテナがあります。1つはroot application context独自のもので、もう1つはcontext(ルートアプリケーションコンテキストのサブコンテナとして)独自のものです。 )。上記の文言に従うと、が発生しonApplicationEvent方法被执行两次ます。この問題の解決策は次のとおりです。

@Component
@Slf4j
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        if (event.getApplicationContext().getParent() == null) {
    
    
            // root application context 没有parent
            LOG.info("Increment counter");
            counter++;
        }
    }
}

高レベルのゲームプレイ:カスタムイベントを使用すると、Springを使用して最小限のコストでオブザーバーモードを実装できます。

イベントをカスタマイズします。

@Data
public class NotifyEvent extends ApplicationEvent {
    
    
    private String email;
    private String content;
    public NotifyEvent(Object source) {
    
    
        super(source);
    }
    public NotifyEvent(Object source, String email, String content) {
    
    
        super(source);
        this.email = email;
        this.content = content;
    }
}

イベントリスナーを登録する

@Component
public class NotifyListener implements ApplicationListener<NotifyEvent> {
    
    
    @Override
    public void onApplicationEvent(NotifyEvent event) {
    
    
        System.out.println("邮件地址:" + event.getEmail());
        System.out.println("邮件内容:" + event.getContent());
    }
}

イベントを投稿する

@RunWith(SpringRunner.class)
@SpringBootTest
public class ListenerTest {
    
    
    @Autowired
    private WebApplicationContext webApplicationContext;

    @Test
    public void testListener() {
    
    
        NotifyEvent event = new NotifyEvent("object", "[email protected]", "This is the content");
        webApplicationContext.publishEvent(event);
    }
}

単体テストを実行すると、メールのアドレスと内容が印刷されていることがわかります。

1.ServletContextAwareインターフェース

ServletContextAwareインターフェースを実装して、setServletContextメソッドをオーバーライドします

  • インターフェイスを実装するクラスは、直接获取Web应用的ServletContext实例(Servler容器)
@Component
public class ServletContextAwareImpl implements ServletContextAware {
    
    
    /**
     * 在填充普通bean属性之后但在初始化之前调用
     * 类似于initializing Bean的afterpropertiesset或自定义init方法的回调
     *
     */
    @Override
    public void setServletContext(ServletContext servletContext) {
    
    
        System.out.println("===============ServletContextAwareImpl初始化================");
    }
}

2.ServletContextListenerインターフェース

ServletContextListenerインターフェースを実装して、contextInitializedメソッドをオーバーライドします

  • 监听 ServletContext 对象的生命周期つまり、Webアプリケーションのライフサイクルを監視できます。
  • サーブレットコンテナがWebアプリケーションを開始または停止ServletContextEvent 事件すると、次ServletContextListenerの2つの方法で生成されイベントがトリガーされます
    • contextInitialized(ServletContextEvent sce):このメソッドは、サーブレットコンテナがWebアプリケーションを起動するときに呼び出されます。このメソッドを呼び出した後、コンテナはフィルタを初期化し、Webアプリケーションの起動時に初期化する必要があるサーブレットを初期化します。
    • contextDestroyed(ServletContextEvent sce):このメソッドは、サーブレットコンテナがWebアプリケーションを終了するときに呼び出されます。このメソッドを呼び出す前に、コンテナーはすべてのサーブレットおよびフィルターフィルターを破棄します。
@Component
public class ServletContextListenerImpl implements ServletContextListener {
    
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
    
    
        //ServletContext servletContext = sce.getServletContext();
        System.out.println("===============ServletContextListenerImpl初始化(contextInitialized)================");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    
    
        System.out.println("===============ServletContextListenerImpl(contextDestroyed)================");
    }
}

3. @PostConstructアノテーション

  • @PostConstructによって変更されたメソッドは、サーバーがサーブレットをロードするときに実行されます在构造函数之后,init()方法之前运行
  • @PostConstructアノテーションを使用した初期化操作の順序はです。最快的前提はこれらの操作不依赖其它Bean的初始化完成です。
@Component
public class PostConstructInit {
    
    
    //静态代码块会在依赖注入后自动执行,并优先执行
    static{
    
    
        System.out.println("---static--");
    }
    /**
     *  @Postcontruct’在依赖注入完成后自动调用
     */
    @PostConstruct
    public static void haha(){
    
    
        System.out.println("===============@Postcontruct初始化(在依赖注入完成后自动调用)================");
    }
}

@PreDestroy
@PreDestroy変更されたメソッドは、サーバーがサーブレットをアンインストールするときに実行され、サーブレットのdestroy()メソッドと同様にサーバーによって1回だけ呼び出されます。@PreDestroyによって変更されたメソッドは、destroy()メソッドの後、サーブレットが完全にアンインストールされる前に実行されます。

4.CommandLineRunnerインターフェース

CommandLineRunnerインターフェイスを実装して、runメソッドをオーバーライドします

  • 受信できる字符串数组コマンドラインパラメータ
  • 构造SpringApplication实例完成之后调用
  • @Order()で、Order注解またはを使用Ordered接口して呼び出し順序を指定できます。值越小,优先级越高
  • ApplicationRunnerとCommandLineRunnerを同時に使用します。默认情况下ApplicationRunner比CommandLineRunner先执行
@Component
public class CommandLineRunnerImpl implements CommandLineRunner {
    
    
    /**
     * 用于指示bean包含在SpringApplication中时应运行的接口。可以在同一应用程序上下文中定义多个commandlinerunner bean,并且可以使用有序接口或@order注释对其进行排序。
     * 如果需要访问applicationArguments而不是原始字符串数组,请考虑使用applicationRunner。
     *
     */
    @Override
    public void run(String... args) throws Exception {
    
    
        System.out.println("===============CommandLineRunnerImpl初始化================");
    }
}

5.ApplicationRunnerインターフェース

ApplicationRunnerインターフェースを実装して、実行メソッドを書き換えます

  • ApplicationRunnerは使用していますApplicationArguments来获取参数的
  • 构造SpringApplication实例完成之后调用run()的时候调用
  • することができ、指定さによって@Order注釈または使用Ordered接口调用顺序@Order()值越小,优先级越高
  • ApplicationRunnerとCommandLineRunnerを同時に使用します。默认情况下ApplicationRunner比CommandLineRunner先执行
  • ApplicationRunnerとCommandLineRunnerの関数は同じですが、唯一の違いは主にパラメーターの処理に反映されます。ApplicationRunner 可以接收更多类型的参数
@Component
public class ApplicationRunnerImpl implements ApplicationRunner {
    
    
    /**
     * 用于指示bean包含在SpringApplication中时应运行的接口。可以定义多个applicationRunner Bean
     * 在同一应用程序上下文中,可以使用有序接口或@order注释对其进行排序。
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
    
    
        System.out.println("===============ApplicationRunnerImpl初始化================");
    }
}
  • args.getSourceArgs():コマンドラインですべてのパラメーターを取得します。
  • args.getNonOptionArgs():コマンドラインでキーレスパラメーターを取得します(CommandLineRunnerと同じ)。
  • args.getOptionNames():すべてのキー/値パラメーターのキーを取得します。
  • args.getOptionValues(key)):キーに応じたキー/値の形式でパラメーターの値を取得します。

6.InitializingBeanインターフェース

オブジェクトのすべてのプロパティが初期化された後、afterPropertiesSet()メソッドが呼び出されます

public class InitializingBeanImpl implements InitializingBean {
    
    
    @Override
    public void afterPropertiesSet() {
    
    
        System.out.println("===============InitializingBeanImpl初始化================");
    }
}

7.SmartLifecycleインターフェース

SmartLifecycleは初始化后、1つのロジックを実行できるだけでなく关闭前別のロジックを実行することもできます。また、このクラス名が示すように、複数のSmartLifecycleの実行順序を制御することもでき智能的生命周期管理接口ます。これは1つです。


import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;

/**
 * SmartLifecycle测试
 *
 * 
 * 
 */
@Component
public class TestSmartLifecycle implements SmartLifecycle {
    
    

    private boolean isRunning = false;

    /**
     * 1. 我们主要在该方法中启动任务或者其他异步服务,比如开启MQ接收消息<br/>
     * 2. 当上下文被刷新(所有对象已被实例化和初始化之后)时,将调用该方法,默认生命周期处理器将检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。
     * 如果为“true”,则该方法会被调用,而不是等待显式调用自己的start()方法。
     */
    @Override
    public void start() {
    
    
        System.out.println("start");

        // 执行完其他业务后,可以修改 isRunning = true
        isRunning = true;
    }

    /**
     * 如果项目中有多个实现接口SmartLifecycle的类,则这些类的start的执行顺序按getPhase方法返回值从小到大执行。默认为 0<br/>
     * 例如:1比2先执行,-1比0先执行。 stop方法的执行顺序则相反,getPhase返回值较大类的stop方法先被调用,小的后被调用。
     */
    @Override
    public int getPhase() {
    
    
        // 默认为0
        return 0;
    }

    /**
     * 根据该方法的返回值决定是否执行start方法。默认为 false<br/> 
     * 返回true时start方法会被自动执行,返回false则不会。
     */
    @Override
    public boolean isAutoStartup() {
    
    
        // 默认为false
        return true;
    }

    /**
     * 1. 只有该方法返回false时,start方法才会被执行。    默认返回 false <br/>
     * 2. 只有该方法返回true时,stop(Runnable callback)或stop()方法才会被执行。
     */
    @Override
    public boolean isRunning() {
    
    
        // 默认返回false
        return isRunning;
    }

    /**
     * SmartLifecycle子类的才有的方法,当isRunning方法返回true时,该方法才会被调用。
     */
    @Override
    public void stop(Runnable callback) {
    
    
        System.out.println("stop(Runnable)");

        // 如果你让isRunning返回true,需要执行stop这个方法,那么就不要忘记调用callback.run()。
        // 否则在你程序退出时,Spring的DefaultLifecycleProcessor会认为你这个TestSmartLifecycle没有stop完成,程序会一直卡着结束不了,等待一定时间(默认超时时间30秒)后才会自动结束。
        // PS:如果你想修改这个默认超时时间,可以按下面思路做,当然下面代码是springmvc配置文件形式的参考,在SpringBoot中自然不是配置xml来完成,这里只是提供一种思路。
        // <bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
        //      <!-- timeout value in milliseconds -->
        //      <property name="timeoutPerShutdownPhase" value="10000"/>
        // </bean>
        callback.run();

        isRunning = false;
    }

    /**
     * 接口Lifecycle的子类的方法,只有非SmartLifecycle的子类才会执行该方法。<br/>
     * 1. 该方法只对直接实现接口Lifecycle的类才起作用,对实现SmartLifecycle接口的类无效。<br/>
     * 2. 方法stop()和方法stop(Runnable callback)的区别只在于,后者是SmartLifecycle子类的专属。
     */
    @Override
    public void stop() {
    
    
        System.out.println("stop");

        isRunning = false;
    }

}
  • start():Beanが初期化された後、このメソッドが実行されます。
  • stop():コンテナーが閉じられた後、Springコンテナーは、現在のオブジェクトがSmartLifecycleを実装していることを検出し、stop(Runnable)を呼び出します。Lifecycleのみを実装している場合は、stop()を呼び出します。
  • isRunning():壊れたコンポーネントが実行されているかどうかを判断するために使用される現在の状態。
  • getPhase():複数のSmartLifecyclesのコールバックシーケンスを制御します。戻り値が小さいほど start()メソッドが早く実行され、stop()メソッドが遅く実行されます。
  • isAutoStartup():startメソッドを実行する前に、このメソッドの戻り値を確認します。falseが返された場合、startメソッドは実行されません。
  • stop(Runnable):コンテナーが閉じられた後、Springコンテナーは、現在のオブジェクトがSmartLifecycleを実装していることを検出し、stop(Runnable)を呼び出します。Lifecycleのみを実装している場合は、stop()を呼び出します。

8.Beanの初期化シーケンスを制御します

SpringコンテナがBeanをロードする順序を指定する場合は、実装org.springframework.core.Ordered接口またはを使用してそれを実現できますorg.springframework.core.annotation.Order注解

  • 順序値が小さいほど、初期化クラスが早く実行されます。

8.1.Orderedインターフェースを実装する

/**
 * Created by pangkunkun on 2017/9/3.
 */
@Component
public class MyApplicationRunner implements ApplicationRunner,Ordered{
    
    


    @Override
    public int getOrder(){
    
    
        return 1;//通过设置这里的数字来知道指定顺序
    }

    @Override
    public void run(ApplicationArguments var1) throws Exception{
    
    
        System.out.println("MyApplicationRunner1!");
    }
}

8.2。@ Orderアノテーションを使用する

@Component
@Order(value = 1)
public class MyApplicationRunner implements ApplicationRunner{
    
    

    @Override
    public void run(ApplicationArguments var1) throws Exception{
    
    
        System.out.println("MyApplicationRunner1!");
    }

オリジナルの初期化方法

Springの実装に依存しない場合は静态代码块类构造函数適切なロジック実現することができます。

  • Javaクラスの初期化の順序は順番静态变量 > 静态代码块 > 全局变量 > 初始化代码块 > 构造方法です。

春のイベントメカニズム
春のイベント監視メカニズム
春のイベント

おすすめ

転載: blog.csdn.net/qq877728715/article/details/110860306