Spring is a lightweight framework that provides a container for managing Inversion of Control (IoC) and Aspect-Oriented Programming (AOP).
Inversion of Control (IoC)
IoC is a design principle where the control over object creation and dependency management is transferred from the application code to an external container. There are two primary methods for achieving this:
- Dependency Lookup (DL): The application code requests dependencies from the container.
- Dependency Injection (DI): The container automatically supplies dependencies to the application components.
Spring implements IoC primarily through Dependency Injection.
Understanding Dependencies
A dependency exists when one class requires the functionality of another class to perform its operations.
Programming to Interfaces: A Recap
When a method can have multiple implementations, programming to an interface is a common practice. This example simulates a user login system, omitting actual database connectivity.
1. Data Access Layer (DAO)
Interface AuthenticationDao:
package com.example.auth.dao;
public interface AuthenticationDao {
boolean authenticate(String username, String password);
}
Concrete implementations:
package com.example.auth.dao.impl;
import com.example.auth.dao.AuthenticationDao;
public class JdbcAuthDao implements AuthenticationDao {
public boolean authenticate(String username, String password) {
System.out.println("Authenticating via JDBC implementation.");
return true;
}
}
package com.example.auth.dao.impl;
import com.example.auth.dao.AuthenticationDao;
public class CustomAuthDao implements AuthenticationDao {
public boolean authenticate(String username, String password) {
System.out.println("Authenticating via custom implementation.");
return true;
}
}
2. Service Layer
package com.example.auth.service;
import com.example.auth.dao.AuthenticationDao;
public class AuthService {
private AuthenticationDao authDao;
// Constructor-based injection
public AuthService(AuthenticationDao authDao) {
this.authDao = authDao;
}
// Setter-based injection
public void setAuthDao(AuthenticationDao authDao) {
this.authDao = authDao;
}
public boolean login(String username, String password) {
return authDao.authenticate(username, password);
}
}
3. Test Class (Without Spring)
package com.example.auth.test;
import org.junit.Test;
import com.example.auth.dao.impl.JdbcAuthDao;
import com.example.auth.service.AuthService;
public class LoginTest {
@Test
public void testLoginWithoutContainer() {
// Using constructor
// AuthService service = new AuthService(new CustomAuthDao());
// Using setter
AuthService service = new AuthService();
service.setAuthDao(new JdbcAuthDao());
service.login("user", "pass");
}
}
In compiled languages like Java, changing implementation details requires code modification and recompilation. Using configuration files for dependency management avoids this.
Dependency Injection
Dependency Injection is the process where an object receives its dependencies from an external source (the container) rather than creating them internally. This is a key mechanism for achieving looce coupling, as dependencies are defined externally, not hardcoded.
Spring's IoC Container
Spring's IoC container manages component dependencies through DI, facilitating loose coupling. Common versions in use are 3.2 and 4.2.
1. Required Libraries
For a basic Java project, include the core Spring libraries, JUnit (e.g., junit-4.12.jar and hamcrest-core-1.3.jar), and a logging framework like Log4j (log4j-1.2.17.jar).
2. Configuration File applicationContext.xml
The container configuration is defined in an XML file. The underlying mechanism for IoC is reflection.
Example Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Bean definitions -->
<bean id="jdbcAuthDao" class="com.example.auth.dao.impl.JdbcAuthDao"/>
<bean id="customAuthDao" class="com.example.auth.dao.impl.CustomAuthDao"/>
<bean id="authService" class="com.example.auth.service.AuthService">
<!-- Constructor Injection -->
<constructor-arg ref="customAuthDao"/>
<!-- Setter Injection (property name matches setter method) -->
<!-- <property name="authDao" ref="jdbcAuthDao"/> -->
</bean>
</beans>
3. Updated Test Class (Using Spring Container)
package com.example.auth.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.auth.service.AuthService;
public class LoginTest {
@Test
public void testLoginWithSpring() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AuthService service = (AuthService) context.getBean("authService");
service.login("user", "pass");
}
}