アノテーションベース Spring DIの復習
はじめに
普段はSpring Bootを利用して開発している DI周りは困ったら調べるくらいでしかないので、ちょっと復習しようと思う
学習内容
この辺に触れていく
@Component @Autowire @Configuration @Bean AnnotationConfigApplicationContext
環境
java周り
Apache Maven 3.9.3 (21122926829f1ead511c958d89bd2f672198ae9f) Maven home: C:\Program Files\apache-maven-3.9.3 Java version: 17.0.2, vendor: Oracle Corporation, runtime: C:\Program Files\jdk-17.0.2 Default locale: ja_JP, platform encoding: MS932 OS name: "windows 11", version: "10.0", arch: "amd64", family: "windows"
pom
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
RestContollerを使って動作確認をしていく
やっていく
DIとは?
依存性の注入という単語で色々な方が解説されているので、割愛します。 公式の文章が気になったので引用します*1
この章では、制御の反転(IoC)原則の Spring Framework 実装について説明します。IoC は、依存性注入(DI)とも呼ばれます。これは、コンストラクター引数、ファクトリメソッドへの引数、ファクトリメソッドが構築または返された後にオブジェクトインスタンスに設定されるプロパティを通じてのみ、オブジェクトが依存関係(つまり、操作する他のオブジェクト)を定義するプロセスです。コンテナーは、Bean を作成するときにそれらの依存関係を注入します。このプロセスは、基本的に、クラスの直接構築または Service Locator パターンなどのメカニズムを使用して、Bean 自身がその依存関係のインスタンス化や場所を制御することの逆(そのため、Inversion of Control という名前)です。 めちゃめちゃ分かりづらいですが、IoC≒DIということ また、違いは次のブログのまとめがわかりやすかったです *2 ※IoCという概念を公式のドキュメントを読んで初めて知りました
アノテーションベースで実装してみる
model
@Data public class Employee { @NonNull private final String name; @NonNull private final String id; }
Controller
@RestController public class EmployeeController { // @Autowired private final EmployeeServiceInterface employeeService; // コンストラクタインジェクション EmployeeController(EmployeeServiceInterface employeeService){ this.employeeService = employeeService; } @RequestMapping("/getEmployee") public Employee getEmployee(){ return employeeService.get(); } }
Service
public interface EmployeeServiceInterface { public Employee get(); } @Service // Beanを登録する public class EmployeeServiceImpl implements EmployeeServiceInterface { @Override public Employee get() { return new Employee("Admin","1"); } }
curlでアクセス、こんな感じで取れる
$ curl http://localhost:8080/getEmployee Content : {"name":"Admin","id":"1"}
@Seriveをつけることで、該当のクラスがDIコンテナに登録される
(細かい話をすると@SpringBootApplicationが付与されたクラスと同じ階層か、配下のパッケージにDIしたいクラスが存在する必要がある)
本来インスタンス化したいクラス変数に@Autowiredを付与することでも、DIしたクラスの代入が可能となるが
公式によるとコンストラクタインジェクションが推奨されているので、このように実装した*3
ApplicationContextからBeanを取得する
新しくConfigクラスを追加する
@Configuration public class MyConfig { @Bean Employee getEmployee() { return new Employee("MrMyConfig", "2"); } }
あんまり良い例ではないけど、Controllerを修正してApplicationContextからBeanを取得する
public class EmployeeController { // @Autowired - private final EmployeeServiceInterface employeeService; +// private final EmployeeServiceInterface employeeService; // コンストラクタインジェクション - EmployeeController(EmployeeServiceInterface employeeService){ - this.employeeService = employeeService; - } +// EmployeeController(EmployeeServiceInterface employeeService){ +// this.employeeService = employeeService; +// } @RequestMapping("/getEmployee") public Employee getEmployee(){ - return employeeService.get(); +// return employeeService.get(); + ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); + return context.getBean(Employee.class); } }
生成した社員インスタンスをDIコンテナに格納、Applicationコンテキストからgetしてくる
本来のConfigクラスの利用方法として、自作コードではなく外部ライブラリの機能をDIしたい場合に利用する
例えば、thymeleafを利用したSpring MVCのアプリを作成している場合、以下のような利用方法になる
@Configuration public class JavaConfig { @Bean public ModelMapper modelMapper(){ return new ModelMapper(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
Configクラスでインスタンス化した社員オブジェクトが取得できることがわかる
$ curl http://localhost:8080/getEmployee Content : {"name":"MrMyConfig","id":"2"}
まとめ
なんか知っていることを改めて整理して書くことの難しさを感じた 出てくる単語や概念をすべて記載すると文章量が増えて分かりづらくなる 結果、誰向けに書いているのか分からなくなった(振り返りなので、これでいいかもしれないけど)
また、公式のドキュメントを見ていると色々な発見があったので、また備忘としてブログにまとめたい
引用
*1 Spring IoC コンテナーと Bean の導入 ::Spring Framework - リファレンス
*3 依存性注入 :: Spring Framework - リファレンス
コンストラクター vs setter、どちらの DI を選ぶ?