320

So since I've been using Spring, if I were to write a service that had dependencies I would do the following:

@Component
public class SomeService {
     @Autowired private SomeOtherService someOtherService;
}

I have now run across code that uses another convention to achieve the same goal

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

Both of these methods will work, I understand that. But is there some advantage to using option B? To me, it creates more code in the class and unit test. (Having to write constructor and not being able to use @InjectMocks)

Is there something I'm missing? Is there anything else the autowired constructor does besides add code to the unit tests? Is this a more preferred way to do dependency injection?

moffeltje
  • 4,521
  • 4
  • 33
  • 57
GSUgambit
  • 4,459
  • 6
  • 25
  • 31

10 Answers10

417

Yes, option B (which is called constructor injection) is actually recommended over field injection, and has several advantages:

  • the dependencies are clearly identified. There is no way to forget one when testing, or instantiating the object in any other circumstance (like creating the bean instance explicitly in a config class)
  • the dependencies can be final, which helps with robustness and thread-safety
  • you don't need reflection to set the dependencies. InjectMocks is still usable, but not necessary. You can just create mocks by yourself and inject them by simply calling the constructor

See this blog post for a more detailed article, by one of the Spring contributors, Olivier Gierke.

gfullam
  • 11,531
  • 5
  • 50
  • 64
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 10
    So let's dive deeper and say you have some other properties like @Value("some.prop") private String property; Would you put this into the constructor as well? It just seems as if you would end up with very long fully parameterized constructors. Which isn't bad perse, just far more code. I definititely appreciate your comment! – GSUgambit Nov 17 '16 at 20:53
  • 25
    Yes, you would put that in the constructor, too. As the linked article says, when the constructor starts having too many parameters, it's often the sign that you should split the class in smaller classes with less responsibilities, and less dependencies. – JB Nizet Nov 17 '16 at 21:41
  • 1
    dont know how I saw everything except he blog post line. Thanks for that! – GSUgambit Nov 17 '16 at 22:44
  • What if you need a second constructor do you need to use @Autowire or can you use two constructors? – powder366 May 11 '17 at 13:27
  • 8
    You can have as many constructors as you want, but only one of them can be annotated with Autowired, and will be called by Spring. – JB Nizet May 11 '17 at 14:23
  • does @Autowired on a property internally does setter injection? – Nisarg Patil Dec 26 '18 at 10:13
  • 3
    @NisargPatil Autowired on a field sets the fiels directly, using reflection, without going through any setter. Autowired on a setter calls the setter. – JB Nizet Dec 26 '18 at 10:14
  • Is it needed to annotate the constructor @Autowired if we deal with a class annotated as a Component? It should work anyway. What are the dangers? – Mateusz Niedbal Oct 12 '22 at 12:52
  • Also, using the constructor means that whatever argument validation your constructor does is applied to the injected values. Compare with the problems introduced by `Serializeable` providing an extra means of object construction, which is now generally considered to have been a mistake. – Raedwald Jun 21 '23 at 12:37
66

I will explain you in simple words:

In Option(A), you are allowing anyone (in different class outside/inside the Spring container) to create an instance using default constructor (like new SomeService()), which is NOT good as you need SomeOtherService object (as a dependency) for your SomeService.

Is there anything else the autowired constructor does besides add code to the unit tests? Is this a more preferred way to do dependency injection?

Option(B) is preferred approach as it does NOT allow to create SomeService object without actually resolving the SomeOtherService dependency.

Vasu
  • 21,832
  • 11
  • 51
  • 67
  • 2
    With option B, how is this not a 'leaky abstraction'? If I don't know beforehand what the actual implementation is of some service, and Impl1 has dependencies A/B/C and Impl2 has A/B/F/G, the constructor approach would require me to know the dependencies up front. But, with @autowire, when i attempt to consume a service, if it has all the necessary dependencies registered in the container, then I'm good, and I didn't have to know anything about the dependencies of the service. – Chris Knoll Oct 01 '18 at 01:43
  • 4
    @ChrisKnoll The point is to know that the dependencies are required for your service to work(especially for testing!). It's not a 'leaky abstraction' to know that your Car needs a Key to start. You don't need to know the exact implementation of Key but you do need to know you need one. Also, as long as you also have your service in question(SomeService) annotated say(Service), then you won't need to know what it's dependencies are. In your class(that consumes the service) have the same autowire annotation for it and you can call SomeService.perform() without calling 'new' keyword – javaBean007 Dec 06 '18 at 21:57
50

