Reputation: 243
I am trying to wrap my head around the @Autowired annotation. I have read into it, but it still isn't making much sense to me
The specific case I'm looking for at is passing a repository into a micro service class
Why do we do
@Autowired
public SomeClass (Repo repo) {
this.repo = repo;
}
instead of simply doing
public SomeClass (Repo repo) {
this.repo = repo;
}
Upvotes: 1
Views: 922
Reputation: 172616
You are right. DI Containers, like Spring, are meant to decouple components from one another, by adhering to the Dependency Inversion Principle (DIP). From that perspective, it is rather awkward—to say the least—when your DI Container forces you to apply those library-specific attributes to your classes. This again introduces tight coupling—the thing we are working so hard to prevent. It also forces a vendor lock-in. All your application code now depends on this external tool. This is a violation of the DIP.
Instead, when your application components have a single public constructor, which is a good practice, there should be no need to define any such attribute on the class. In that case, the class's constructor unambiguously declares its required dependencies. Any good DI Container should be able to compose an object graph based on the static type information provided by that single constructor.
If I'm not mistaken, newer versions of Spring do allow you to ommit the attribute.
Upvotes: 1
Reputation:
The @Autowired annotation will automatically use a 'Bean' (spring-managed resource) of type Repo from the application context if one exists. If your SomeClass method is itself a 'Bean' (annotated with @Component, @Service, or @Repository), spring will ensure that a bean of type Repo exists in the application context before instantiating your SomeClass bean.
This article is helpful: https://www.baeldung.com/spring-autowire
As others on this thread have mentioned, constructor injection makes unit testing easier. One way to get around this is to create a static class annotated with @TestConfiguration that contains the beans you need for your test context.
This article has some info about that: https://www.baeldung.com/spring-boot-testing
The Baeldung site is a great resource if you are new to spring.
Upvotes: 0
Reputation: 4681
You can do this:
public SomeClass (Repo repo) {
this.repo = repo;
}
if you plan to instantiate your object yourself.
But since you want the container to instantiate your objects (beans), you instruct it to inject your Repo
dependency during SomeClass
bean creation. You do that by adding @Autowired
on top of your constructor.
You could also do this (field injection):
@Component
public class SomeClass {
@Autowired
private Repo repo;
}
But to make unit testing easier, constructor injection is preferable.
Note that since Spring 4.3 @Autowired
is no longer necessary on the constructor.
EDIT
Better, when using Lombok
you can do this:
@Component
@RequiredArgConstructor
public class SomeClass {
private final Repo repo;
}
Lombok will generate the constructor
public SomeClass (Repo repo) {
this.repo = repo;
}
for you and the container will inject the dependency.
Upvotes: 1
Reputation: 3805
When you have multiple constructor, you must indicate to spring which constructor to use. Useless much of the time.
Check here : https://www.baeldung.com/constructor-injection-in-spring
As of Spring 4.3, classes with a single constructor can omit the @Autowired annotation. A nice little bit of convenience and boilerplate removal!
Upvotes: 0