我将从一个简单的例子开始。你有一个Spring启动应用程序在初始化时运行CommandLineRunner
类。
// 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();
}
}
现在,像这样,这个工作,一切都很好。但是,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中的任何警告。我很困惑,第二代码比第一代更好?我是否遵循了错误的逻辑?它应该以不同方式连接吗?
简而言之,这样做的正确方法是什么?
提前致谢!
请注意,如果有语法或逻辑错误,我会直接在这里输入代码。 DataSource
只是一个纯粹的例子,这个问题适用于任何自动装配的东西。
注意2只是说MyCommandLineRunner.java
不能有另一个空的构造函数,因为DataSource需要自动装配/初始化。它将报告错误,不会被编译。
有几种方法可以改善它。
@Autowired
中删除MyCommandLineRunner
,因为您要让@Bean
方法构造它的实例。将DataSource
直接注入方法作为参数。@Autowired
并移除@Bean
并在@Component
上拍打MyCommandLineRunner
注释以检测并删除工厂方法。MyCommandLineRunner
方法中的@Bean
内联为lambda。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
最终,然后你不需要@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的解决方案依赖于Spring在创建bean时注入依赖关系
@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());
}
}