我将从一个简单的例子开始。您有一个Spring启动应用程序,该应用程序CommandLineRunner在初始化时运行一个类。
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位于何处(我在注释中标记了何处)
@Autowired
Spring团队建议: 始终在bean中使用基于构造函数的依赖项注入。始终对强制性依赖项使用断言。
现在,如果我遵循这一点,我将有一个基于构造函数的依赖注入
@Autowired public MyCommandLineRunner(DataSource ds) { ... }
这也意味着我也必须编辑Application.java,因为构造函数需要一个参数。在Application.java如果我尝试使用setter注入,我会得到相同的警告。如果我也重构它,我会得出一些讨厌的代码。
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只是一个纯示例,此问题适用于所有自动接线的情况。
DataSource
note 2 只能说MyCommandLineRunner.java不能有另一个空的构造函数,因为DataSource需要自动装配/初始化。它会报告错误,并且不会被编译。
MyCommandLineRunner.java
有几种方法可以改善它。
您可以删除@Autowired从你的MyCommandLineRunner,你是让一个@Bean方法构建它的一个实例。注入DataSource直接进入方法作为参数。
MyCommandLineRunner
@Bean
或删除@Autowired并删除您@Bean的@Component注释,MyCommandLineRunner并在其上打上注释,以检测到它并删除工厂方法。
@Component
将MyCommandLineRunner您的@Bean方法内联为lambda。
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 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); } }
@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); } }
所有这些都是构造实例的有效方法。使用哪一种,请使用自己喜欢的一种。还有更多选项(此处提到的所有变化)。