在Spring中传递Bean依赖项的首选方法是什么?

时间:2016-09-29 19:13:53

标签: java spring dependency-injection spring-boot javabeans

在阅读Spring框架的文档时,我偶然发现了在工厂方法中传递Bean依赖关系的两种不同样式。第一个是这样的(直接使用依赖的工厂方法):

@Configuration
public class MessagingConfiguration {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        return connectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        return new RabbitTemplate(connectionFactory());
    }

}

第二个看起来像这样(将依赖项作为工厂方法中的参数注入):

@Configuration
public class MessagingConfiguration {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        return connectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }

}

我想知道两种解决方案的PRO和CON是什么,哪种更可取?

3 个答案:

答案 0 :(得分:1)

我不相信你会对"首选"达成共识对此。

第二种风格的PRO是它按原样工作,无论依赖bean的定义在哪里。

例如,如果有问题的两个bean是在两个独立的@Configuration类中定义的,那么第一个类就会变成这样:

@Configuration
public class MessagingConfiguration1 {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        return connectionFactory;
    }

}

@Configuration
public class MessagingConfiguration2 {

    @Autowired
    ConnectionFactory connectionFactory;    

    @Bean
    public RabbitTemplate rabbitTemplate() {
        return new RabbitTemplate(this.connectionFactory);
    }

}

虽然第二种风格保持不变:

@Configuration
public class MessagingConfiguration1 {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        return connectionFactory;
    }

}

@Configuration
public class MessagingConfiguration1 {

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }

}

第二个PRO是它可以更容易地看到bean有多少依赖项,这意味着它可能做得太多,这意味着它可能是重构的时候。

直到几周前我才意识到第二种风格,但这是我以后一直在使用的。

祝你好运!

答案 1 :(得分:1)

在正常使用情况下,两种方法之间不应存在如此大的差异。但是,如果您希望将配置拆分为多个类,则不能简单地使用第一个解决方案。假设您想将两个@Bean方法放入单独的类中,并通过@Import将第一个方法导入第二个类,编译器无法知道RabbitTemplate中第一个类的connectionFactory()方法(connectionFactory( ))第二类的构造函数。所以你会遇到编译器错误。要解决此问题,您可以使用spring文档中建议的第二种方法:Injecting dependencies on imported @Bean definitions

  

注入对导入的@Bean定义的依赖

     

上面的例子很有效,但很简单。在最实际的   在这些场景中,bean将彼此依赖   配置类。使用XML时,这本身不是问题,   因为没有涉及编译器,人们可以简单地声明   ref =“someBean”并相信Spring会在容器中解决它   初始化。当然,在使用@Configuration类时,Java   编译器在配置模型上放置约束   对其他bean的引用必须是有效的Java语法。

     

幸运的是,解决这个问题很简单。正如我们已经讨论的,   @Bean方法可以有任意数量的参数来描述   bean依赖。让我们考虑一个更真实的场景   几个@Configuration类,每个类都取决于声明的bean   其他人:

@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }

}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new     AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

答案 2 :(得分:0)

第二个(如果您考虑在编写代码时进行测试)。始终尝试通过依赖注入来避免硬连接和创建实例,以便更容易测试代码(例如,模拟https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html)并保持可配置。