19

Spring javadoc には、「ライフサイクル インターフェイスは最上位のシングルトン Bean でのみサポートされていることに注意してください」と記載されています。こちらのURL

LifecycleBeanTest.xmlはBeanを次のように説明しています。

<beans ...>
    <bean id="lifecycle" class="tests.LifecycleBean"/>
</beans>

十分に「トピッシュ」で「シングルトニッシュ」に見えます。

どういう意味ですか?Spring に私の Bean の実装Lifecycleを知らせて、それで何かをする方法は?

私の主な方法がSpringで次のように見えるとします

public static void main(String[] args) {
    new ClassPathXmlApplicationContext("/tests/LifecycleBeanTest.xml").close();
}

そのため、コンテキストをインスタンス化してからすぐに閉じます。

close()アプリケーションがすべての機能を実行するまで実行を遅らせる Bean を構成内に作成できますか? そのメイン メソッド スレッドは、アプリケーションの終了を待機しますか?

たとえば、次の Bean は思ったように動作しません。どちらも呼び出されませstart()ん。stop()

package tests;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.Lifecycle;

public class LifecycleBean implements Lifecycle {

    private static final Logger log = LoggerFactory.getLogger(LifecycleBean.class);

    private final Thread thread = new Thread("Lifecycle") {
        {
            setDaemon(false);
            setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    log.error("Abnormal thread termination", e);
                }
            });
        }

        public void run() {
            for(int i=0; i<10 && !isInterrupted(); ++i) {
                log.info("Hearbeat {}", i);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    return;
                }
            }
        };
    };


    @Override
    public void start() {
        log.info("Starting bean");
        thread.start();
    }

    @Override
    public void stop() {
        log.info("Stopping bean");
        thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }
    }

    @Override
    public boolean isRunning() {
        return thread.isAlive();
    }

}

更新 1

ビーン・イン・コードを待つことができることはわかっています。Spring 自体にフックするのは興味深いことです。

4

5 に答える 5

4

SmartLifecycleの代わりに使用する必要がありLifecycleます。前者だけがあなたが期待通りLifecycleに働いています。isRunning()実装でtrueを返すようにしてください。

私はSmartLifecycleそれが設計されているように聞こえる非同期ジョブに使用しました。私はそれがあなたのために働くと思いますが、同時にあなたはのApplicationListenerようなイベントを見るかもしれませんContextStoppedEvent

于 2012-11-25T18:08:21.470 に答える
4

メソッドを調べAbstractApplicationContext.doClose()て、Spring 開発者によってアプリケーション コンテキスト クローズの中断が提供されていないことを確認 できます。

protected void doClose() {
    boolean actuallyClose;
    synchronized (this.activeMonitor) {
        actuallyClose = this.active && !this.closed;
        this.closed = true;
    }

    if (actuallyClose) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }

        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }

        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        try {
            getLifecycleProcessor().onClose();
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
        }

        // Destroy all cached singletons in the context's BeanFactory.
        destroyBeans();

        // Close the state of this context itself.
        closeBeanFactory();

        // Let subclasses do some final clean-up if they wish...
        onClose();

        synchronized (this.activeMonitor) {
            this.active = false;
        }
    }
}

したがって、アプリケーション コンテキストが閉じるのを防ぐことはできません。

TestContext フレームワークでサービスをテストする

JUnit で Spring テスト コンテキスト フレームワークを使用している場合は、Lifecycle を実装するサービスをテストするために使用できると思います。内部 Spring テストの 1つからの手法を使用しました。

少し変更された LifecycleBean (waitForTermination()メソッドを追加しました):

public class LifecycleBean implements Lifecycle {

    private static final Logger log = LoggerFactory
            .getLogger(LifecycleBean.class);

    private final Thread thread = new Thread("Lifecycle") {
        {
            setDaemon(false);
            setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    log.error("Abnormal thread termination", e);
                }
            });
        }

        public void run() {
            for (int i = 0; i < 10 && !isInterrupted(); ++i) {
                log.info("Hearbeat {}", i);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    return;
                }
            }
        };
    };

    @Override
    public void start() {
        log.info("Starting bean");
        thread.start();
    }

    @Override
    public void stop() {
        log.info("Stopping bean");
        thread.interrupt();
        waitForTermination();
    }

    @Override
    public boolean isRunning() {
        return thread.isAlive();
    }

    public void waitForTermination() {
        try {
            thread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return;
        }
    }
}

