Core Concepts and Practical Usage of Spring Framework 6
Spring is a lightweight, open-source framework widely used in Java enterprise development. It encompasses both a foundational module—Spring Framework—and an extensive ecosystem known as the Spring technology stack.
Spring Framework vs. Spring Ecosystem
Spring Framework is the core library that provides essential features like Inversion of Control (IoC) and Aspect-Oriented Programming (AOP). It serves as the base for other Spring projects such as Spring Boot, Spring MVC, Spring Security, Spring Data, and Spring Cloud.
The broader Spring ecosystem includes all these subprojects, offering comprehensive solutions across cloud-native development, data access, security, microservices, and more.
Key Features of Spring Framework
- Non-invasive: Domain models remain unaffected; only minimal annotations are needed on components.
- Inversion of Control (IoC): Delegates object creation and dependency management to the framework.
- Aspect-Oriented Programming (AOP): Modularizes cross-cutting concerns like logging, transactions, and security.
- Container-based lifecycle management: Manages bean instantiation, configuration, and destruction.
- Component-based architecture: Encourages building applications from reusable, well-defined components.
- Comprehensive integration: Seamlessly works with numerous third-party libraries and frameworks.
Getting Started with Spring 6
Add the following dependencies to your pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Basic Example
Define a simple class:
package com.example;
public class MessageService {
public void printMessage() {
System.out.println("Hello from Spring!");
}
}
Configure it in beans.xml:
<?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 id="messageService" class="com.example.MessageService" />
</beans>
Use it in a test:
package com.example;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MessageServiceTest {
@Test
public void testMessageService() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MessageService service = context.getBean("messageService", MessageService.class);
service.printMessage();
}
}
Dependency Injection (DI)
Spring supports two primary DI styles:
Setter Injection
<bean id="book" class="com.example.Book">
<property name="title" value="Effective Java" />
<property name="author" value="Joshua Bloch" />
</bean>
Constructor Injection
<bean id="book" class="com.example.Book">
<constructor-arg name="title" value="Clean Code" />
<constructor-arg name="author" value="Robert C. Martin" />
</bean>
Special values like null, XML entities (<), and CDATA sections are also supported.
Managing Complex Dependencies
For object-type properties, use:
- External bean reference:
<property name="department" ref="deptBean" /> - Inner bean: Define the dependent bean inline within the property.
- Cascading assignment: Set nested properties like
dept.namedirectly.
Collections (arrays, lists, maps) can be injected using <array>, <list>, and <map> tags.
Utility Schemas and Shortcuts
The util namespace allows defining reusable collections:
<util:list id="courseList">
<value>Java</value>
<value>Spring</value>
</util:list>
The p namespace simplifies property injection:
<bean id="student" class="com.example.Student"
p:name="Alice" p:courses-ref="courseList" />
External Configuration
Load database properties from application.properties:
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.user=root
jdbc.password=secret
jdbc.driver=com.mysql.cj.jdbc.Driver
Reference them in XML:
<context:property-placeholder location="classpath:application.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
</bean>
Bean Scopes and Lifecycle
Beans are singleton by default. Other scopes include prototype, request, session, etc.
Lifecycle callbacks can be defined via init-method and destroy-method. Additionally, implement BeanPostProcessor for custom initialization logic.
FactoryBean
Use FactoryBean to encapsulate complex object creation:
public class CustomObjectFactory implements FactoryBean<MyObject> {
@Override
public MyObject getObject() throws Exception {
return new MyObject(); // complex setup here
}
@Override
public Class<?> getObjectType() {
return MyObject.class;
}
}
Annotation-Based Configuration
Enable component scanning:
<context:component-scan base-package="com.example" />
Use stereotypes like @Component, @Service, @Repository, or @Controller to declare beans.
Inject dependencies with:
@Autowired: By type (supports constructor, field, setter injection).@Qualifier: Resolve ambiguity when multiple beans of the same type exist.@Resource(from Jakarta EE): Prefers name-based injection (byName), falls back to type.
Full Java Configuration
Replace XML with a @Configuration class:
@Configuration
@ComponentScan("com.example")
public class AppConfig {
}
Launch with:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
Aspect-Oriented Programming (AOP)
AOP modularizes cross-cutting concerns. Key concepts:
- Aspect: A class containing advice and pointcuts.
- Join point: A specific point in execution (e.g., method call).
- Pointcut: Expression that matches join points.
- Advice: Action taken at a join point (before, after, around, etc.).
Annotation-Driven AOP
Enable with <aop:aspectj-autoproxy /> and define aspects:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint jp) {
System.out.println("Calling: " + jp.getSignature());
}
}
Reuse pointcuts with @Pointcut:
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void logEntry(JoinPoint jp) { /* ... */ }
Set aspect precedence with @Order.
XML-Based AOP
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut id="businessService"
expression="execution(* com.example.service.*.*(..))" />
<aop:before pointcut-ref="businessService" method="logBefore" />
</aop:aspect>
</aop:config>
Testing with JUnit
JUnit 5
@SpringJUnitConfig(locations = "classpath:beans.xml")
public class ServiceTest {
@Autowired
private MyService service;
@Test
void testOperation() {
assertNotNull(service);
}
}
JUnit 4
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beans.xml")
public class LegacyServiceTest {
@Autowired
private MyService service;
@Test
public void testLegacy() {
// ...
}
}
Transaction Management
Use @Transactional on service methods. Configure a transaction manager:
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
Key attributes:
readOnly: Optimizes read-only operations.timeout: Sets maximum execution time.rollbackFor/noRollbackFor: Customize rollback behavior.isolation: Controls concurrency effects (e.g.,READ_COMMITTED).propagation: Defines how transactions interact (e.g.,REQUIRES_NEW).
Resource Abstraction
Spring’s Resource interface unifies access to files, classpath resources, URLs, etc.:
ClassPathResource: Loads from classpath.FileSystemResource: Accesses local files.UrlResource: Handles HTTP, FTP, file URLs.
Example:
Resource resource = new ClassPathResource("config/app.properties");
InputStream is = resource.getInputStream();
Ahead-of-Time (AOT) Compilation
Spring 6 supports AOT via GraalVM, enabling native image compilation for faster startup and lower memory usage—ideal for cloud-native environments. Unlike JIT (Just-In-Time), AOT compiles bytecode to native machine code during build time.
GraalVM’s Native Image tool produces standalone executables without requiring a JVM at runtime.
Validation Annotations
Common constraints include:
@NotNull,@NotEmpty,@NotBlank@Min,@Max,@Size@Email,@Pattern
These integrate with Spring’s validation infrastructure for robust input checking.