Introduction to Spring and Dependency Injection (JSR 330)
The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform. A key element of Spring is infrastructural support at the application level: Spring focuses on the “plumbing” of enterprise applications so that teams can focus on application-level business logic, without unnecessary ties to specific deployment environments.
Introduction
Spring Framework is an implementation of the Inversion of Control (IoC) principle. IoC is
also known as Dependency Injection
(DI). It is a process whereby objects define their dependencies, that is, the
other objects they work with, only through constructor arguments, arguments to a factory method, or properties that
are set on the object instance after it is constructed or returned from a factory method.
The objects that form the backbone of your application and that are managed by the Spring IoC container are called
beans
. Abean
is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. These beans are created with the configuration metadata that you supply to the container, for example, in the form of@Bean
definitions.
The container then injects those dependencies when it creates the bean
. This process is fundamentally the inverse, hence the name
Inversion of Control
(IoC), of the bean
itself controlling the instantiation or location of its dependencies by using direct
construction of classes, or a mechanism such as the Service Locator pattern.
Prerequisites
Project Structure
To demonstrate Dependency Injection, we will create a project that showcases the concept. This project will have the following directory structure:
.
|__src/
| |__main/
| | |__java/
| | | |__com/
| | | | |__juliuskrah/
| | | | | |__gs/
| | | | | | |__Application.java
| | | | | | |__MessagePrinter.java
| | | | | | |__MessageService.java
|__pom.xml
You can also find the complete sample in the github repository.
Dependency Injection
Every java based application has a few objects that work together to present what the end-user sees as a working application. When writing a complex Java application, application classes should be as independent as possible of other Java classes to increase the possibility to reuse these classes and to test them independently of other classes while doing unit testing. Dependency Injection helps in gluing these classes together and at the same time keeping them independent.
Consider you have an application which has a printing component and you want to provide messages to be printed. Your standard code would look something like this:
file:
src/main/java/com/juliuskrah/gs/MessagePrinter.java
file:
src/main/java/com/juliuskrah/gs/MessageService.java
What we’ve done here is create a dependency between the MessagePrinter
and the MessageService
. In an inversion of control
scenario we would instead do something like this:
file:
src/main/java/com/juliuskrah/gs/MessagePrinter.java
Here MessagePrinter
should not worry about MessageService
implementation. The MessageService
will be implemented
independently and will be provided to MessagePrinter
at the time of MessagePrinter
instantiation and this entire procedure
is controlled by the Spring Framework.
At this point we will declare our dependencies on the Spring Framework. This a Maven based project and we
will use a pom.xml
to manage our dependencies:
file:
pom.xml
In this post we will be using the Standard JSR 330
(found in javax.inject
package)
annotations to demonstrate DI.
Adding Spring and JSR 330 Annotations
Before we dive into the cool things, let us look at the various options for injection in Spring:
- Constructor-based dependency injection
- Setter-based dependency injection
- Field-based dependency injection
We will demonstrate all three with code examples.
The above MessagePrinter
class can be rewritten to add Spring support using constructor-based injection:
file:
src/main/java/com/juliuskrah/gs/MessagePrinter.java
Notice the introduction of two new annotations; @Component
at the class or type level and @Inject
at the constructor level.
NOTE
Since Spring 4.3 it is no longer necessary to specify an injection point if the target bean only defines one constructor. In our case, the@Inject
annotation is not necessary as we have only one constructor.
The example above shows the basic concept of dependency injection, the MessagePrinter
is decoupled from the
MessageService
implementation, with Spring Framework wiring everything together.
@Component
indicates that an annotated class is a “component”. Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.
@Inject
identifies injectable constructors, methods, and fields. May apply to static as well as instance members. An injectable member may have any access modifier (private, package-private, protected, public). Constructors are injected first, followed by fields, and then methods. Fields and methods in superclasses are injected before those in subclasses. Ordering of injection among fields and among methods in the same class is not specified.
The MessagePrinter
class can be rewritten to add Spring support using setter-based injection:
file:
src/main/java/com/juliuskrah/gs/MessagePrinter.java
The MessagePrinter
class can be rewritten to add Spring support using field-based injection:
file:
src/main/java/com/juliuskrah/gs/MessagePrinter.java
Now that we have most of our application configuration metadata out of the way, we can create our configuration class:
file:
src/main/java/com/juliuskrah/gs/Application.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@ComponentScan
public class Application {
@Bean
public MessageService messageService() {
return () -> "Hello World!";
}
public static void main(String... args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
MessagePrinter messagePrinter = applicationContext.getBean(MessagePrinter.class);
messagePrinter.printMessage();
}
}
From the above snippet on lines 1, 2 and 5 we see three new annotations @Configuration
, @ComponentScan
, and
@Bean
respectively.
@Configuration
indicates that a class declares one or more@Bean
methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
@ComponentScan
configures component scanning directives for use with@Configuration
classes.
@Bean
indicates that a method produces a bean to be managed by the Spring container.
Before we go any further, a few more notes on @ComponentScan
as it applies in this guide and Spring in general.
- Either
basePackageClasses
orbasePackages
(or its alias value) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation (in our casecom.juliuskrah.gs
). What this means is, you can specify a package that Spring needs to scan forComponent
s. - The
MessagePrinter
class is annotated with@Component
which makes it eligible for discovery by the@ComponentScan
as it is also located in thecom.juliuskrah.gs
package. -
MessagePrinter
now scanned by the Spring container can now process@Inject
annotations.
On line 11 of Application.java
we have the ApplicationContext
.
The
ApplicationContext
is the central interface within a Spring application for providing configuration information to the application. It is read-only at run time, but can be reloaded if necessary and supported by the application. A number of classes implement theApplicationContext
interface (e.g.AnnotationConfigApplicationContext
), allowing for a variety of configuration options and types of applications.
The ApplicationContext provides:
- Bean factory methods for accessing application components.
- The ability to load file resources in a generic fashion.
- The ability to publish events to registered listeners.
- The ability to resolve messages to support internationalization.
- Inheritance from a parent context.
On line 12, we call getBean()
and pass MessagePrinter
as argument. This is possible because, the @ComponentScan
has detected the @Component
annotation on MessagePrinter
and registered it as a Bean
. The Spring container then
processes the injected constructor argument MessageService
and looks for a Bean
of type MessageService
. This
Bean
is defined in the Application
class implemented to return "Hello World!"
for the getMessage()
method.
When this project is run, it will print "Hello World!"
in the console.
Conclusion
In this post we learnt the basics of the Spring Framework and Dependency Injection using code samples. We also talked about the types of injection in Spring. You can mix, Constructor-based, Setter-based and Field-based DI but it is a good rule of thumb to use constructor arguments for mandatory dependencies and setters or field for optional dependencies. You can find the source to this guide in the github repository. Until the next post, keep doing cool things .