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

5

阅读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);
    }

}

我想知道这两种解决方案的优缺点,并且哪一种更可取?

3个回答

1
我不相信你会在这个问题上得到"可取"的共识。
第二种样式的优点是,它可以直接使用,不管依赖的 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);
    }

}

第二个优点是它更容易看出一个bean是否有太多的依赖关系,这意味着它可能在做太多的事情,这意味着可能需要进行重构。

实际上,直到几周前我才不知道第二种风格,但自从那时起我一直在使用它。

祝好运!


1
在正常情况下,这两种方法之间不应该有太大的区别。但是,如果您想将配置拆分为多个类,则第一种解决方案不能简单地使用。假设您想将两个@Bean方法放入不同的类中,并通过@Import将第一个导入到第二个中,在第二个类的RabbitTemplate(connectionFactory())构造函数中没有机会让编译器知道第一个类的connectionFactory()方法的连接。因此,您将遇到编译器错误。为了解决这个问题,可以像Spring文档建议的那样使用第二种方法:在导入的@Bean定义上注入依赖项
注入对导入的@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");
}

0

在您的第一个示例中,“RabbitTemplate”使用“connectionFactory()”方法的直接结果。 - gtonic
我不是 OP。调用 connectionFactory 的结果已被缓存。所有调用都将返回相同的值。 - Sotirios Delimanolis
猜测与Spring测试文档相关的部分应该是这里:https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans - 如果我没错的话,上面的第一个片段不允许Spring注入模拟依赖项,而第二种方法可以允许 - 我是对的吗? - Michael Lihs
请编辑您的答案以澄清这一点,以及关于“尽可能多的实例”的部分。 - Sotirios Delimanolis
嗯 - 看起来你的观点是无效的 - 参见 https://gist.github.com/michaellihs/700cb3bfd961e1566682040fce6d4e98。模拟可以使用两种风格,因此在这方面没有争议... - Michael Lihs
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接