テストクラス:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:Test-context.xml")
public class LifecycleBeanTest {

    @Autowired
    LifecycleBean bean;

    Lifecycle appContextLifeCycle;

    @Autowired
    public void setLifeCycle(ApplicationContext context){
        this.appContextLifeCycle = (Lifecycle)context;
    }

    @Test
    public void testLifeCycle(){
        //"start" application context
        appContextLifeCycle.start();

        bean.waitForTermination();
    }
}

Test-context.xml コンテンツ:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean class="LifecycleBean"/>
</beans>

PS コンテキストの開始と停止は、同じアプリケーション コンテキストで何度も実行したいことではない@DirtiesContextため、最良の結果を得るためにテスト メソッドに注釈を付ける必要がある場合があります。

質問の新しいバージョンへの回答

DefaultLifecycleProcessor はbeanFactory.getBeanNamesForType(Lifecycle.class, false, false);getBeanNamesForType javadoc から Lifecycle を実装する Bean のリストを取得するために使用します。

注: このメソッドは、最上位の Bean のみをイントロスペクトします。指定されたタイプに一致する可能性のあるネストされた Bean もチェックしませ ん。

したがって、このメソッドは内部 Bean をリストしません (xml 構成のみが使用可能であった場合、ネストされた Bean と呼ばれていました。ネストされたBean xml 要素として宣言されています)。

ドキュメントの次の例を検討してください

<bean id="outer" class="...">
  <!-- Instead of using a reference to target, just use an inner bean -->
  <property name="target">
    <bean class="com.mycompany.PersonImpl">
      <property name="name"><value>Tony</value></property>
      <property name="age"><value>51</value></property>
    </bean>
  </property>
</bean>

Start() および Stop() は、アプリケーション コンテキストによって伝達される単なるイベントであり、アプリケーション コンテキストの有効期間には関連付けられていません。たとえば、いくつかのサービス Bean を使用してダウンロード マネージャーを実装できます。 「停止」イベントをブロードキャストし、ユーザーが「開始」ボタンを押すと、「開始」イベントをブロードキャストして処理を再開できます。Spring は適切な順序でイベントをディスパッチするため、ここで使用できます。

于 2012-11-25T20:49:02.697 に答える
3

インターフェイスを使用Lifecycleしたことがなく、どのように機能するのかわかりません。start()しかし、単にコンテキストを呼び出すと、これらのコールバックが呼び出されるように見えます。

AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("...");
ctx.start();

ただし、通常は@PostConstruct/@PreDestroy注釈を使用するか、または実装しInitializingBeanますDisposableBean

public class LifecycleBean implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() {
        //...
    }

    @Override
    public void destroy() {
        //...
    }

}

close()アプリケーション コンテキストを呼び出していないことに注意してください。LifecycleBeanJVMで非デーモンスレッドを作成しているため、main終了しても実行されたままになります。

そのスレッドを停止すると、JVM は存在しますが、アプリケーション コンテキストを適切に閉じません。基本的に、最後の非デーモン スレッドが停止し、JVM 全体が終了します。これは少しハックな回避策です。バックグラウンドの非デーモン スレッドが終了しようとしているときは、アプリケーション コンテキストを明示的に閉じます。

public class LifecycleBean implements ApplicationContextAware /* ... */ {

    private AbstractApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = (AbstractApplicationContext)applicationContext;
    }

    public void run() {
        for(int i=0; i<10 && !isInterrupted(); ++i) {
            log.info("Hearbeat {}", i);
            try {
                sleep(1000);
            } catch (InterruptedException e) {
            }
        }
        applicationContext.close();
    }

}
于 2012-11-25T13:57:59.390 に答える
0

それで、最終的に私は次のことを発見しました:

1)私のBeanを次のように定義しますimplements Lifecycle

stop()2)このようなメソッドに遅延を導入します

@Override
public void stop() {
    log.info("Stopping bean");
    //thread.interrupt();
    try {
        thread.join();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return;
    }
}

3) そして、次のようにコード コンテキストを作成します。

new ClassPathXmlApplicationContext("/tests/LifecycleBeanTest.xml").stop();

それから私は私が欲しいものを手に入れます:

コンテキスト作成コードは、すべての Lifecycle Bean のすべての停止が実行されるまで終了しません。したがって、このコードは JUnit テストで機能します

于 2012-11-26T12:03:00.270 に答える