Backend Development 15 min read

Summary of Design Patterns Used in the Spring Framework

This article provides a comprehensive overview of how Spring implements classic design patterns such as Simple Factory, Factory Method, Singleton, Adapter, Decorator, Proxy, Observer, and Template Method, explaining their implementation details, underlying principles, and practical code examples within the Spring container.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Summary of Design Patterns Used in the Spring Framework

Spring leverages a variety of classic design patterns to achieve loose coupling, extensibility, and modularity in its IoC container and MVC framework.

1. Simple Factory (BeanFactory)

BeanFactory acts as a simple factory: given a unique identifier, it returns a bean instance, deciding whether to create the bean eagerly or lazily based on configuration.

Implementation steps:

Read bean XML definitions and convert each <bean> element into a BeanDefinition object.

Register these definitions in the BeanFactory via BeanDefinitionRegistry , storing them in a ConcurrentHashMap .

Allow custom processing by implementing BeanFactoryPostProcessor , e.g., PropertyPlaceholderConfigurer for placeholder resolution.

The result is a loosely coupled system where dependencies are injected by the factory rather than hard‑coded.

2. Factory Method (FactoryBean)

Spring’s FactoryBean interface represents a factory bean. When a bean implements this interface, getBean() returns the result of FactoryBean#getObject() instead of the factory itself.

Typical example: the integration of MyBatis with Spring, where SqlSessionFactoryBean implements FactoryBean to expose a SqlSessionFactory instance.

Code example (image omitted in source).

3. Singleton Pattern

Spring’s default bean scope is singleton, meaning a single instance per container. The actual singleton handling is performed in AbstractBeanFactory#getSingleton , which uses double‑checked locking and early reference handling to resolve circular dependencies.

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 diagram shows Spring’s double‑checked locking singleton implementation.

4. Adapter Pattern (HandlerAdapter)

In Spring MVC, HandlerAdapter adapts various handler types (e.g., controllers) to a common execution contract. DispatcherServlet obtains a handler from HandlerMapping , then delegates to the appropriate HandlerAdapter to invoke the handler and obtain a ModelAndView .

This makes extending the controller layer straightforward: add a new handler class and a matching adapter.

5. Decorator Pattern

Spring uses wrapper/decorator classes (named with Wrapper or Decorator ) to add responsibilities dynamically, such as HttpServletRequestWrapper . Decorators provide more flexibility than subclassing.

6. Proxy Pattern

AOP in Spring is built on dynamic proxies (both JDK and CGLIB). Static proxies require manual implementation, while dynamic proxies are generated at runtime, allowing method interception without writing proxy code.

7. Observer Pattern

Spring’s event mechanism follows the observer pattern. ApplicationEvent (extends EventObject ) represents an event, ApplicationListener is the observer interface, and ApplicationEventPublisher publishes events to all registered listeners.

public abstract class ApplicationEvent extends EventObject {
    private final long timestamp = System.currentTimeMillis();
    public long getTimestamp() { return this.timestamp; }
}
public interface ApplicationListener
extends EventListener {
    void onApplicationEvent(E event);
}

The AbstractApplicationContext registers listeners and multicasts events via an ApplicationEventMulticaster .

8. Strategy Pattern (Resource Interface)

Spring’s Resource abstraction follows the strategy pattern: different implementations ( UrlResource , ClassPathResource , FileSystemResource , etc.) provide a uniform API for accessing various underlying resources.

9. Template Method Pattern

Many Spring components (e.g., JdbcTemplate ) use the template method pattern combined with callbacks. The abstract class defines the algorithm skeleton (resource acquisition, exception handling, cleanup) while subclasses or callback objects provide the variable part.

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);
}

To avoid subclassing for each operation, Spring introduces callback interfaces such as StatementCallback :

public interface StatementCallback {
    Object doWithStatement(Statement stmt);
}

Clients pass a callback implementation to JdbcTemplate.execute(StatementCallback) , keeping the template logic reusable while allowing custom behavior.

Overall, Spring’s architecture demonstrates extensive use of classic design patterns to provide a flexible, extensible, and maintainable framework for enterprise Java applications.

design patternsJavaSpringdependency injectionsingletonFactoryObserver
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.