1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

taglibでAutowiredしたかった

Last updated at Posted at 2014-12-26

2015年1月29日
本件に関して本文を修正しました。

以前の内容はそのまま下部に残してありますが、
役に立ちませんのであしからず。

ざっくり言うと


Spring管理のクラスをtaglibでインジェクションしたい時はRequestContextAwareTagをextendsしようね!って話

前提


  1. strutsspringが混在するプロジェクト
  2. applicationContext.xmlを2つ有する
  3. 一つはstruts-config.xmlを介するコンテキスト
  4. もう一つはstruts-config.xmlに寄らないコンテキスト
  5. 以下では3applicationContext.xml4applicationContextForSpring.xmlとして扱う
  6. 新規でサービスクラス(HogeService.java)を作成し、applicationContextForSpring.xmlに記載した

目的


taglibHogeServiceクラスをインジェクションすること

試行1


手段:おとなしく@Autowiredを書いてみる
結果:インジェクション出来ずNullPointerExceptionが発生`[1]

試行2


手段:taglibTagSupportではなくRequestContextAwareTagをextendsし、以下のようにして取得

public class HogeTag extends RequestContextAwareTag {
    HogeService hogeService = getRequestContext().getWebApplicationContext().getBean(HogeService.class);
    
}

結果:取得できずNullPointerException

手がかり


  1. getBeanDefinitionNames()でbean一覧を出力してみるとapplicationContext.xml管理のものが取得できている様子
  2. 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ねーよ!ってなった
予想は合っている様子。

解決策


  1. HogeServiceapplicationContext.xmlで管理する
  2. 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.xmlcontext: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つならこんなに悩んだり変なことする必要は無いんだが。。。
先人の考えはわからんのでとりあえずこんな感じの解決で。

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?