Dependency Inversion Types in Sprint Boot
There are two basic types of Dependency Injection:
constructor and setter.
Spring recommends constructor based dependency in most cases. From the Spring docs: “Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.”
Constructor DI
Let’s say I have this POJO:
public class MeaningOfLife {
final int lifeInt;
final String lifeString;
public MeaningOfLife(int lifeInt, String lifeString) {
this.lifeInt = lifeInt;
this.lifeString = lifeString;
}
public int getLifeInt() {
return lifeInt;
}
public String getLifeString() {
return lifeString;
}
}
@Value
annotation to pick up properties from the environment and, importantly, we are providing defaults. This construct: @Value("#{ @environment['life.int'] ?: 0 }") int intLife
can be read as:MeaningOfLife
elsewhere in our app, such as our HomeController
:http localhost:8080/meaningOfLife
, you will see it returnsSetter DI
Let’s say I have this interface:
And I have a couple of implementation classes that I’ll expose as beans:
Person
and Dog
as setters:Nameable
parameter. In order have Spring call these setters properly when it instantiates the NameHelper
, we have to give it a hint to know which implementation to inject. In this case, we use the @Qualifier
annotation with the bean name we want to inject. There’s a little bit of Spring magic going on here in that a bean’s default name will be it’s class name converted into standard Java variable format. So, it the class was named MyVeryImportantBean
, it’s default name in Spring would be myVeryImportantBean
.In our HomeController
, I’ve added a new method:
@RequestParam
is required, so in order to hit this endpoint, you must give it a type
query parameter:The above will respond with Micah
.
Among the cool features of Spring is that you can autowire an array of bean interface type and Spring will load it up with all of the concrete objects its instantiated of that type. For instance, I’ve added the following to our NameHelper
component:
The @Autowired
setter will be called by Spring with an array containing a Person
and a Dog
Nameable
. If we added another class that implements Nameable
, it would automatically be added to this list.
The getAllNames
method uses some of the nice Java 8 streams interface to give us an array of the calls to the getName
method for each of the Nameable
s in the array.
In our HomeController
we can add an endpoint to exercise this
http localhost:8080/allNames
returns:Bringing it Home
It’s easy to get a list of all of the beans that are loaded into the Spring application context. These beans can be referenced and injected into other components of your application.
I’ve added a /beans
endpoint to our HomeController
to drive this point home, so to speak
In this case, it takes an options query parameter: q
. If you don’t provide the parameter, it will return all the beans Spring has available.
Try: http localhost:8080/beans
It’s a long list. Let’s look for some of the beans we created in this example:
`http localhost:8080/beans?q=greeting
Notice that there’s no frenchGreetingService
. That’s because of our @ConditionalOnProperty
setting in GreetingServiceConfig
.
We’ve covered a lot of ground in this post. We looked at the history of Dependency Injection, its use in Spring services and components, as well as different approaches including constructor and setter dependency injection.
Comments
Post a Comment