Spring - @Autowire a List with elements

1

I have a controller that has this @Autowired:

@Autowired
Rules rules;

This Rules class is defined as follows:

@Service
public class Rules {

    @Autowired
    private List<RegistrationRule> allRules;

    public List<RegistrationRule> getAllRules() {
        return allRules;
    }
}

I want it when I do @Autowired in the allRules list it already comes with some standard items inside it, as follows:

allRules.add( new EmployeePositionRule() );
allRules.add( new CostCenterRule() );

I tried putting it in the constructor of the Rules class as follows:

public Rules() {
    allRules.add( new EmployeePositionRule() );
    allRules.add( new CostCenterRule() );
}

But if I make an exception, it is thrown at the time of compiling the project:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'intRaptMecController': Unsatisfied dependency expressed through field 'rules'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rules' defined in file [...validations\rules\Rules.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [validations.rules.Rules]: Constructor threw exception; nested exception is java.lang.NullPointerException

What is the right way to do this?

    
asked by anonymous 06.07.2018 / 17:39

2 answers

2

As Leonardo Lima commented, UnsatisfiedDependencyException happens because Spring did not find any Bean of type List<RegistrationRule> . For this, you can declare a method that returns the default values you need. Example:

@Bean
public List<RegistrationRule> myDefaultRules() {
  List<RegistrationRule> defaultRules = new ArrayList();
  defaultRules.add(new EmployeePositionRule());
  defaultRules.add(new CostCenterRule());
  return defaultRules;
}

Note that the @Bean annotation is to tell Spring that you are providing a dependency, in this case your default list of RegistrationRule .

If you wanted to inject different values into your allRules list, you need to declare other Beans and use the @Qualifier annotation to differentiate between which list of RegistrationRule you want to inject. Example:

@Bean(name = "defaultRules")
public List<RegistrationRule> myDefaultRules() {
  List<RegistrationRule> defaultRules = new ArrayList();
  defaultRules.add(new EmployeePositionRule());
  defaultRules.add(new CostCenterRule());
 return defaultRules;
}

@Bean(name = "otherRules")
public List<RegistrationRule> otherRules() {
  List<RegistrationRule> otherRules = new ArrayList();
  otherRules.add(new FooRule());
  otherRules.add(new MyOtherRule());
  return otherRules;
}

@Autowired
@Qualifier("defaultRules")
private List<RegistrationRule> defaultRules;

@Autowired
@Qualifier("otherRules")
private List<RegistrationRule> otherRules;

I hope I have helped!

EDIT 1:

Try this example.

@Configuration
public class RulesConfiguration {

    @Bean
    public List<RegistrationRule> allRules() {
        List<RegistrationRule> allRules = new ArrayList();
        allRules.add(new EmployeePositionRule());
        allRules.add(new CostCenterRule());
        return allRules;
    }

}

@Service
public class RuleService {

    @Autowired
    List<RegistrationRule> rules;

}
    
06.07.2018 / 18:08
3

The reason is simple.

The constructor of class Rules is called before to inject List<RegistrationRule> into the allRules field. Therefore, allRules will always be null.

This would be the current code, still with the problem:

@Service
public class Rules {

    @Autowired
    private List<RegistrationRule> allRules;

    public Rules() {
       allRules.add( new EmployeePositionRule() ); //allRules estará nulo!
       allRules.add( new CostCenterRule() );
    }

    public List<RegistrationRule> getAllRules() {
        return allRules;
    }
}

The fix can be injection by constructor or by set method. In fact, avoid using @Autowired in private fields (the famous Field Injection ), as this injection technique is the least recommended by Various reasons , although it is (unfortunately) very common.

So, try doing it this way:

@Service
public class Rules {

    private List<RegistrationRule> allRules;

    @Autowired
    public Rules(List<RegistrationRule> allRules) {
       allRules.add( new EmployeePositionRule() );
       allRules.add( new CostCenterRule() );
    }

    public List<RegistrationRule> getAllRules() {
        return allRules;
    }
}

Also confirm that your @Bean list of RegistrationRule was created correctly. It can be created within any class annotated with @Configuration ( org.springframework.context.annotation.Configuration ), like this:

@Configuration
public class RulesConfiguration {

    @Bean
    public List<RegistrationRule> allRules() {
       List<RegistrationRule> rules = new ArrayList();
       return rules;
    }

}
    
06.07.2018 / 18:03