Unlocking Spring: 9 Essential Design Patterns Every Backend Developer Should Know
This article explains how Spring implements nine classic design patterns—including Simple Factory, Factory Method, Singleton, Adapter, Decorator, Proxy, Observer, Strategy, and Template Method—detailing their implementation mechanisms, core principles, key Spring interfaces, and code examples for each pattern.
Spring Design Patterns Overview
1. Simple Factory (BeanFactory)
Implementation: BeanFactory – Spring’s simple‑factory embodiment that returns a bean based on a unique identifier.
Principle: a factory class decides which product class to instantiate according to supplied parameters.
Key mechanisms in Spring:
Various *Aware interfaces (e.g., BeanFactoryAware) inject the corresponding BeanFactory instance during bean creation.
BeanPostProcessor allows custom processing after bean instantiation.
InitializingBean and DisposableBean provide lifecycle callbacks.
XML bean definitions are parsed into BeanDefinition objects and registered in the BeanFactory’s internal map.
BeanFactoryPostProcessor (e.g., PropertyPlaceholderConfigurer) can modify definitions before bean instantiation.
Bean container startup phases and instantiation phase (reflection or CGLIB) expose many extension points.
Design significance: achieves loose coupling by delegating dependency resolution to the BeanFactory and enables extra bean processing via Spring‑provided interfaces.
2. Factory Method
Implementation: FactoryBean interface.
Principle: a bean that implements FactoryBean is itself a factory; Spring calls its getObject() method and registers the returned object as the bean instance.
Typical example: MyBatis’s SqlSessionFactoryBean – the bean exposed to the container is the result of getObject(), not the factory itself.
3. Singleton
Spring’s default bean scope is singleton; each bean is created once and shared.
Dependency injection occurs in AbstractBeanFactory#getBean, which delegates to getSingleton using double‑checked locking.
Core code:
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);
}Summary: Spring guarantees a single instance per bean and provides global access through the BeanFactory, without enforcing singleton at the constructor level.
4. Adapter
Implementation: Spring MVC’s HandlerAdapter .
Principle: DispatcherServlet obtains a handler from HandlerMapping, then forwards the request to the appropriate HandlerAdapter, which executes the handler and returns a ModelAndView to the servlet.
Significance: Adding new controller types only requires a new handler and a matching adapter, making controller extension straightforward.
5. Decorator
Implementation: Classes whose names contain Wrapper or Decorator.
Essence: dynamically attach additional responsibilities to an object, offering more flexibility than subclassing.
6. Proxy
Implementation: Spring AOP’s dynamic proxy mechanism.
Dynamic proxy: created at runtime without manual proxy class code.
Static proxy: requires a manually written proxy class.
Principle: when an aspect is woven, Spring creates a proxy object that delegates to the target bean while applying advice.
7. Observer
Implementation: Spring’s event‑driven model using ApplicationEvent and ApplicationListener .
Components: event source, event object, listener.
Key interfaces and classes: ApplicationEvent – base class for all events (extends EventObject). ApplicationListener<E extends ApplicationEvent> – listener interface with onApplicationEvent(E event). ApplicationEventPublisher – publishes events via publishEvent. AbstractApplicationContext – registers listeners and delegates event multicasting. ApplicationEventMulticaster – broadcasts events to all registered listeners.
8. Strategy (Resource)
Implementation: Spring’s Resource interface and its concrete strategies. UrlResource – accesses network resources. ClassPathResource – accesses resources on the classpath. FileSystemResource – accesses file‑system resources. ServletContextResource – accesses resources relative to the servlet context. InputStreamResource and ByteArrayResource – stream‑ and byte‑array‑based resources.
Typical methods: getInputStream() – opens a fresh InputStream for the resource. exists() – checks resource existence. isOpen() – indicates if the resource is already opened. getDescription(), getFile(), getURL() – provide descriptive or low‑level access.
Purpose: provide a uniform API for accessing diverse underlying resources.
9. Template Method
Definition: a superclass defines the algorithm skeleton (concrete steps) while allowing subclasses to override specific steps (abstract or hook methods).
Spring’s usage: combines template method with callbacks, e.g., JdbcTemplate.
Core JdbcTemplate class defines execute(String sql) that manages connection, statement, and resource cleanup.
Subclass‑specific work is delegated to executeWithStatement (abstract) or to a StatementCallback implementation.
Code example of the template method:
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;
} catch (SQLException e) {
// handle
} finally {
closeStatement(stmt);
releaseConnection(con);
}
}
protected abstract Object executeWithStatement(Statement stmt, String sql);
}Callback interface: <code>public interface StatementCallback { Object doWithStatement(Statement stmt); } </code> Using the callback: <code>JdbcTemplate jdbcTemplate = ...; final String sql = ...; StatementCallback callback = new StatementCallback() { public Object doWithStatement(Statement stmt) { // custom logic return ...; } }; jdbcTemplate.execute(callback); </code> Rationale: avoids subclass explosion by passing behavior as a callback object while still leveraging the stable resource‑management logic of JdbcTemplate .
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
