干净的代码-@Autowired应该在哪里应用? [英] Clean code - Where should @Autowired be applied?
问题描述
我将从一个简单的例子开始.您有一个Spring启动应用程序,该应用程序在初始化时运行CommandLineRunner
类.
I'll start with a simple example. You have a Spring boot application that runs a CommandLineRunner
class on initialization.
// 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
所在的位置报告警告(我在注释中标记了该位置)
Now, like this, this works, everything is OK. However, IntelliJ reports a warning where @Autowired
is located (I marked where in the comment)
Spring团队建议:始终在bean中使用基于构造函数的依赖项注入.始终对强制性依赖项使用断言.
Spring team recommends: Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.
现在,如果我遵循这一点,我将有一个基于构造函数的依赖注入
Now if I follow this, I have a constructor based dependency injection
@Autowired
public MyCommandLineRunner(DataSource ds) { ... }
这也意味着我也必须编辑Application.java
,因为构造函数需要一个参数.在Application.java
中,如果尝试使用setter注入,则会收到相同的警告.如果我也进行重构,我将得出一些讨厌的代码.
This also means that I have to edit Application.java
as well, since the constructor needs an argument. In Application.java
if I try to use the setter injection, I'll get the same warning. If I refactor that as well, I'll end up with some, in my opinion, nasty code.
// 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中不报告任何警告. 我很困惑,第二个代码比第一个更好吗?我是否遵循错误的逻辑?这应该以不同的方式接线吗?
The above code yields the same result, but doesn't report any warnings in IntelliJ. I'm confused, how is the 2nd code better than the first one? Am I following an incorrect logic? Should this be wired differently?
简而言之,正确的方法是什么?
note DataSource
只是一个简单的例子,这个问题适用于所有自动装配的东西.
note DataSource
is just a pure example, this question applies to anything being autowired.
注释2 只是说MyCommandLineRunner.java
不能有另一个空的构造函数,因为DataSource需要自动装配/初始化.它会报告错误,并且不会被编译.
note 2 Just to say that MyCommandLineRunner.java
can't have another, empty, constructor, since DataSource needs to be autowired/initialized. It will report an error and will not be compiled.
推荐答案
有几种改进方法.
-
您可以从
MyCommandLineRunner
中删除@Autowired
,因为您正在让@Bean
方法构造它的实例.将DataSource
作为参数直接注入该方法.
You can remove
@Autowired
from yourMyCommandLineRunner
as you are letting a@Bean
method construct an instance of it. Inject theDataSource
directly into the method as an argument.
或删除@Autowired
并删除@Bean
并在MyCommandLineRunner
上打上@Component
注释以检测到它并删除工厂方法.
Or remove @Autowired
and remove the @Bean
and slap a @Component
annotation on your MyCommandLineRunner
to have it detected and remove factory method.
在您的@Bean
方法内以lambda形式插入MyCommandLineRunner
.
Inline your MyCommandLineRunner
inside your @Bean
method as a lambda.
MyCommandLineRunner
中没有自动装配
No Autowiring in the 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
的用法
Usage of @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);
}
}
所有这些都是构造实例的有效方法.使用哪一种,请使用自己喜欢的一种.还有更多选项(此处提到的所有变体).
All of these are valid ways of constructing your instances. Which one to use, use the one that you feel comfortable with. There are more options (all variations on the ones mentioned here).
这篇关于干净的代码-@Autowired应该在哪里应用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!