Unlock Spring’s Power: 9 Design Patterns Every Java Developer Should Master

This article explores nine core design patterns implemented in Spring—simple factory, factory method, singleton, adapter, decorator, proxy, observer, strategy, and template method—detailing their implementation mechanisms, underlying principles, code examples, and practical significance for building loosely coupled, extensible Java applications.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Unlock Spring’s Power: 9 Design Patterns Every Java Developer Should Master

1. Simple Factory (Not one of the 23 GoF patterns)

Implementation: BeanFactory.

Spring’s BeanFactory embodies the simple factory pattern: it returns a bean instance based on a unique identifier, with creation timing (eager or lazy) determined by the specific configuration.

Core principle: A factory class decides which product class to instantiate based on supplied parameters.

How Spring creates beans:

During container startup, Spring reads bean definitions from XML, converts each bean element into a BeanDefinition, registers them in a BeanDefinitionRegistry, and stores them in a concurrent map inside the BeanFactory.

After registration, developers can hook into the process via BeanFactoryPostProcessor (e.g., PropertyPlaceholderConfigurer) to modify definitions before instantiation.

Bean instantiation phase:

Spring instantiates beans using reflection or CGLIB. Several extension points are available: BeanFactoryAware – injects the owning BeanFactory into the bean. BeanPostProcessor – allows custom logic before and after bean initialization. InitializingBean – callback after property population. DisposableBean – callback before bean destruction.

Design significance: Decouples client code from concrete implementations, letting Spring act as a third‑party factory that injects dependencies, achieving loose coupling and enabling additional processing during bean lifecycle.

2. Factory Method

Implementation: FactoryBean interface.

Mechanism: Beans that implement FactoryBean are treated as factories; when getBean() is called, Spring invokes the bean’s getObject() method and returns its result rather than the factory instance itself.

Example: Integration of Spring with MyBatis— SqlSessionFactoryBean implements FactoryBean, so the bean returned is the SqlSessionFactory produced by getObject().

3. Singleton Pattern

Spring’s default bean scope is singleton; bean creation occurs in AbstractBeanFactory.getBean(), which delegates to doGetBean and ultimately calls getSingleton.

Key method analysis:

public Object getSingleton(String beanName){
    // allow early references
    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);
}

Spring applies a double‑checked locking mechanism to ensure thread‑safe singleton creation.

Summary: The singleton pattern guarantees a single instance and provides a global access point; Spring fulfills the global access point via the BeanFactory while leaving actual object construction to the container.

4. Adapter Pattern

Implementation: Spring MVC’s HandlerAdapter.

Mechanism: HandlerAdapter selects and invokes the appropriate Handler based on the request mapping.

Process: DispatcherServlet obtains a handler from HandlerMapping, passes it to the corresponding HandlerAdapter, which executes the handler and returns a ModelAndView back to the servlet.

Significance: Adding new controllers only requires a matching HandlerAdapter, making controller extension straightforward.

5. Decorator Pattern

Implementation: Spring’s wrapper classes (names containing “Wrapper” or “Decorator”).

Essence: Dynamically adds responsibilities to an object without subclassing, offering more flexibility than creating concrete subclasses.

6. Proxy Pattern

Implementation: AOP in Spring relies on dynamic proxying.

Two proxy types:

Dynamic proxy – generated at runtime, no manual proxy class needed.

Static proxy – manually written class that delegates to the target.

Mechanism: At runtime, Spring’s AOP container weaves aspects by creating a proxy object for the target bean, allowing method interception.

7. Observer Pattern

Implementation: Spring’s event‑driven model.

Key components: ApplicationEvent – abstract base class for events. ApplicationListener<E extends ApplicationEvent> – listener interface with onApplicationEvent method. ApplicationEventPublisher – publishes events to registered listeners. ApplicationEventMulticaster – broadcasts events to all listeners.

Example code snippets illustrate the abstract event class, listener interface, and publishing logic.

8. Strategy Pattern

Implementation: Spring’s Resource abstraction.

The Resource interface defines a family of methods ( getInputStream(), exists(), isOpen(), getDescription(), getFile(), getURL()) that encapsulate different resource‑access strategies.

Concrete implementations include UrlResource, ClassPathResource, FileSystemResource, ServletContextResource, InputStreamResource, and ByteArrayResource, each handling a specific underlying resource type.

9. Template Method Pattern

Classic definition: The abstract class defines the algorithm skeleton; subclasses override specific steps.

Spring’s realization: Combines template method with callbacks. Most Spring extensions (e.g., JdbcTemplate) follow this pattern.

Example – JdbcTemplate:

public abstract class JdbcTemplate {
    public final Object execute(String sql) {
        Connection con = null;
        Statement stmt = null;
        try {
            con = getConnection();
            stmt = con.createStatement();
            Object retValue = executeWithStatement(stmt, sql);
            return retValue;
        } finally {
            closeStatement(stmt);
            releaseConnection(con);
        }
    }
    protected abstract Object executeWithStatement(Statement stmt, String sql);
}

Callback interface:

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

Usage:

JdbcTemplate jdbcTemplate = ...;
final String sql = ...;
StatementCallback callback = new StatementCallback() {
    public Object doWithStatement(Statement stmt) {
        return ...;
    }
};
jdbcTemplate.execute(callback);

The template method handles resource management while the callback supplies the variable business logic.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsJavaaopBackend Developmentspringdependency-injectionTemplate Method
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

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.