我将从一个简单的例子开始。您有一个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所在的位置(我在注释中标出了位置)



现在,如果我遵循这一点,我将有一个基于构造函数的依赖注入(inject)
@Autowired
public MyCommandLineRunner(DataSource ds) { ... }

这也意味着我也必须编辑Application.java,因为构造函数需要一个参数。在Application.java中,如果尝试使用setter注入(inject),则会收到相同的警告。如果我也重构它,我会得出一些讨厌的代码。
// 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只是一个纯粹的示例,此问题适用于所有 Autowiring 的东西。

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

最佳答案

有几种方法可以改善它。

  • 当您让@Autowired方法构造它的实例时,可以从MyCommandLineRunner中删除@Bean。将DataSource作为参数直接注入(inject)到方法中。
  • 或删除@Autowired并删除@Bean并在@Component上打上MyCommandLineRunner批注以检测到它并删除工厂方法。
  • 将您的MyCommandLineRunner作为lambda内联到@Bean方法中。
  • MyCommandLineRunner中没有 Autowiring
    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);
        }
    }
    

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

    10-07 17:01