java - 应用依赖注入(inject)的正确 Spring Boot 方法是什么

标签 java spring spring-boot dependency-injection

我目前正在开发一个 Spring Boot 应用程序,它通过以下方式将一些 Bean 连接在一起(高度简化的示例):

@Component
@Order(0)
public class PlayingFieldByBeans implements CommandLineRunner { 

    @Override
    public void run(String... arg0) throws Exception {
        List<String> names = new ArrayList<>();
        names.add("Alex");
        names.add("Benedict");
        names.add("Chloe");

        System.out.println("Printing from lazy beans variant: ");
        names.forEach(n -> {
            System.out.println(player(n));
        });

    }

    @Bean
    @Lazy
    public Player player(String name) {
        return new Player(name, shoes());       
    }

    @Bean
    @Lazy
    private Shoes shoes() {
        return new Shoes("Adidas");     
    }   
}

然而,实际的 Bean 需要比此处显示的更多的配置和设置,并且在使用内部 Lazy Bean 方法时,在 PlayingFieldByBeans 类中需要相当多的代码行。因此,我创建了一种使用 Component 注释将其连接在一起的不同方式:

@Component
@Order(1)
public class PlayingFieldByComponents implements CommandLineRunner {    

    @Autowired
    private PlayerComponent playerComponent;

    @Override
    public void run(String... arg0) throws Exception {
        List<String> names = new ArrayList<>();
        names.add("Alex");
        names.add("Benedict");
        names.add("Chloe");

        System.out.println("Printing from component variant: ");
        names.forEach(n -> {
            System.out.println(playerComponent.player(n));
        });

    }       
}

PlayerComponent 类如下所示:

@Component
public class PlayerComponent {

    @Autowired
    private ShoesComponent shoesComponent;

    public Player player(String name) {
        return new Player(name, shoesComponent.shoes());
    }
}

ShoesComponent 与 PlayerComponent 类非常相似。

出于可维护性和 TDD 的目的,我不确定在这里使用 spring 框架的最正确方法是什么。

问题

鉴于 Player 和 Shoes bean 需要的不仅仅是一行初始化(多个设置、对其他 bean 的多个依赖项等),设计和连接它们的最佳方法是什么?

编辑 - 根据建议

添加了一个配置类来捆绑 bean:

@Configuration
public class BeanConfiguration {

    @Bean
    @Lazy
    public Player player(String name) {
        return new Player(name, shoes());       
    }

    @Bean
    @Lazy
    public Shoes shoes() {
        return new Shoes("Adidas");     
    }

}

以及匹配的执行类:

@Component
@Order(2)
public class PlayingFieldByConfiguration implements CommandLineRunner { 

    @Autowired
    private BeanConfiguration beanConfiguration;

    @Override
    public void run(String... arg0) throws Exception {
        List<String> names = new ArrayList<>();
        names.add("Alex");
        names.add("Benedict");
        names.add("Chloe");

        System.out.println("Printing from component variant: ");
        names.forEach(n -> {
            System.out.println(beanConfiguration.player(n));
        });

    }       
}

重新使用相同的第一个 bean,因此它似乎没有创建新的 bean

Printing from component variant: 
Player name: Alex has shoes of brand: Adidas
Player name: Alex has shoes of brand: Adidas
Player name: Alex has shoes of brand: Adidas

最佳答案

一种解决方案是更改 Player bean 的范围(如果我们想创建不同的品牌,则稍后更改 Shoes),正如 Andriy Slobodyanyk 提到的

@Configuration
public class BeanConfiguration {

    @Bean
    @Lazy
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public Player player(String name) {
        return new Player(name, shoes());
    }

    @Bean
    @Lazy
    public Shoes shoes() {
        return new Shoes("Adidas");
    }
}

如果上述还不够(因为您提到实际案例场景更加复杂),另一种选择是使用 FactoryBean

public class PlayerFactoryBean implements FactoryBean<Player> {

    private String name;
    private Shoes shoes;

    public void setName(String name) {
        this.name = name;
    }

    public void setShoes(Shoes shoes) {
        this.shoes = shoes;
    }

    @Override
    public Player getObject() throws Exception {
        //initialization logic goes here
        System.out.println("Creating bean using factory");
        return new Player(name, shoes);
    }

    @Override
    public Class<Player> getObjectType() {
        return Player.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
<小时/>
@Configuration
public class BeanConfiguration {

    @Bean
    @Lazy
    public Shoes shoes() {
        return new Shoes("Adidas");
    }

    @Bean
    public PlayerFactoryBean playerFactoryBean(){
        PlayerFactoryBean pfb = new PlayerFactoryBean();
        pfb.setShoes(shoes());
        return pfb;
    }
}
<小时/>
@Component
@Order(2)
public class PlayingFieldByConfiguration implements CommandLineRunner {

    @Autowired
    private PlayerFactoryBean factoryBean;

    @Override
    public void run(String... arg0) throws Exception {
        List<String> names = new ArrayList<>();
        names.add("Alex");
        names.add("Benedict");
        names.add("Chloe");

        System.out.println("Printing from component variant: ");
        names.forEach(n -> {
            try {
                factoryBean.setName(n);
                System.out.println(factoryBean.getObject());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

    }
}

关于java - 应用依赖注入(inject)的正确 Spring Boot 方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43559478/

相关文章:

java - SQLException,参数 5 没有值

java - 如何使用正则表达式验证字符串必须以字母开头,所有其他字符在java中可以是字母/数字/空格

java - HttpSessionListener 中的事务提交不会持久保存到数据库

java - 如何在 java.validation 约束上国际化 Spring RestController?

java - Spring 批MultiResourceItemWriter : How to uniquely identify each of the written files

mysql - Docker中的Spring无法连接MySQL

java - org.openqa.selenium.WebDriverException : unknown error: DevToolsActivePort file doesn't exist

java - Spring 启动器 : Missing Bean instead of missing value

spring - 如何在 Spring MongoDB 中将 2 个整数值连接成一个字符串

java - Spring Boot从application.properties读取值