
SpringでコレクションをDIする方法
少しはまった箇所があるので備忘録がてら。
サンプルのアプリケーション。
@SpringBootApplication
@RestController
public class DemoApplication {
private GreetingService service;
@Autowired
public DemoApplication(GreetingService service) {
this.service = service;
}
@RequestMapping("/")
String index() {
String greetings = service.getGreetings().stream().collect(Collectors.joining(","));
return greetings;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
DIした GreetingService
を利用して挨拶を表示する。
GreetingService
は以下。
@Component
public class GreetingService {
private List<Greeting> greetings;
@Autowired
public GreetingService(List<Greeting> greetings) {
this.greetings = greetings;
}
public List<String> getGreetings() {
return greetings.stream().map(it -> it.hello()).collect(Collectors.toList());
}
}
コンストラクタ・インジェクションでDIした Greeting
のリストを回して、挨拶文を取得している。
Greeting
は以下。
public interface Greeting {
String hello();
@Component
static class EnglishGreeting implements Greeting {
@Override
public String hello() {
return "Hello";
}
}
@Component
static class JapaneseGreeting implements Greeting {
@Override
public String hello() {
return "こんにちは";
}
}
}
EnglishGreeting
と JapaneseGreeting
の2つの実装クラスを作成し、 @Component
によりコンポーネントを登録している。この2つのコンポーネントインスタンスが先ほどの GreetingService
にDIされるため、画面に表示されるメッセージは「Hello,こんにちは」のようになる。
このように、List
などのコレクション型を使った複数コンポーネントのDIは簡単に行うことができる。
ここで、 Greeting
を以下のように一部コメントアウトして、コンポーネントが1件も見つからない状態を作る。
public interface Greeting {
String hello();
//@Component
static class EnglishGreeting implements Greeting {
@Override
public String hello() {
return "Hello";
}
}
//@Component
static class JapaneseGreeting implements Greeting {
@Override
public String hello() {
return "こんにちは";
}
}
}
するとどうなるか?
Spring 5系では(確認したのはver 5.2.9)特に問題は起こらない。 GreetingService
のコンストラクタには空のリストが渡される。
Spring 4系だと、Boot起動時に UnsatisfiedDependencyException
が発生してしまう。
以下は、Spring 4系での対処方法を記す。
ググると、以下のように @Autowired
アノテーションの属性に require = false
を指定すればOKというのが見つかる。
@Autowired(required = false)
public GreetingService(List<Greeting> greetings) {
this.greetings = greetings;
}
たしかに GreetingService
コンポーネントに関しては上記で例外発生を回避できるのだが、それを参照する DemoApplication
側で UnsatisfiedDependencyException
が発生してしまう結果となった。
以下のように、 List
を Optional
で包んで受け取ることで例外が発生せず動作するようにできた。
@Autowired
public GreetingService(Optional<List<Greeting>> greetings) {
this.greetings = greetings.orElseGet(ArrayList::new);
}
おわり。