Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

java - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 usually must use reflection to access the field, because fields are usually private.

If you use property injection you can also write code in the setter. E.g. validation code or you clear internal caches that hold values which are derived from the property that the setter modifies. What you want to do 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){
     checkDataSource();

     Connection con = dataSource.getConnection();
     ...
     return customer;
  }

  private void checkDataSource(){
     if(this.dataSource == null){
         throw new IllegalStateException("dataSource is not set");
     }
  }

 
  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.

EDIT

Spring also suggests to use constructor injection as I found in the spring documentation, section Setter-based Dependency Injection.

The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.

Constructor injection is also a better way when you think about unit tests, because it is easier to call the constructor instead of setting private (@Autowired) fields.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...