Spring is a comprehensive, open-source Java framework originally created by Rod Johnson in 2003. It is designed to simplify enterprise application development by providing a layered architecture. Spring is often described as a "one-stop-shop" (full-stack) framework because it offers solutions for various layers of an application, including the web layer (Spring MVC), the bussiness layer (IoC/AOP), and the persistence layer (JDBC Template and ORM integration).
Core Concepts: IoC and AOP
The framework revolves around two fundamental pillars:
- Inversion of Control (IoC): This shifts the responsibility of object creation and lifecycle management from the application code to the Spring container. Instead of manually instantiating objects using the
newkeyword, developers configure Spring to manage them. - Aspect-Oriented Programming (AOP): This allows for the separation of cross-cutting concerns (such as logging, security, and transaction management) from the main business logic, leading to cleaner and more maintainable code. Benefits of Using Spring
- Decoupling: By managing object dependencies, Spring reduces the tight coupling between components.
- Simplified Testing: Spring's support for JUnit allows for easy testing of components through dependency injection.
- Declarative Transactions: Developers can manage transactions via configuration rather than complex manual coding.
- Rich Integration: Spring seamlessly integrates with popular frameworks like Hibernate, MyBatis, and Quartz.
- API Abstraction: It provides a simplified wrapper around difficult Java EE APIs, such as JDBC, JavaMail, and Remote Method Invocation (RMI).
The Mechanics of IoC and Decoupling
To understand IoC, consider the evolution of component interaction. In traditional development, a service might directly instantiate its dependencies:
// High coupling example
public class OrderService {
private InventoryRepo repo = new MySQLInventoryRepo();
public void process() {
repo.updateStock();
}
}
In this scenario, OrderService is tightly coupled to MySQLInventoryRepo. Using a Factory pattern mitigates this slightly, but the service still depends on the Factory. Spring's IoC container uses XML configuration (or annotations) and reflection to solve this.
Reflection-based IoC Implementation: Spring reads a configuration file, identifies the class path, and uses reflection to instantiate the object:
<!-- Spring XML Configuration -->
<bean id="inventoryRepo" class="com.example.repo.impl.MySQLInventoryRepo" />
// Internal logic simplified
String className = getAttribute("class"); // "com.example.repo.impl.MySQLInventoryRepo"
Class<?> clazz = Class.forName(className);
Object instance = clazz.getDeclaredConstructor().newInstance();
Bean Management and Scopes
A "Bean" is simply an object managed by the Spring IoC container. There are three primary ways to instantiate beans:
1. Default Constructor
The container calls the no-argument constructor of the class.
<bean id="dataService" class="com.tech.service.DataService" />
2. Static Factory Method
The container calls a static method on a factory class to obtain the instance.
<bean id="clientService" class="com.tech.factory.ClientFactory" factory-method="createInstance" />
3. Instance Factory Method
The container calls a non-static method on an existing bean to create another bean.
<bean id="serviceFactory" class="com.tech.factory.ServiceFactory" />
<bean id="authService" factory-bean="serviceFactory" factory-method="generateAuthService" />
Bean Scopes
Spring supports several scope that define the lifecycle of a bean:
- singleton (Default): Only one instance per Spring IoC container.
- prototype: A new instance is created every time the bean is requested.
- request/session: Specific to web-aware applications, creating instances per HTTP request or session.
Dependency Injection (DI)
Dependency Injection is the process of providing the dependencies of an object at runtime. Spring primarily supports Setter Injection and Constructor Injection.
Constructor Injection
public class TaskManager {
private String taskName;
public TaskManager(String taskName) {
this.taskName = taskName;
}
}
<!-- XML Configuration -->
<bean id="taskManager" class="com.tech.domain.TaskManager">
<constructor-arg name="taskName" value="Daily Cleanup" />
</bean>
Setter Injection
public class NotificationService {
private String protocol;
public void setProtocol(String protocol) {
this.protocol = protocol;
}
}
<!-- XML Configuration -->
<bean id="notifier" class="com.tech.service.NotificationService">
<property name="protocol" value="HTTPS" />
</bean>
Injecting Complex Types and Collections
Spring allows for the injection of sophisticated data structures like Lists, Sets, and Maps directly via XML.
<bean id="configProvider" class="com.tech.util.ConfigProvider">
<!-- List Injection -->
<property name="serverList">
<list>
<value>192.168.1.1</value>
<value>192.168.1.2</value>
</list>
</property>
<!-- Map Injection -->
<property name="settings">
<map>
<entry key="timeout" value="5000" />
<entry key="retryLimit" value="3" />
</map>
</property>
</bean>
Spring in Web Applications
To integrate Spring into a web project, the container needs to start when the server (e.g., Tomcat) starts. This is achieved using a ServletContextListener. The listener monitors the ServletContext initialization, triggers the creation of the ApplicationContext, and stores it within the ServletContext for global access. This ensures that all managed bean are available as soon as the web application is live.