[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 メーリングリストの案内