スコープの短いコンポーネントの取得

暇つぶしネタ。


S2 などの DI コンテナを使っていると、sintleton コンポーネントから prototype などの、よりスコープの短いコンポーネントを取得したいケースがあります。
この場合、単純に DI するだけではうまくいきません。

public interface BarService {
    void execute();
}

public class FooServiceImpl implements FooService {
    private BarService service; // prototype-scope
    public void setBarService(BarService service) {
        this.service = service
    }
    
    public void fooMethod() {
        service.execute();
    }
}

上記のように FooService を DI してしまうと、BarService はコンテナ起動時に1回だけ初期化されるため、そのときに FooService の DI が行われてしまい、毎回新しい service インスタンスがほしい場合にはうまくいきません。
この問題は通常、 S2Container からコンポーネントを直接取ることで解決します。

public class FooServiceImpl implements FooService {
    public void fooMethod() {
        FooService service = (FooService) SingletonS2ContainerFactory.getContainer().getComponent(BarServiceImpl.class);
        service.execute();
    }
}

prototype スコープであれば、getComponent するたびにコンポーネントが生成されますので、これで問題は解決です。
・・・が、少し記述が長ったらしくなってしまうので、ユーティリティメソッドを作ってみました。

public class Components {
    @SuppressWarnings("unchecked")
    public static <T> T get(Class<? extends T> clazz) {
        return (T) SingletonS2ContainerFactory.getContainer().getComponent(clazz);
    }
}

public class FooServiceImpl implements FooService {
    void fooMethod() {
        BarService service = Components.get(BarServiceImpl.class);
        service.execute();
    }
}

だいぶすっきりしました。
なお Eclipse ecjではコンパイルが通り実行できましたが、javac でコンパイルが通るかは未確認です。