Please note, that since Spring 4.3 you don't even need an @Autowired on your constructor, so you can write your code in Java style rather than tying to Spring's annotations. Your snippet would look like that:

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}
stinger
  • 3,790
  • 1
  • 19
  • 30
  • 2
    Interesting, really curious as to how does spring inject the dependency in this case ? – Mr Matrix Jan 04 '20 at 20:51
  • 7
    @el Mowgli - Spring scans your classes for constructor that matches your class' fields. Find details here: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-constructor-injection – stinger Jan 08 '20 at 07:12
  • ... which seems to mean that constructor injection became the default injection mechanism. Still you need the constructor with all the dependencies explicitly passed – Andrey M. Stepanov Sep 27 '21 at 01:22
  • 3
    You can use Lombok's annotation: `@RequiredArgsConstructor` instead of the explicit constructor. – Marcus Voltolim Jul 11 '22 at 18:58
  • @MarcusViníciusVoltolim how your comment about lombok is related to Spring autowiring technique? – stinger Jul 21 '22 at 13:30
  • 3
    @stinger not really, really related but I think a lot of projects are using lombok and Spring together. In our company we have multiple project where we use both and it makes developing a breeze. Instead of writing the constructor (and potentially updating it) you simply slap a `@RequiredArgsConstructor ` and mark any injected service/component `final`. Lombok will create the constructor and Spring will inject it. – Stephan Stahlmann Jul 29 '22 at 10:23
  • Note that this only works when you have a single constructor. See also: https://stackoverflow.com/questions/37930595/spring-framework-4-3-0-when-do-i-need-autowired – Mr. Polywhirl Feb 01 '23 at 20:13
  • Correction: It is true that you don't need to add @Autowired manually because it is IMPLICITLY included when you have only one constructor. Spring will add the @Autowired annotation automatically to the only constructor available, and if there are multiple then it won't do it at all. This also explains why you can slap on `@RequiredArgsConstructor` via Lombok which, if it's the only constructor, will implicitly have `@Autowired`, so Spring and Lombok are working together. It is important to know how this all works without making assumptions or people will get confused. – Andrew Young-Min Cho Jun 21 '23 at 03:58
17

Good to know

If there is only one constructor call, there is no need to include an @Autowired annotation. Then you can use something like this:

@RestController
public class NiceController {

    private final DataRepository repository;

    public NiceController(ChapterRepository repository) {
        this.repository = repository;
    }
}

... example of Spring Data Repository injection.

Daniel Perník
  • 5,464
  • 2
  • 38
  • 46
  • 8
    Good to know but removing an annotation does not improve readability much. – Nitin Gaur Dec 05 '19 at 07:21
  • I would much rather have this than a hundred redundant Autowired when there is already another annotation marking the class as part of the DI system – Novaterata Feb 11 '21 at 21:38
13

Actually, In my experience, The second option is better. Without the need for @Autowired. In fact, it is wiser to create code that is not too tightly coupled with the framework (as good as Spring is). You want code that tries as much as possible to adopt a deferred decision-making approach. That is as much pojo as possible, so much such that the framework can be swapped out easily. So I would advise you create a separate Config file and define your bean there, like this:

In SomeService.java file:

public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

In ServiceConfig.java file:

@Config
public class ServiceConfig {
    @Bean
    public SomeService someService(SomeOtherService someOtherService){
        return new SomeService(someOtherService);
    }
}

In fact, if you want to get deeply technical about it, there are thread safety questions (among other things) that arise with the use of Field Injection (@Autowired), depending on the size of the project obviously. Check this out to learn more on the advantages and disadvantages of Autowiring. Actually, the pivotal guys actually recommend that you use Constructor injection instead of Field Injection

Dougie T
  • 379
  • 3
  • 10
10

I hope I won't be downgraded for expressing my opinion, but for me option A better reflects the power of Spring dependency injection, while in the option B you are coupling your class with your dependency, in fact you cannot instantiate an object without passing its dependencies from the constructor. Dependency Injection have been invented for avoid that by implementing Inversion of Control,so for me option B doesn't have any sense.

5

Autowired constructors provides a hook to add custom code before registering it in the spring container. Suppose SomeService class extends another class named SuperSomeService and it has some constructor which takes a name as its argument. In this case, Autowired constructor works fine. Also, if you have some other members to be initialized, you can do it in the constructor before returning the instance to spring container.

public class SuperSomeService {
     private String name;
     public SuperSomeService(String name) {
         this.name = name;
     }
}

@Component
public class SomeService extends SuperSomeService {
    private final SomeOtherService someOtherService;
    private Map<String, String> props = null;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        SuperSomeService("SomeService")
        this.someOtherService = someOtherService;
        props = loadMap();
    }
}
Atul Dwivedi
  • 1,452
  • 16
  • 29
3

I prefer construction injection, just because I can mark my dependency as final which is not possible while injecting properties using property injection.

your dependencies should be final i.e not modified by program.

noob
  • 349
  • 2
  • 8
0

There are few cases when @Autowired is preferable. One of them is circular dependency. Imagine the following scenario:

@Service
public class EmployeeService {
    private final DepartmentService departmentService;

    public EmployeeService(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }
}

and

@Service
public class DepartmentService {
    private final EmployeeService employeeService;

    public DepartmentService(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }
}

Then Spring Bean Factory will throw circular dependency exception. This won't happen if you use @Autowired annotation in both beans. And this is understandable: the constructor injection happens at very early stage of Spring Bean initialization, in createBeanInstance method of Bean Factory, while @Autowired-based injection happens way later, on post processing stage and is done by AutowiredAnnotationBeanPostProcessor. Circular dependency is quite common in complex Spring Context application, and it needs not to be just two beans referring one another, it could a complex chain of several beans.

Another use case, where @Autowired is very helpful, is self-injection.

@Service
public class EmployeeService {
    
    @Autowired
    private EmployeeService self;

}

This might be needed to invoke an advised method from within the same bean. Self-injection is also discussed here and here.

igor.zh
  • 1,410
  • 15
  • 19
0

There is a way to inject the dependencies through constructor using @RequeiredArgsContructor annotation from Lombok

@RequiredArgsConstructor
@Service
 class A {
     private final B b // needs to be declared final to be injected
}

In this way you don't need to specify a constructor