Spring
is one of the most popular application development frameworks to build Java applications. It was developed by Rod Johnson and was first released under the Apache 2.0 license in June 2003.
Spring is a lightweight open-source framework. Spring provides you different modules such as Spring Core, Spring AOP, Spring MVC, Spring ORM etc. for different purposes. But if you want, you can use any one of the modules selectively based on your need. No need to download all the spring modules to use one of them. Also to use a module, you have to write very less code and configurations. For those reasons, spring is called lightweight.
Spring Modules
The Spring Framework provides about 20 modules. Based on their primary features, those modules can be put together in different groups, such as –
- Core Container : Mainly provides IOC and Dependency Injection features.
- Data Access : Provide support to interact with the database.
- Web : Provide support to build web applications.
- AOP : Provides support to implement aspect-oriented programming.
- Test : Provides support to test with JUnit and TestNG.
For individual modules, Spring provides individual JAR
files that contain required code for that module. Before going into much detail, let’s first understand a few basic concepts.
Inversion of Control (IOC) and Dependency Injection (DI)
Let’s understand the concept with an example. Suppose we have a Vehicle
class that depends on the Engine
class to start.
public interface Engine {
void start();
}
public class EngineImpl implements Engine {
public void start() {
System.out.println("Starting engine...");
}
}
public class Vehicle {
public void start() {
Engine engine = new EngineImpl();
engine.start();
}
}
As we can see, Vehicle
class depends on Engine
class and to get the dependency Vehicle
class instantiated EngineImpl
using new
keyword.
This approach has few problems. First of all, the Vehicle class is more interested in calling the start()
method of the Engine
interface. But it is not interested in owning the responsibility to create a new instance of the implementation of that interface. But we have given that responsibility to the Vehicle
class.
Second problem is, if you want to use a separate implementation of the Engine
interface, you have to change the code of the Vehicle
class as Vehicle class is responsible for creating an instance of EngineImpl
class.
Probably a better approach would be to invert the responsibility of instantiation from Vehicle
class to some factory class.
public class ObjectFactory {
public static Engine getEngine() {
return new EngineImpl();
}
}
public class Vehicle {
public void start() {
Engine engine = ObjectFactory.getEngine();
engine.start();
}
}
Here we have inverted the instance creation responsibility from Vehicle
to ObjectFactory
class. Now, if you want to use some other implementation of the Engine
interface, there is no need to modify the Vehicle
class. Just return an instance of that new type from ObjectFactory
.
This is better than earlier. But the Vehicle class is still responsible for making a call to the ObjectFactory.getEngine()
method to get the dependency. So, the Vehicle
class is now tightly coupled with the ObjectFactory
class.
Vehicle class doesn’t care about how the dependency is created and should not own the responsibility to make a call to get that dependency.
The best approach would be to give control to an outside service to take care of creating and supplying the dependencies. All the Vehicle class should do is declare what it needs. This is what inversion of control is. Inversion of Control (IoC)
means that objects do not create other objects on which they depend to do their work. Instead, they get the dependencies from an outside service.
The technique to provide or inject the dependencies to dependent objects is called dependency injection (DI)
. The Spring framework utilizes DI to achieve IoC.
Benefit of declare dependency
There are some clear benefit of using DI –
- DI reduces the amount of code that we have to write to instantiate dependencies and inject them where required.
- The configuration to mark injectable classes and where they should be injected is very simple. We will see this in a moment.
- We can swap between different implementations of a dependency very easily.
Declare Components
If you want some of your classes to be managed by Spring, you have to mark them with the appropriate stereotype annotation. The stereotype annotations can also specify a name for that bean. Please remember, we use the term bean
to refer to any component managed by the Spring container including their dependencies and their life cycles. The stereotype annotations are –
- @Component : A spring managed class.
- @Service : A spring managed class that contains business logic.
- @Repository : A spring managed class that interacts with databases.
- @Controller : A spring managed class that is used as a front controller in a web application.
Actually Service, Repository, Controller
can be considered as a special type of Component
. To verify this let’s look at the source code of @Service
–
@Component
public @interface Service {
...
}
You see, it is annotated with @Component
annotation.
In our example, you can use appropriate stereotype annotations on the implementation classes.
@Component
public class EngineImpl implements Engine {
public void start() {
System.out.println("Starting engine...");
}
}
@Service
public class Vehicle {
private Engine engine; // dependency
public void start() {
engine.start();
}
}
Declare dependency
In Spring, you can define the dependency configuration in multiple ways like using annotation, using XML file or using Java configuration class. In most of our examples we will use annotation. To define the dependency we can use @Autowired
annotation.
@Service
public class Vehicle {
@Autowired
private Engine engine;
public void start() {
engine.start();
}
}
The @Autowired
annotation tells Spring to search for a class which implements the declared interface and inject an instance of that class. You can use @Autowired
annotation on constructor and setter as well. We will discuss this in detail separately.
Specify package to scan
We have annotated our classes to use them as Spring beans. Spring will manage those classes and inject the dependencies where required. But where (in which package) should Spring search for those annotated classes? To make Spring aware about that location, we use @ComponentScan
annotation and specify the package name. Spring will scan for the annotated classes under the specified package and all of its sub-packages. Please remember, we have to use @ComponentScan
annotation along with the @Configuration
annotation. Consider our package structure is like below –
So, we can specify the base package as com.kcs
.
@ComponentScan(basePackages = {"com.kcs"})
@Configuration
public class AppConfig {
}
If we use @ComponentScan
without specifying basePackages
, Spring will scan the current package and all of its sub-packages. For example, if our AppCofig.java
is present in the com.kcs
package, then @ComponentScan
and @ComponentScan(basePackages = {"com.kcs"})
are the same.
IoC container
For dependency injection, Spring provides an IoC container
that provides you instances of your classes with all the dependencies injected into them. Spring also manages their entire life cycle. So, when you get an instance of Vehicle class, an implementation of Engine interface will be assigned to the engine reference variable.
Let’s see this in action. To build our application, we will use –
- Maven as a dependency management tool
- Eclipse as IDE
- JDK 8
- Spring 5
First create a maven project and declare dependency for spring-context
module.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.21</version>
</dependency>
The required annotation based configuration we have already discussed. Now create a class with the main method and inside the main method, get an instance of the Vehicle class and then call the start() method.
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Vehicle vehicle = ctx.getBean(Vehicle.class);
vehicle.start();
}
}
We will discuss ApplicationContext
in detail separately. For now if you try as shown above, it will print –
Starting engine…
You see, no need to create an instance of EngineImpl manually and assign that to the engine reference variable. All you have done is specified a few configurations and the rest is take care by Spring.
That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.
One thought on “Introduction to Spring”
Comments are closed.