[Seasar-user:13148] Re: [HotDeploy] AnnotationのAnnotation
Koichi Kobayashi
[E-MAIL ADDRESS DELETED]
2008年 3月 4日 (火) 17:30:19 JST
小林 (koichik) です.
クラスローダのディープな世界へようこそ.
まず,Java ではクラスは VM 上に一つではなく,
同じクラスでもクラスローダごとに複数存在する
場合があります.
そして異なったクラスローダでロードされたクラスは,
たとえ同じ .class ファイルからロードされたもので
あっても異なったクラスとして扱われます.
例えば Foo というクラスがクラスローダ A と B に
ロードされた場合,Foo(A) と Foo(B) は異なった
クラスであり,そのインスタンスも別物です.
Foo(A) 型の変数に Foo(B) のインスタンスを代入
することもできません.
HOT deploy は,リクエストごとに異なるクラスローダを
使ってクラスをロードします.
しかし,一部のクラスはオリジナルのクラスローダに
ロードされます.
その場合に代入できない問題に対処するため,
オリジナルのクラスローダにロード済みのクラスは
HOT deploy 用のクラスローダにはロードしないように
しています.
が,しかし.
それだけでは回避できない状況もあります.
Date: Tue, 4 Mar 2008 09:42:06 +0900
From: "Kazuya Sugimoto" <[E-MAIL ADDRESS DELETED]>
To: [E-MAIL ADDRESS DELETED]
Subject: [Seasar-user:13144] [HotDeploy] AnnotationのAnnotation
> // HogeAnnoが付いているアノテーションでもnullになってしまいます
> Annotation annoAnno =
> annotation.annotationType().getAnnotation(HogeAnno.class);
> }
>
> また、isAnnotationPresentでもtrueが返ってくるはずがfalseになりました。
> if(annotation.annotationType().isAnnotationPresent(HogeAnno.class)) {
> ・・
> }
HogeAnno.class というリテラルを使っていますが,
この場合の Class オブジェクト (Class クラスの
インスタンス) は,このコードを含むクラスと
同じクラスローダに読み込まれた HogeAnno クラスを
表現します.
おそらく,このコードを含むクラスは
HotdeployClassLoader ではなく,本来のクラスローダ
(以下 Tomcat に合わせて WebappClassLoader) に
ロードされています.
そのため,HogeAnno.class もまた WebAppClassLoader に
ロードされいるクラスを表しています.
しかし.
HobeAnno が WebAppClassLoader にロードされる
タイミングは必要になるまで遅延される場合があります.
おそらく,最初にこのコードを実行されるまでは,
WebappClassLoader には HogeAnno はロードされません.
それよりも前に,HogeAnno が付けられたオブジェクトが
このコードに渡されます.ここに注意.
つまり,アノテーションの付けられているクラス
がロードされる時点では,HogeAnno は
WebappClassLoader にはロードされておらず,
HotdeployClassLoader にロードされていると
考えられるわけです.
そして上記コードが実行される際に HogeAnno が
WebappClassLoader にロードされますが,
getAnnotation() で取得できる HogeAnno は
HotdeployClassLoader にロードされたものであり,
上記コードが使用する WebappClassLoader に
ロードされた HogeAnno とは異なったクラスとして
扱われてしまいます.
そんなわけで (どんなわけで?),
getAnnotation(HogeAnno.class) は null を返し,
isAnnotationPresent(HogeAnno.class) は false を
返すことになります.
ぜぇぜぇ.
> getAnnotationsで配列で取得してみると以下の3つが確認できました。
> java.lang.annotation.Target
> java.lang.annotation.Retention
> HogeAnno(クラス名を見ただけで代入や比較では確認してません)
ここで取得した HogeAnno のインスタンスに対して
getClass().getClassLoader() をすると
HotdeployClassLoader が返ってくるはずです.
# 初回のみ.
一方,HogeAnno.class.getClassLoader() では
WebappClassLoader が返ってくるはずです.
HOT deploy でも,最初のリクエストを処理している
途中で HogeAnno が WebappClassLoader にロード
されるため,2 回目以降のリクエストでは
HotdeployClassLoader にロードされることはなく,
うまく動くことになります.
対策ですが,HogeAnno が常に HotdeployClassLoader で
扱われるようにするか,常に WebappClassLoader で
扱われるようにするかのどちらかになります.
常に HotdeployClassLoader で扱われるようにするには,
上記コードも Hotdeploy 対象のクラスで行うように
する必要があります.<root>.util.XxxUtil クラスなどで
あれば HOT deploy 対象となります.
常に WebAppClassLoader で扱われるようにするには,
HogeAnno をルートパッケージ以外に移動するか,
convention.dicon で NamingConvention に
<initMethod name="addIgnorePackageName">
<arg>"HogeAnnoのパッケージ"</arg>
</initMethod>
とする必要があります.
--
<component name="koichik">
<property name="fullName">"Koichi Kobayashi"</property>
<property name="email">"[E-MAIL ADDRESS DELETED]"</property>
<property name="blog">"http://d.hatena.ne.jp/koichik"</property>
</component>
Seasar-user メーリングリストの案内