ラベル Spring の投稿を表示しています。 すべての投稿を表示
ラベル Spring の投稿を表示しています。 すべての投稿を表示

2011年10月15日土曜日

@PostConstructと@PreDestroyの悩み

アノテーションの使い方を前回で書いたのはいいんだけども。

ちょっと気になったので試したら悩みができた。。。
@Autowiredとか@Resourceとかで設定した場合でprototype指定されていたら、
ApplicationContextを直接持ってないとこでdestroyって呼べなくね?みたいな。
ApplicationContext持ち回せばいいのかな。。。。

試しに以下のようなコードを書いたが確かにdestroyは呼ばれない。(前回試してるから当たり前なんだが)
クラスを2重にするのがめんどくさかったのでJUnitで。。。。

対象Bean
package com.zomu.t.example;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("testComponent")
@Scope("prototype")
public class TestComponent {

 @PostConstruct
 public void init() {
  System.out.println("init.");
 }

 public void outputMsg() {
  System.out.println("msg....");
 }

 @PreDestroy
 public void destroy() {
  System.out.println("destroy.");
 }

}


実行クラス
package com.zomu.t.example;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:ApplicationContext.xml" })
public class TestInitDestroy2 {

 @Resource(name = "testComponent")
 TestComponent tc = null;

 @Test
 public void main() {
  tc.outputMsg();
 }
}


んーどうすりゃいいんだ。
思いつくのはこれしかない。

実行クラス
package com.zomu.t.example;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:StoreManager-Common-ApplicationContext.xml" })
public class TestInitDestroy2 {

 @Resource(name = "testComponent")
 TestComponent tc = null;

 @Autowired
 ConfigurableApplicationContext appContext = null;

 @Test
 public void main() {
  tc.outputMsg();

  appContext.getBeanFactory().destroyBean("testComponent", tc);
 }
}
やってることは、前回と変わりはない。ApplicationContextと手を握り合って「バルス」って唱えただけ。

これだと当たり前にdestroyも呼ばれるんだが。
他にいい方法ってないの?

@PostConstructと@PreDestroy

Springの@PreDestroyと@PostConstructについて。
これはBean定義時代の「init-method」と「destroy-method」のアノテーション。

Scopeをprototypeで作成したBeanについて@PreDestroyはいつ呼ばれんだ?
って気になったので、サンプルを作成して確かめたんでメモる。

まずは、prototypeで作成しない場合の例

対象のComponent
package com.zomu.t.example;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Component;

@Component("testComponent")
public class TestComponent {

 @PostConstruct
 public void init() {
  System.out.println("init.");
 }

 public void outputMsg() {
  System.out.println("msg....");
 }

 @PreDestroy
 public void destroy() {
  System.out.println("destroy.");
 }

}


実行クラス
package com.zomu.t.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestInitDestroy {

 public static void main(String[] args) {

  ConfigurableApplicationContext appContext = new ClassPathXmlApplicationContext(
    new String[] { "ApplicationContext.xml" });

  TestComponent tc = (TestComponent) appContext.getBean("testComponent");

  tc.outputMsg();

  appContext.close();

 }
}


結果
init.
msg....
destroy.

おお。ちゃんと呼ばれてますね。
では、prototypeで作成した場合。

対象のComponent
package com.zomu.t.example;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("testComponent")
@Scope("prototype")
public class TestComponent {

 @PostConstruct
 public void init() {
  System.out.println("init.");
 }

 public void outputMsg() {
  System.out.println("msg....");
 }

 @PreDestroy
 public void destroy() {
  System.out.println("destroy.");
 }

}

さっきと違うのは、@Scopeでprototype指定しているだけ。
実行すると、、、

結果
init.
msg....

あれ、destroyが呼ばれない。
なんじゃこりゃ。
プロトタイプだから、もうBeanFactoryの知ったこっちゃねぇって事?
じゃー実行クラスで実験してみる。

package com.zomu.t.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestInitDestroy {

 public static void main(String[] args) {

  ConfigurableApplicationContext appContext = new ClassPathXmlApplicationContext(
    new String[] { "ApplicationContext.xml" });

  TestComponent tc = (TestComponent) appContext.getBean("testComponent");

  tc.outputMsg();

  appContext.getBeanFactory().destroyBean("testComponent", tc);

  appContext.close();

 }
}


結果
init.
msg....
destroy.

なるほど。
ちゃんとインスタンスと登録名さえ渡してくれりゃケツ拭きまっせ。ってことっぽい。
めんどいから拭いてくれよー。って思いました。