一尘不染

干净的代码-@Autowired应该在哪里应用?

spring-boot

我将从一个简单的例子开始。您有一个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如果我尝试使用setter注入,我会得到相同的警告。如果我也重构它,我会得出一些讨厌的代码。

// 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只是一个纯示例,此问题适用于所有自动接线的情况。

note 2
只能说MyCommandLineRunner.java不能有另一个空的构造函数,因为DataSource需要自动装配/初始化。它会报告错误,并且不会被编译。


阅读 479

收藏
2020-05-30

共1个答案

一尘不染

有几种方法可以改善它。

  1. 您可以删除@Autowired从你的MyCommandLineRunner,你是让一个@Bean方法构建它的一个实例。注入DataSource直接进入方法作为参数。

  2. 或删除@Autowired并删除您@Bean@Component注释,MyCommandLineRunner并在其上打上注释,以检测到它并删除工厂方法。

  3. 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); 
    }
}

所有这些都是构造实例的有效方法。使用哪一种,请使用自己喜欢的一种。还有更多选项(此处提到的所有变化)。

2020-05-30