2016年1月30日

Spring @Autowired フィールドを持つクラスを手動で生成

初めて Spring を触って、@Autowired の便利さに感動したので・・・

前置き

@Autowired について簡単に説明すると、自動で初期化されるフィールドの目印。
(フィールドだけでなくセッターメソッドに対しても使用可能)
つまり、コンストラクタやフィールドの右辺に、フィールドの初期化処理を書かなくてよくなる。
古い Spring では初期化設定を xml に書く必要があったが、今はアノテーション(C# での カスタム属性)のみで完結できるようになっている。

[@Autowired サンプル]

@Controller
public class HogeController {

    @Autowired
    private HogeService service;

    // 以降、URL 割り当てメソッド
}

@Service
public class HogeService {

    @Autowired
    private HogeDao dao;

    // 以降、サービスメソッド
}

@Component
public HogeDao {}
HogeController にマップされた URL アクセス時、HogeController インスタンスがフレームワークによって生成され、@Autowired フィールドの service にも HogeService インスタンスが割り当てられる。HogeService の dao フィールドも @Autowired なので同様。
DI 目的なら HogeService と HogeDao のインターフェースを用意した方がいいけど、ここでは省略。
@Autowired 可能なクラスは、@Component 派生アノテーション付きで、@ComponentScan 対象である必要がある。(もしくは xml に記述したクラス)

@Autowired 持ちクラスを手動で生成

上記サンプルのような使い方だと、@Autowired 持ちクラスが初期化されるためには、@Autowired フィールドとして生成されるか、@Controller のようにフレームワークから自動生成される必要がある。
つまり、フィールドとしてしか作れない。(@Controller もフレームワークのフィールドみたいなもの)

@Autowired 持ちクラスをフィールドじゃなく、ローカル変数として欲しい(コードで手動生成したい)ケースが稀にあると思う。
java - In Spring, can I autowire new beans from inside an autowired bean? - Stack Overflow の createAutowiredObject で簡単に生成できる。
@Component 付クラスである必要もない。

[createAutowiredObject 使用サンプル]

@Controller
public class HogeController {

    @Autowired
    private AutowireCapableBeanFactory factory;

    private <T> T createAutowiredObject(Class<T> c){
        return factory.createBean(c);
    }

    public void createAutowiredTest(){
        HogeComponent component = createAutowiredObject(HogeComponent.class);
        // HogeComponent のフィールドが初期化されていることを確認
        System.out.println(component.sub);
    }
}

public HogeComponent {
    @Autowired
    public HogeComponentSub sub;
}

@Component
public HogeComponentSub {}

使いどころ

@Component 付きクラスは、デフォルトでシングルトンである。
@Scope("prototype") を付けることで非シングルトンにすることも可能だが、親クラスも同じ @Scope を設定してやる必要がある。(シングルトンのフィールドは同じくシングルトンになる)

親クラスがシングルトンで、その内部で非シングルトンの @Autowired 持ちクラスが欲しい場合、createAutowiredObject のようなメソッドがあるといいかもしれない。
親クラスの @Scope を変えても可能だが、変えられないケースもあると思うので。

おまけ

@Scope の効果を Spring MVC で視覚的に確認できるもの

0 件のコメント:

コメントを投稿