spring @Autowire property vs setter

JavaSpringDependency Injection

Java Problem Overview


What is the difference between anotate @Autowired to a property or do it in the setter?

As far as I know they both have the same result, but is there any reason to use one over the other?

UPDATE (to be more concise)

Is there a difference between this

package com.tutorialspoint;

import org.springframework.beans.factory.annotation.Autowired;

public class TextEditor {
   private SpellChecker spellChecker;

   @Autowired
   public void setSpellChecker( SpellChecker spellChecker ){
      this.spellChecker = spellChecker;
   }
   
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

and this

package com.tutorialspoint;

import org.springframework.beans.factory.annotation.Autowired;

public class TextEditor {
   @Autowired
   private SpellChecker spellChecker;

   public TextEditor() {
      System.out.println("Inside TextEditor constructor." );
   }
   
   public void spellCheck(){
      spellChecker.checkSpelling();
   }
}

Java Solutions


Solution 1 - Java

Sometimes you need an instance of class A, but you do not store A in the fields of the class.
You just need A instance to perform a one-shot operation. Or, you use A instance to obtain an instance of B, and you are storing B in the field.

In those cases, a setter (or constructor) autowire will suit you better.
You will not have unused class-level fields.

Concrete example:
You need to construct RabbitTemplate (an object that sends messages to RabbitMQ) To construct it, you need ConnectionFactory
http://docs.spring.io/spring-amqp/docs/latest_ga/api/org/springframework/amqp/rabbit/core/RabbitTemplate.html#RabbitTemplate-org.springframework.amqp.rabbit.connection.ConnectionFactory-

You do not need to store that ConnectionFactory. In that case, code that looks like this:

Class MyClass {
private RabbitTemplate template;

@Autowired 
void setConnectionFactory(ConnectionFactory c) {
    template=new RabbitTemplate(c);
}
}

...will serve you better than directly autowiring the ConnectionFactory field.

In this example, autowiring at the constructor level would be even better, because your object will always be completely constructed. It will be clear that ConnectionFactory is a mandatory dependency, not an optional one.

Solution 2 - Java

With @Autowired annotation, you don't need a setter method. Once your bean's constructor is done with allocating/creating the object, Spring will scan for this annotation and would inject the object instances that you annotated.

While if you have setter and if you are still using xml config, you would explicitly set properties.

Having said that, You could annotate your constructor and setter method with autowired annotation which i would prefer as this would give me flexibility later on to move away from Spring (although i wont do it).

Solution 3 - Java

If you use @Autowired annotation on a property, spring will initiate the property using spring.xml. You don't need setter in this case.

If you use @Autowired annotation on a setter, you are specifying to spring that it should initiate this property using this setter method where you can add your custom code, like initializing some other property with this property.

Usage with Example: In the case of using DAO operations using JdbcTemplate, you need DataSource as an input to JdbcTemplate, but DataSource is not required as a property in itself. So you can use DataSource Setter to initialize JdbcTempate by auto-wiring DataSource Setter. Please see the below code:

class DaoDemo{
   //@Autowired
   //private DataSource dataSource;
   private JdbcTemplate jdbcTemplate;

   @Autowired
   public void setDataSource(DataSource dataSource){
     //this.dataSource = dataSource;  
     this.jdbcTemplate = new JdbcTemplate(dataSource);
   }

   public int getTableRowCount(){
      String sql = "SELECT COUNT(*) FROM DEMOTABLE";
      //jdbcTemplate.setDataSource(dataSource);    //No need to do this as its done in DataSource Setter now.
      return jdbcTemplate.queryForObject(sql,Integer.class);

}

In the above code, the only use of dataSource was to get passed in JdbcTemplate. So, creating a property of dataSource doesn't make sense here. So, just use the @Autowired on setter method of DataSource bean to get its entry from spring.xml and make use of it at that particular time itself.

Solution 4 - Java

There are 3 types of autowiring:

  • Property based
@Autowired
private MyService service;
  • Constructor based. Note in Spring Boot you don't even need @Autowired annotation in this case:
class MyController {
 private final MyService service;

 public MyController(MyService service) {
  this.service = service;
 }
}
  • Setter based:
private MyService service;


@Autowired
public void setService(MyService service) {
 this.service = service;
}

It is recommended to use Constructor based, then if not possible, Setter based and lastly Property based.

Why?

  • First, because in Constructor based you don't even use any Spring annotations. This helps you transition to different frameworks.

  • Second, Constructor or Setter based, make unit testing much easier. You don't need to use any Spring specific testing tools and you can only use Junit and Mockito.

  • Third, Constructor based is good because you can declare the property as final and not expose setters which helps with immutability and thread safety of the class.

Solution 5 - Java

Autowiring works best when it is used consistently across a project. If autowiring is not used in general, it might be confusing to developers to use it to wire only one or two bean definitions. With @Autowired on a field you don't need a setter method, which, on one hand makes the class smaller and easier to read, but on the other hand makes mocking the class a bit uglier.

Explicit dependencies in property and constructor-arg settings always override autowiring. You cannot autowire so-called simple properties such as primitives, Strings, and Classes (and arrays of such simple properties). This limitation is by-design.

Autowiring is less exact than explicit wiring. Spring is careful to avoid guessing in case of ambiguity that might have unexpected results, the relationships between your Spring-managed objects are no longer documented explicitly.

Wiring information may not be available to tools that may generate documentation from a Spring container.

Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Maps, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown.

Solution 6 - Java

If you can, you should avoid the setter. If you don't need it, it's better when it doesn't exists, right?

I personally prefer Guice allowing me to write

public class TextEditor {
   private final SpellChecker spellChecker;

   @Inject public TextEditor(SpellChecker spellChecker) {
      this.spellChecker = spellChecker;
   }

   public void spellCheck(){
      spellChecker.checkSpelling();
   }
}

This goes a step further: With a final field, I know it won't ever change and I get the multithreading visibility guarantee.

Solution 7 - Java

There is one case where using @Autowired on an OPTIONAL property would not work.

If you want to do some initialization using that property, it might not be set before the constructor is called, and since it is optional, you cannot put it as an argument in the constructor.

In that case it is better to use an @Autowired setter method, so you can perform the initialization once the property is autowired.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlusoView Question on Stackoverflow
Solution 1 - JavaBartosz BilickiView Answer on Stackoverflow
Solution 2 - JavaSMAView Answer on Stackoverflow
Solution 3 - JavaSahil ChhabraView Answer on Stackoverflow
Solution 4 - JavaACVView Answer on Stackoverflow
Solution 5 - Java1218985View Answer on Stackoverflow
Solution 6 - JavamaaartinusView Answer on Stackoverflow
Solution 7 - JavaAdamSkwerskyView Answer on Stackoverflow