Unlock Spring’s Power: 9 Essential Design Patterns for Backend Engineers
This article explains how Spring implements nine core design patterns—including Simple Factory, Factory Method, Singleton, Adapter, Decorator, Proxy, Observer, Strategy, and Template Method—detailing their implementation, underlying principles, and practical significance for Java backend development.
1. Simple Factory (not one of the 23 GoF patterns)
Implementation: BeanFactory. In Spring, BeanFactory is an embodiment of the simple factory pattern, obtaining a bean by a unique identifier.
Essence: A factory class decides which product class to create based on the supplied parameter.
Bean container startup phase
Read bean XML configuration, convert each bean element into a BeanDefinition object.
Register BeanDefinition objects into BeanFactory via BeanDefinitionRegistry, stored in a ConcurrentHashMap.
After registration, developers can hook custom code by implementing BeanFactoryPostProcessor (e.g., PropertyPlaceholderConfigurer).
Bean instantiation phase
Beans are instantiated via reflection or CGLIB. Spring provides several extension points:
Aware interfaces (e.g., BeanFactoryAware) – Spring injects the corresponding BeanFactory instance.
BeanPostProcessor – Spring calls its methods during bean instantiation.
InitializingBean – Spring invokes its callback after property population.
DisposableBean – Spring invokes its callback when the bean is destroyed.
Design significance
Loose coupling: dependencies are injected by the BeanFactory instead of hard‑coded, introducing a third party that resolves bean relationships.
Additional bean processing: By implementing the above interfaces, developers can add custom behavior during a bean’s lifecycle.
2. Factory Method
Implementation: FactoryBean interface.
Principle: A bean that implements FactoryBean is itself a factory; when getBean() is called, Spring invokes its getObject() method and returns the produced object rather than the FactoryBean instance.
Example
Typical use case is the integration of Spring with MyBatis.
Code illustration:
Explanation: The bean implements FactoryBean, so the returned object is the result of SqlSessionFactoryBean.getObject(), not the FactoryBean itself.
3. Singleton Pattern
Spring’s default bean scope is singleton; a single instance is created and shared.
Bean creation occurs in AbstractBeanFactory.getBean(), which delegates to getSingleton(). The method uses double‑checked locking and early reference handling to ensure thread‑safe singleton creation.
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 implements the singleton pattern with a global access point (BeanFactory) but does not enforce a single instance at the constructor level because it manages arbitrary Java objects.
4. Adapter Pattern
Implementation: Spring MVC’s HandlerAdapter.
Principle: HandlerAdapter selects and invokes the appropriate handler based on the handler’s type.
Process: DispatcherServlet obtains a handler from HandlerMapping, forwards the request to the matching HandlerAdapter, which executes the handler and returns a ModelAndView to the servlet.
Significance: Adding a new handler only requires a corresponding HandlerAdapter, simplifying extension of Spring MVC.
5. Decorator Pattern
Implementation: Classes whose names contain Wrapper or Decorator (e.g., HttpMessageConverter wrappers).
Essence: Dynamically adds responsibilities to an object, offering more flexibility than subclassing.
6. Proxy Pattern
Implementation: AOP is built on dynamic proxy mechanisms.
Dynamic proxy: Created in memory without manually writing proxy classes.
Static proxy: Requires a manually written proxy class that delegates to the target.
Principle: At runtime, Spring weaves aspects by creating a proxy that delegates to the target bean.
7. Observer Pattern
Implementation: Spring’s event‑driven model.
Core components: ApplicationEvent (event source), ApplicationListener (listener), and ApplicationEventMulticaster (dispatcher).
ApplicationEvent extends EventObject; listeners implement ApplicationListener<E extends ApplicationEvent> and define onApplicationEvent(E event).
public abstract class ApplicationEvent extends EventObject {
private final long timestamp = System.currentTimeMillis();
public long getTimestamp() { return this.timestamp; }
}
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}ApplicationContext implements ApplicationEventPublisher and uses an ApplicationEventMulticaster to broadcast events to all registered listeners.
public interface ApplicationEventPublisher {
void publishEvent(ApplicationEvent event);
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
private ApplicationEventMulticaster applicationEventMulticaster;
protected void registerListeners() {
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String name : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(name);
}
}
}8. Strategy (Resource) Pattern
Implementation: Spring’s Resource abstraction.
Purpose: Provide a uniform way to access various underlying resources (URL, classpath, file system, servlet context, input stream, byte array). getInputStream() – opens a new InputStream. exists() – checks resource existence. isOpen() – indicates whether the resource is already open. getDescription() – returns a descriptive string. getFile() – returns a File object. getURL() – returns a URL object.
Concrete implementations include UrlResource, ClassPathResource, FileSystemResource, ServletContextResource, InputStreamResource, and ByteArrayResource.
9. Template Method Pattern
Classic definition: The abstract superclass defines the algorithm skeleton; subclasses implement specific steps.
In Spring, the pattern is combined with callbacks. Example: JdbcTemplate defines execute() that manages connection and statement lifecycle, while the actual SQL execution is delegated to a StatementCallback.
public abstract class JdbcTemplate {
public final Object execute(String sql) {
Connection con = null;
Statement stmt = null;
try {
con = getConnection();
stmt = con.createStatement();
return executeWithStatement(stmt, sql);
} finally {
closeStatement(stmt);
releaseConnection(con);
}
}
protected abstract Object executeWithStatement(Statement stmt, String sql);
}
public interface StatementCallback {
Object doWithStatement(Statement stmt);
}
public final Object execute(StatementCallback callback) {
Connection con = null;
Statement stmt = null;
try {
con = getConnection();
stmt = con.createStatement();
return callback.doWithStatement(stmt);
} finally {
closeStatement(stmt);
releaseConnection(con);
}
}Using callbacks avoids the need for many concrete subclasses and keeps the stable resource‑handling code in the template.
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.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
