簡単な例から始めましょう。初期化時にCommandLineRunner
クラスを実行するSpringブートアプリケーションがあります。
// MyCommandLineRunner.Java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
@Autowired //IntelliJ Warning
private DataSource ds;
@Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
// Application.Java
@SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
@Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner();
}
}
今、このように、これはうまくいき、すべてがOKです。ただし、IntelliJは@Autowired
がある場所に警告を報告します(コメントのどこにマークを付けたか)
Springチームの推奨事項: Beanでは常にコンストラクターベースの依存性注入を使用します。必須の依存関係には常にアサーションを使用します。
これに従うと、依存関係の注入に基づくコンストラクターができます
@Autowired
public MyCommandLineRunner(DataSource ds) { ... }
これは、コンストラクタが引数を必要とするため、Application.Java
も編集する必要があることも意味します。 Application.Java
でセッターインジェクションを使用しようとすると、同じ警告が表示されます。私もそれをリファクタリングすると、私の意見では、厄介なコードができてしまいます。
// MyCommandLineRunner.Java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private DataSource ds;
@Autowired // Note that this line is practically useless now, since we're getting this value as a parameter from Application.Java anyway.
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
@Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
// Application.Java
@SpringBootApplication
public class Application {
private DataSource ds;
@Autowired
public Application(DataSource ds) { this.ds = ds; }
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
@Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner(ds);
}
}
上記のコードは同じ結果をもたらしますが、IntelliJで警告を報告しません。私は混乱しています、2番目のコードは最初のコードよりどのように優れていますか?間違ったロジックに従っていますか?これは別の方法で配線する必要がありますか?
要するに、これを行う正しい方法は何ですか?
noteDataSource
は単なる例であり、この質問は自動配線されているすべてのものに当てはまります。
注2DataSourceを自動配線/初期化する必要があるため、MyCommandLineRunner.Java
に別の空のコンストラクターを含めることはできません。エラーが報告され、コンパイルされません。
それを改善するにはいくつかの方法があります。
@Autowired
メソッドを使用してインスタンスを構築しているので、MyCommandLineRunner
から@Bean
を削除できます。 DataSource
を引数として直接メソッドに注入します。
または、@Autowired
を削除して@Bean
を削除し、MyCommandLineRunner
の@Component
アノテーションをスラップして、検出してファクトリメソッドを削除します。
MyCommandLineRunner
を@Bean
メソッド内にラムダとしてインライン化します。
MyCommandLineRunner
に自動配線がありませんpublic class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
@Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
そしてアプリケーションクラス。
@SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
@Bean
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return new MyCommandLineRunner(ds);
}
}
@Component
の使用@Component
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
@Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
そしてアプリケーションクラス。
@SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
CommandLineRunner
@SpringBootApplication
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class)
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
@Bean
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return (args) -> (logger.info("DataSource: {}", ds);
}
}
これらはすべて、インスタンスを構築する有効な方法です。どちらを使うか、気持ちのいいものを使いましょう。さらに多くのオプションがあります(ここで説明したオプションのすべてのバリエーション)。
フィールドds
をfinalにすることを検討してください。そうすれば、_@Autowired
_は不要になります。依存関係注入についての詳細 http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html#using-boot -spring-beans-and-dependency-injection
コードをクリーンに保つために、Lombokアノテーションの使用を検討しましたか? @RequiredArgsConstructor(onConstructor = @__(@Autowired))
は、@ Autowiredアノテーションを使用してコンストラクタを生成します。詳細はこちら https://projectlombok.org/features/Constructor.html
コードは次のようになります。
_@Slf4j
@RequiredArgsConstructor
// MyCommandLineRunner.Java
public class MyCommandLineRunner implements CommandLineRunner {
//final fields are included in the constructor generated by Lombok
private final DataSource ds;
@Override
public void run(String... args) throws Exception {
log.info("DataSource: {} ", ds.toString());
}
}
// Application.Java
@SpringBootApplication
@RequiredArgsConstructor(onConstructor_={@Autowired}) // from JDK 8
// @RequiredArgsConstructor(onConstructor = @__(@Autowired)) // up to JDK 7
public class Application {
private final Datasource ds;
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
@Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner(ds);
}
}
_
後で編集
Lombokを使用しないソリューションは、Beanの作成時に依存関係を注入するためにSpringに依存しています
_@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
/**
* dependency ds is injected by Spring
*/
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return new MyCommandLineRunner(ds);
}
}
// MyCommandLineRunner.Java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds){
this.ds = ds;
}
@Override
public void run(String... args) throws Exception {
logger.info("DataSource: "+ ds.toString());
}
}
_