简体   繁体   中英

What is the proper spring boot way to apply dependency injection

I am currently working on an spring boot application that wires some beans together in the following way (heavily simplified example):

@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");     
    }   
}

The actual beans however, require alot more configuration and setting than is shown here and it takes quite alot of lines of code in the PlayingFieldByBeans class when using the inner Lazy Bean methodology. So I created a different way of wiring it together using Component annotation:

@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));
        });

    }       
}

The PlayerComponent class looks like this:

@Component
public class PlayerComponent {

    @Autowired
    private ShoesComponent shoesComponent;

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

The ShoesComponent is very similar to the PlayerComponent class.

For maintainablity and TDD purposes I am not sure what is the most proper way to use the spring framework here.

Question

Given the Player and Shoes beans require more then just one line of initialization (multiple settings, multiple dependencies on other beans etc), what is the best way to design and wire them?

Edit - based on suggestion

Added a configuration class to bundle the beans:

@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");     
    }

}

And the matching executing class:

@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));
        });

    }       
}

Re uses the same first bean, so it doesn't seem to create a new one

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

One solution would be to change scope of Player bean (and Shoes later on if we want to create different brands) as mentioned by 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");
    }
}

If above would not be sufficient (since you mentioned real case scenario is more compilcated) another option is to use 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();
            }
        });

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM