Design Patterns in the Spring Framework: A Comprehensive Overview
This article provides a detailed overview of how the Spring framework implements classic design patterns—including Simple Factory, Factory Method, Singleton, Adapter, Decorator, Proxy, Observer, Strategy, and Template Method—illustrated with code snippets and explanations of their roles in Spring's dependency injection and bean lifecycle management.
1. Simple Factory (BeanFactory)
Spring's BeanFactory embodies the simple factory pattern: a unique identifier is used to obtain a bean instance, with creation timing determined by the specific situation.
Implementation relies on various Aware interfaces (e.g., BeanFactoryAware) and lifecycle interfaces such as BeanPostProcessor, InitializingBean, and DisposableBean.
Bean definitions are read from XML, converted into BeanDefinition objects, and registered in a ConcurrentHashMap within the BeanFactory.
Extension points like BeanFactoryPostProcessor allow custom code insertion (e.g., PropertyPlaceholderConfigurer).
2. Factory Method (FactoryBean)
Beans implementing the FactoryBean interface act as factories; when getBean() is called, Spring invokes the bean's getObject() method and returns its result rather than the factory itself.
Typical example: the integration of Spring with MyBatis.
3. Singleton Pattern
Spring's default bean scope is singleton. Bean creation occurs in AbstractBeanFactory.getBean(), which calls getSingleton() implementing double‑checked locking.
public Object getSingleton(String beanName) {
// allow early reference
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}The singleton pattern ensures a single instance with a global access point, which Spring provides via the BeanFactory.
4. Adapter Pattern (HandlerAdapter)
Spring MVC uses HandlerAdapter to invoke different handlers based on mapping rules, allowing easy extension by adding new handlers and corresponding adapters.
5. Decorator Pattern
Spring employs wrapper/decorator classes (e.g., classes with names containing "Wrapper" or "Decorator") to dynamically add responsibilities to objects without creating subclasses.
6. Proxy Pattern (AOP)
AOP in Spring is built on dynamic proxying; the container weaves aspects at runtime, creating proxy objects that delegate to the target bean.
7. Observer Pattern (Application Events)
Spring's event mechanism follows the observer pattern. Core components include ApplicationEvent, ApplicationListener, ApplicationEventPublisher, and ApplicationEventMulticaster.
public abstract class ApplicationEvent extends EventObject {
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() { return this.timestamp; }
} public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}8. Strategy Pattern (Resource Interface)
Spring's Resource abstraction provides a strategy for accessing various underlying resources (e.g., UrlResource, ClassPathResource, FileSystemResource, etc.).
9. Template Method Pattern (JdbcTemplate)
Spring's JdbcTemplate implements the template method pattern combined with callbacks to separate invariant workflow from variable database operations.
public abstract class JdbcTemplate {
public final Object execute(String sql) {
Connection con = null;
Statement stmt = null;
try {
con = getConnection();
stmt = con.createStatement();
Object ret = executeWithStatement(stmt, sql);
return ret;
} finally {
closeStatement(stmt);
releaseConnection(con);
}
}
protected abstract Object executeWithStatement(Statement stmt, String sql);
}Clients provide a StatementCallback implementation to inject custom logic.
public interface StatementCallback {
Object doWithStatement(Statement stmt);
}Usage example:
JdbcTemplate jdbcTemplate = ...;
final String sql = ...;
StatementCallback callback = new StatementCallback() {
public Object doWithStatement(Statement stmt) {
// custom logic
return ...;
}
};
jdbcTemplate.execute(callback);Overall, Spring leverages these classic design patterns to achieve loose coupling, extensibility, and consistent resource management across its core container and various modules.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
