2015年1月29日
本件に関して本文を修正しました。
以前の内容はそのまま下部に残してありますが、
役に立ちませんのであしからず。
ざっくり言うと
Spring管理のクラスをtaglibでインジェクションしたい時はRequestContextAwareTag
をextendsしようね!って話
前提
-
struts
とspring
が混在するプロジェクト -
applicationContext.xml
を2つ有する - 一つは
struts-config.xml
を介するコンテキスト - もう一つは
struts-config.xml
に寄らないコンテキスト - 以下では
3
をapplicationContext.xml
、4
をapplicationContextForSpring.xml
として扱う - 新規でサービスクラス(HogeService.java)を作成し、
applicationContextForSpring.xml
に記載した
目的
taglib
でHogeService
クラスをインジェクションすること
試行1
手段:おとなしく@Autowired
を書いてみる
結果:インジェクション出来ずNullPointerException
が発生`[1]
試行2
手段:taglib
でTagSupport
ではなくRequestContextAwareTag
をextendsし、以下のようにして取得
public class HogeTag extends RequestContextAwareTag {
HogeService hogeService = getRequestContext().getWebApplicationContext().getBean(HogeService.class);
…
}
結果:取得できずNullPointerException
手がかり
-
getBeanDefinitionNames()
でbean一覧を出力してみるとapplicationContext.xml
管理のものが取得できている様子 -
taglib
の呼び出し元クラスはapplicationContext.xml
で管理されている
予想
- taglibを呼び出すviewが、ActionとControllerのどちらに属してるかによって取得できるコンテキストが変わるのではないか
-
getRequestContext().getWebApplicationContext()
で取得できるbeanが
Action
の場合applicationContext.xml
、
Controller
の場合はapplicationContextForSpring.xml
で管理しているものではないか
検証
- ControllerとActionそれぞれで呼び出し元クラスを準備
- 以下のテストtaglibを作成。
public class TestTag extends RequestContextAwareTag {
@Override
protected int doStartTagInternal() throws Exception {
Integer num = this.getRequestContext()
.getWebApplicationContext()
.getBean("hogeService", Integer.class);
System.out.println(num);
return SKIP_BODY;
}
}
- テンプレートから呼び出してみる
結果:
Controller
:hogeServiceがとれた
Action
:そんなbeanねーよ!ってなった
予想は合っている様子。
解決策
-
HogeService
をapplicationContext.xml
で管理する -
taglib
の呼び出し元クラスをapplicationContextForSpring.xml
で管理する
今回の場合、呼び出し元は既存のActionクラスだったので1
で解決しました。※ @making@githubさんにコメントでアドバイスいただいたとおりです。
まとめ
呼び出すbeanがどのコンテキストで管理されているかが問題なのではなく、taglib呼び出し元がAction・Controllerどちらなのかによって取得できるbean一覧が異なる。
Action → applicationContext.xml管理のbean
Controller → applicationContextForSpring.xml管理のbean
疑問
Action・Controller両方でtaglib呼び出したい!ってなったら両方のコンテキストで管理しないといけないのだろうか…。
--------------------------------- 以下、修正前の本文です ---------------------------------
------ 読めば何を勘違していたかわかると思いますが役には立ちません ------
やりたかったこと
カスタムタグでサービスクラスをインジェクションしたかった。
できなかった
とりあえずこんな感じで。
public class HogeTestTag extends TagSupport {
@Autowired
private HogeService hogeService;
…
}
TagSupportをextendsしてるクラスで黙って@Autowired
した。
出来なかった…。
( ´∀`)<ぬるぽって怒られてしまった…。
どうやら@Autowired
ではダメらしい。[1]
どうしたらいいんだぁぁぁぁー!!!ヽ(`Д´)ノウワァァァン!!
できるよん
結論、先の注釈に書いてあったんだがw
RequestContextAwareTagをextendsしてgetRequestContext().getWebApplicationContext()
でWebApplicationContextを取得して頑張ればいい。
つまりこういうこと。
public class HogeTag extends RequestContextAwareTag {
HogeService hogeService = getRequestContext().getWebApplicationContext().getBean(HogeService.class);
…
}
あっさり解決
かと思われたのだが。。。
上記のやり方で取得できるbeanはweb.xml
でこんな感じで書かれてるやつのapplicationContext.xml
でcontext:component-scan
的なことされてるやつだけだったのです。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
今回インジェクションしたかったのはweb.xml
でservletとして大元のapplicationContext.xmlとは別に読み込まれているcontextだったわけなのです。
なんでapplicationContext的なのが2つある作りなのか、その意味を知る者はいない…
おかげでgetBean(HogeService.class)
が0件という残念な結果に。。。
getBeanDefinitionCount()
でbeanの件数を確認してみたり、getBeanDefinitionNames()
で登録されてるbean一覧を出してみたり、そんなこんなで上記のやつらが読み込まれているとわかったわけなのです。
わかったけどこれじゃダメなんやー!(つд⊂)エーン
解決編
HogeService hogeService = new ClassPathXmlApplicationContext("applicationContext.xml").getBean(HogeService.class);
設定ファイルを指定してClassPathXmlApplicationContext
取ってやりませう。完
但し、これtaglib呼び出し毎にやると毎回スキャンされてしまい、更にはログが恐ろしいことになるので、staticで初回だけ読み込むとかなんとか工夫しよう。(一度失敗した身w)
そもそもcontextが1つならこんなに悩んだり変なことする必要は無いんだが。。。
先人の考えはわからんのでとりあえずこんな感じの解決で。