Petr Mensik Petr Mensik - 18 days ago 4
Java Question

Why use constructor over setter injection in CDI?

I couldn't find any reasonable answer here on SO so I hope it's not a duplicate. So why should I prefer setter or constructor injection over simple

@Inject
MyBean bean;


I get the usage of the constructor injection if you need to do something with injected bean during your class initialization like

public void MyBean(@Inject OtherBean bean) {
doSomeInit(bean);
//I don't need to use @PostConstruct now
}


but still, it's almost the same like
@PostConstruct
method and I don't get setter injection at all, isn't it just a relic after Spring and other DI frameworks?

Answer

Constructor and property injection gives you the option to initialize the object even in a non CDI environment easily, e.g a unit test.

In a non-CDI environment you can still simply use the object by just passing the constructor arg.

OtherBean b = ....;
new MyBean(b);

If you just use field injection you must use reflection to access the field if it is private for example.

If you use property injection you can also write code in the setter. So it depends on your implementation needs.

Setter vs constructor injection

In object-oriented programming an object must be in a valid state after construction and every method invocation changes the state to another valid state.

For setter injection this means that you might require a more complex state handling, because an object should be in a valid state after construction. Even if the setter has not been invoked yet. Thus the object must be in a valid state even if the property is not set. E.g. by using a default value or a null object.

If you have a dependency between the object's existence and the property, the property should either be a constructor argument. This will also make the code more clean, because if you use a constructor parameter you document that the dependency is necessary.

So instead of writing a class like this

public class CustomerDaoImpl implements CustomerDao {

  private DataSource dataSource;

  public Customer findById(String id){
     // Is the dataSource set?!
     Connection con = dataSource.getConnection();
     ...
     return customer;
  }

  public void setDataSource(DataSource dataSource){
     this.dataSource = dataSource;
  }

}

you should either use constructor injection

public class CustomerDaoImpl implements CustomerDao {

  private DataSource dataSource;

  public CustomerDaoImpl(DataSource dataSource){
      if(dataSource == null){
        throw new IllegalArgumentException("Parameter dataSource must not be null");
     }
     this.dataSource = dataSource;
  }

  public Customer findById(String id) {    
      Customer customer = null;
     // We can be sure that the dataSource is not null
     Connection con = dataSource.getConnection();
     ...
     return customer;
  }
}

My conclusion

  • Use properties for every optional dependency.
  • Use constructor args for every mandatory dependency.

PS: My blog The difference between pojos and java beans explains my conclusion in more detail.