Design Patterns Used by Spring and Modern Frameworks
Design Patterns in Spring and Modern Frameworks
Design patterns are not just textbook theory. The frameworks you use every day are built on top of them. Understanding which patterns power Spring, Jakarta EE, and modern front-end frameworks helps you use those frameworks more effectively and make better architectural decisions.
Singleton in the Spring Container
By default, every Spring bean has singleton scope. The Spring IoC container creates exactly one instance per bean definition and reuses it for every injection point. This is not the GoF Singleton pattern - there is no private constructor and no static getInstance(). Instead the container itself acts as the factory and registry, enforcing the single instance.
+-----------------------------+
| Spring IoC Container |
| |
| Bean: "userService" |
| Scope: singleton (default) |
| Instance: one shared |
+-----------------------------+
^ ^
| |
@Autowired @Autowired
Controller AnotherService
Both the controller and AnotherService receive the same UserService instance. If you mark a bean as @Scope("prototype"), the container creates a new instance each time - demonstrating that the pattern is a deliberate choice, not an accident.
Factory and FactoryBean
Spring's BeanFactory and ApplicationContext are textbook Factory implementations. They read bean definitions and create objects without the client knowing the concrete class. For complex creation logic, Spring provides the FactoryBean interface.
public class ConnectionFactory implements FactoryBean<Connection> {
public Connection getObject() {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
return conn;
}
public Class<?> getObjectType() {
return Connection.class;
}
public boolean isSingleton() {
return false;
}
}
When Spring encounters a FactoryBean, it calls getObject() to produce the bean instead of instantiating the factory class directly. The client asks for a Connection and never sees the factory or the connection creation logic.
Proxy for AOP
Spring AOP uses the Proxy pattern extensively. When you annotate a method with @Transactional, Spring creates a proxy around your bean. The proxy intercepts calls, starts a transaction, delegates to the real method, and commits or rolls back. Your code remains clean of transaction boilerplate.
public class OrderServiceProxy extends OrderService {
private TransactionManager txManager;
@Override
public void placeOrder(Order order) {
txManager.beginTransaction();
try {
super.placeOrder(order);
txManager.commit();
} catch (Exception e) {
txManager.rollback();
throw e;
}
}
}
The proxy extends or wraps the target class and adds cross-cutting concerns like transactions, security checks, and caching. The rest of the application injects OrderService without knowing a proxy is in the way.
Template Method in Spring
The Template Method pattern defines the skeleton of an algorithm in a base class and lets subclasses override specific steps. Spring's JdbcTemplate and RestTemplate are classic examples. The template handles connection setup, error handling, and resource cleanup. You supply only the SQL query or the request details.
Observer in Application Events
Spring's ApplicationEventPublisher and @EventListener implement the Observer pattern. When an order is placed, the publisher fires an OrderPlacedEvent. Multiple listeners - one for email, one for inventory, one for analytics - respond independently. Adding a new listener does not change the order service at all.
Real-World Scenario
A modern Spring Boot microservice uses Singleton beans for stateless services, Factory for externalised configuration, Proxy for AOP, Template Method for HTTP calls via RestTemplate, and Observer for async event handling. These patterns are not separate decisions but an integrated architecture that the framework bakes in.
Key Points
- Spring uses the Singleton pattern via bean scopes - one instance per definition by default.
- BeanFactory and FactoryBean implement the Factory pattern, hiding complex object creation.
- Spring AOP relies on the Proxy pattern to inject cross-cutting concerns transparently.
- Template Method powers JdbcTemplate and RestTemplate, separating infrastructure from business logic.
- ApplicationEventPublisher uses the Observer pattern to decouple event producers from consumers.