【问题标题】:Spring Boot - Auto configuration of a class that contains AutoWired dependenciesSpring Boot - 包含 AutoWired 依赖项的类的自动配置
【发布时间】:2020-10-28 11:10:49
【问题描述】:

我正在开发一个具有可重用逻辑的通用 Java 库,以与一些 AWS 服务进行交互,而这些服务又将被多个消费者应用程序使用。出于here 概述的原因,以及 Spring Boot 似乎为 SQS 集成之类的东西提供了许多样板免费代码这一事实,我决定将这个公共库实现为具有自动配置的自定义 Spring Boot 启动器。

我也是 Spring 框架的新手,因此遇到了一个问题,即我的自动配置类的实例变量没有通过 AutoWired 注释初始化。

为了更好地解释这一点,这里是我常见依赖项的一个非常简化的版本。

CommonCore.java

@Component
public class CommonCore { 

   @AutoWired
   ReadProperties readProperties;

   @AutoWired
   SqsListener sqsListener; // this will be based on spring-cloud-starter-aws-messaging 

   public CommonCore() {
       Properties props = readProperties.loadCoreProperties();
       //initialize stuff
   }

   processEvents(){
     // starts processing events from a kinesis stream.
   }
}

ReadProperties.java

@Component
public class ReadProperties {

    @Value("${some.property.from.application.properties}")
    private String someProperty;

    public Properties loadCoreProperties() {
      Properties properties = new Properties();
      properties.setProperty("some.property", someProperty);
      
      return properties;
    }
 }

CoreAutoConfiguration.java

@Configuration
public class CommonCoreAutoConfiguration {

    @Bean
    public CommonCore getCommonCore() {  
        return new CommonCore();
    }
}

通用依赖将被其他应用程序使用,如下所示:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class SampleConsumerApp implements ApplicationRunner {

    @Autowired
    CommonCore commonCore;

    public SampleConsumerApp() {
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleConsumerApp.class, args);
    }

    @Override
    public void run(ApplicationArguments args) {

        try {
            commonCore.processEvents();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我提到的主要问题是CommonCore 实例中的AutoWired 对象未按预期初始化。但是,我认为实际的问题是根深蒂固的;但由于我对 Spring 框架缺乏了解,我发现自己很难调试。

我希望在这些方面提供一些指导

  1. 这种开发自定义启动器的方法对我的用例有意义吗?
  2. AutoWired 依赖项无法使用这种方法初始化的原因是什么?

【问题讨论】:

  • 很难这样说,但也许它们根本没有实例化。例如,我会为其中一个@Component 类(例如ReadProperties )创建一个无参数构造函数,并在那里放置一个日志行或一个调试器断点,看看它是否被构造。也许这与您的组件扫描范围有关
  • 感谢您的回复。我试过了,构造函数没有被调用。我想要进行自动配置的原因是避免必须为使用公共库的应用程序定义自定义组件扫描路径。如果我在 CommonCoreAutoConfiguration 类本身中自动连接,它就可以工作。但不在我需要的 CommonCore 类中
  • 你能从核心 jar 中分享你的 META-INF/spring.factories 文件吗?

标签: java spring spring-boot


【解决方案1】:

猜测,但我认为这是因为事物的构造顺序。我说的是这个类:

@Component
public class CommonCore { 

   @AutoWired
   ReadProperties readProperties;

   @AutoWired
   SqsListener sqsListener; // this will be based on spring-cloud-starter-aws-messaging 

   public CommonCore() {
       Properties props = readProperties.loadCoreProperties();
       //initialize stuff
   }

   processEvents(){
     // starts processing events from a kinesis stream.
   }
}

您正在尝试在构造函数中使用 Spring 注入的组件,但构造函数在 Spring 执行其 @Autowire 魔术之前被调用。

所以一种选择是自动装配为构造函数参数

类似这样的东西(未经测试):

@Component
public class CommonCore { 

   private final ReadProperties readProperties;

   private final SqsListener sqsListener; // this will be based on spring-cloud-starter-aws-messaging 
   @AutoWired 
   public CommonCore(SqsListener sqsListener, ReadProperties readProperties) {
       this.readProperties = readPropertis;
       this.sqsListener = sqsListener;
       Properties props = readProperties.loadCoreProperties();
       //initialize stuff
   }

   processEvents(){
     // starts processing events from a kinesis stream.
   }
}

旁注:我更喜欢通过构造函数参数使用依赖注入,只要有可能。这也使得单元测试更容易,无需任何 Spring 特定的测试库。

【讨论】:

  • 感谢您的回复。使用这种方法,在CommonCoreAutoConfiguration类的自动配置过程中我应该如何初始化CommonCore实例?
  • 为什么需要初始化它。它已经是@Component,因此由 Spring 初始化。我认为您根本不需要 @Bean 方法
  • 或者选项二不要设为@Component。而是在您的 @Bean 方法中手动创建它,并将 SqsListenerReadProperties 作为该方法的参数。然后从@ CommonCore 中删除@Component@AutoWired 注释。
  • 知道了,谢谢!我认为使用后一种方法,使用该库的任何人都需要在其应用程序中使用 @ComponentScan 来发现库中的组件。我想避免这种情况,因此我遵循此处描述的自动配置方法:stackoverflow.com/questions/48808752/…
  • 这属于“固执己见”的领域,但是当对象依赖于另一个对象时,在构造时给它一个依赖项不会违反 OOP 原则。向例如用户公开这些参数会破坏封装原则。撇开 Spring 不谈,如果第三个人想要构建 CommonCore,那么她想知道构建一个需要什么。为他们隐藏这一点并不能完成太多的 IMO。此外,您始终可以让您的构造函数保持包或受保护的可见性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 2021-02-21
  • 2014-12-06
  • 2018-01-11
  • 2020-03-26
相关资源
最近更新 更多