Back to Patterns in Spring & JDK
Q1Question 1 of 2

Where does Spring and the JDK already use GoF design patterns

Key Concepts
  • Spring's ApplicationContext and BeanFactory are a Factory that creates, wires, and manages beans so you never call new on a service.
  • Every Spring bean is a Singleton by default, meaning one shared instance per container, which is why stateless services are the norm.
  • Spring AOP, @Transactional, and lazy beans use the Proxy pattern to wrap your methods with cross-cutting concerns.
  • JdbcTemplate, RestTemplate, and JmsTemplate use Template Method where the skeleton is fixed and you plug in one varying step via a callback.
  • The JDK's Comparator is the clearest Strategy in everyday code, and java.io streams are the canonical Decorator.
  • Spring events (ApplicationEvent and @EventListener) implement the Observer pattern for loose coupling between publishers and listeners.
  • Recognizing these patterns in real frameworks proves you understand patterns as living engineering tools, not textbook trivia.

Definition

Spring and the JDK are patterns in disguise: Factory for beans, Singleton scope, Proxy for @Transactional, Template Method for JdbcTemplate, Observer for events, and Iterator plus Strategy throughout the JDK.
  • Factory – ApplicationContext and BeanFactory create and wire beans, so you never call new on a service class.
  • Singleton – every Spring bean is one shared instance per container by default, enforcing stateless design.
  • Proxy – Spring AOP and @Transactional inject a proxy that wraps your method with begin/commit/rollback logic.
  • Template Method – JdbcTemplate, RestTemplate, and JmsTemplate define the fixed skeleton and let you plug in one varying step.
  • Observer – ApplicationEvent and @EventListener let listeners react to published events without tight coupling.
  • Strategy, Iterator, Decorator – Comparator swaps comparison logic, every collection returns an Iterator, and java.io streams layer behaviour with decoration.

Imagine the National Bank of India's software is like the bank building itself. The container that hires and manages every employee is the Factory. Each manager role has exactly one person on duty: that is the Singleton. When a customer meets a "manager", they are often meeting a stand-in who checks their ID first: that stand-in is the Proxy, exactly how @Transactional works. Rajesh does not build any of this; it is already wired into the building he walks into.

Understanding the Concepts

Where does Spring use the Factory pattern?

Spring's ApplicationContext and BeanFactory are a textbook Factory. You ask the container for a bean and it decides how to construct it, wire its dependencies, and hand it back. You never call new TransferService(...), so the creation detail is centralised.

JAVA
package com.bank;

import com.bank.service.TransferService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class FactoryDemo {
    public static void main(String[] args) {
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(BankConfig.class);
        TransferService svc = ctx.getBean(TransferService.class);
        svc.transfer("RAJESH-001", "PRIYA-002",
                new java.math.BigDecimal("50000"));
    }
}

The intent is exactly the Factory pattern: the caller asks for an object by type, and the factory decides how to build it.

Where does Spring use the Singleton pattern?

By default, every Spring bean is a Singleton within the container. There is one shared TransferService instance for the whole application. This is Spring's own managed Singleton, distinct from the classic static-field Singleton.

JAVA
package com.bank.service;

import org.springframework.stereotype.Service;

@Service
public class TransferService {
    // One instance shared across the entire application.
    // Must be stateless: no mutable instance fields.
}

Because the instance is shared, services must be stateless. If you need per-request state, use the prototype or request scope instead.

Where does Spring use the Proxy pattern?

Spring AOP, @Transactional, and lazy beans all use the Proxy pattern. When you annotate a method with @Transactional, Spring does not inject your raw class. It injects a proxy that opens a transaction, calls your method, then commits or rolls back.

JAVA
package com.bank.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;

@Service
public class TransferService {
    @Transactional
    public void transfer(String from, String to, BigDecimal amount) {
        // proxy: BEGIN TRANSACTION
        accountRepository.debit(from, amount);
        accountRepository.credit(to, amount);
        // proxy: COMMIT (or ROLLBACK on exception)
    }
}

A common bug: calling a @Transactional method from another method in the same class bypasses the proxy, so the transaction silently never starts. Knowing the Proxy pattern explains why.

Where does Spring use the Template Method pattern?

JdbcTemplate, RestTemplate, and JmsTemplate define the fixed skeleton: acquire connection, run work, handle exceptions, release connection. You fill the one varying step through a callback.

JAVA
package com.bank.repository;

import org.springframework.jdbc.core.JdbcTemplate;
import com.bank.model.Account;
import java.math.BigDecimal;
import java.util.List;

public class AccountRepository {
    private final JdbcTemplate jdbc;

    public AccountRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    public List<Account> findAll() {
        return jdbc.query(
                "SELECT id, balance FROM accounts",
                (rs, i) -> new Account(
                        rs.getString("id"),
                        rs.getBigDecimal("balance")));
    }
}

The RowMapper you pass is the variable step. The template owns the boilerplate of connection management and exception translation.

Where does Spring use the Strategy pattern?

Strategy shows up where behaviour is pluggable. Spring Security picks among many AuthenticationProvider implementations at runtime. Spring's Resource loaders choose how to load a classpath versus a URL resource.

JAVA
// Spring Security: multiple authentication strategies
@Bean
public AuthenticationManager authManager(
        DaoAuthenticationProvider daoProvider,
        LdapAuthenticationProvider ldapProvider) {
    return new ProviderManager(List.of(daoProvider, ldapProvider));
}

Where does Spring use the Observer pattern?

Spring's ApplicationEvent and @EventListener implement Observer. You publish an event and every listener reacts independently.

JAVA
package com.bank.event;

import java.math.BigDecimal;

public class TransferCompletedEvent {
    private final String transactionId;
    private final BigDecimal amount;

    public TransferCompletedEvent(String transactionId, BigDecimal amount) {
        this.transactionId = transactionId;
        this.amount = amount;
    }

    public String getTransactionId() { return transactionId; }
    public BigDecimal getAmount() { return amount; }
}
JAVA
package com.bank.listener;

import com.bank.event.TransferCompletedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class AuditListener {
    @EventListener
    public void onTransferCompleted(TransferCompletedEvent event) {
        System.out.println("Audit: Transfer " + event.getTransactionId()
                + " of " + event.getAmount() + " recorded");
    }
}

Adding a new reaction, such as loyalty points, is a new @EventListener class. The transfer logic is never touched.

Where does Spring use the Decorator and Adapter patterns?

Decorator shows up in BeanPostProcessor, which wraps beans to add behaviour, and in servlet request/response wrappers. Adapter appears as Spring MVC's HandlerAdapter, which lets the dispatcher invoke many kinds of handlers through one interface.

JAVA
// BeanPostProcessor: wraps beans after creation (Decorator)
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String name) {
        System.out.println("Bean created: " + name);
        return bean; // could return a decorated proxy
    }
}

Where does the JDK use design patterns?

The JDK is full of patterns:

  • Iterator – every collection returns an Iterator via iterator(), which is the Iterator pattern.
  • Strategy – Comparator swaps the comparison algorithm: list.sort(Comparator.comparing(Account::getBalance)).
  • Decorator – java.io streams: new BufferedReader(new InputStreamReader(new FileInputStream(f))) layers buffering over decoding over raw bytes.
  • Command – Runnable and Callable are actions packaged as objects for later execution.
  • Builder – StringBuilder, UriComponentsBuilder.fromUriString("/api").queryParam("id", 1).build().
JAVA
package com.bank;

import com.bank.model.Account;
import java.math.BigDecimal;
import java.util.*;

public class JdkPatternDemo {
    public static void main(String[] args) {
        List<Account> accounts = List.of(
                new Account("RAJESH-001", new BigDecimal("50000")),
                new Account("PRIYA-002", new BigDecimal("120000")));

        // Strategy: Comparator swaps the comparison logic
        List<Account> sorted = new ArrayList<>(accounts);
        sorted.sort(Comparator.comparing(Account::getBalance));

        // Iterator: every collection provides one
        Iterator<Account> it = sorted.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

Why does recognizing these matter in practice?

Recognizing patterns in frameworks proves you understand them operationally, not as textbook definitions. Practical consequences at National Bank of India:

  • Knowing @Transactional is a Proxy explains why self-invocation skips the transaction.
  • Knowing beans are Singletons tells Rajesh to keep services stateless.
  • Knowing events are Observer lets Pankaj add a new audit reaction without touching transfer logic.
  • Knowing JdbcTemplate is Template Method tells the team to use callbacks, not to subclass the template.

What should a senior engineer know?

  • Proxy limitations. CGLIB proxies cannot intercept final methods. JDK dynamic proxies require an interface. This affects which classes can use @Transactional.
  • Singleton thread safety. Since beans are shared, any mutable state must be synchronised or avoided entirely.
  • Event ordering. Spring @EventListener does not guarantee ordering across listeners unless you use @Order or @TransactionalEventListener.
  • Template Method vs Strategy in Spring. JdbcTemplate is Template Method (fixed skeleton + pluggable callback), while AuthenticationProvider is Strategy (interchangeable implementations). Knowing the difference helps you decide which pattern to apply in your own code.

What are common interview follow-up questions?

  • Why does self-invocation bypass @Transactional?
  • What is the difference between Spring's managed Singleton and the classic static Singleton?
  • How does JdbcTemplate use Template Method internally?
  • Name three patterns in the JDK and show where they appear.
  • How would you add a new event listener without changing existing code?

Visual Overview

DIAGRAM
  Spring IoC Container (Factory + Singleton)
  ┌──────────────────────────────────────────────┐
  │  @Service TransferService (Singleton bean)    │
  │       │                                       │
  │       │  wrapped by                           │
  │       ▼                                       │
  │  Proxy (AOP / @Transactional)                 │
  │       │                                       │
  │       │  BEGIN → call → COMMIT / ROLLBACK     │
  │       ▼                                       │
  │  JdbcTemplate (Template Method)               │
  │       │                                       │
  │       │  acquire conn → callback → release    │
  │       ▼                                       │
  │  ApplicationEvent (Observer)                  │
  │       │                                       │
  │       ├──→ AuditListener                      │
  │       ├──→ NotificationListener               │
  │       └──→ LoyaltyListener                    │
  └──────────────────────────────────────────────┘
DIAGRAM
  JDK Patterns:

  Iterator:   Collection.iterator() → hasNext() / next()
  Strategy:   list.sort(Comparator.comparing(Account::getBalance))
  Decorator:  BufferedReader → InputStreamReader → FileInputStream
  Command:    Runnable / Callable → execute later
  Builder:    StringBuilder.append().append().toString()

Key Takeaway

Spring and the JDK are not separate from design patterns; they are built out of them, so every developer uses patterns daily whether they name them or not. Recognising where the framework uses a pattern is what proves practical understanding rather than textbook memorisation.

In Spring, the container itself is a Factory that creates and wires beans, which is why you never call new on a service. Those beans are Singletons by default, meaning one shared instance per container, which is why stateless services are the norm. Anything with @Transactional works through a dynamic Proxy that wraps the method to begin and commit the transaction. The template classes like JdbcTemplate are Template Method: they own the fixed skeleton of connect, execute, and clean up, and let you plug in the one varying step through a callback. Events use the Observer pattern through ApplicationEvent and @EventListener, and pluggable authentication providers are Strategy.

In the JDK, every collection hands back an Iterator. Comparator is the clearest Strategy because it lets you swap the comparison algorithm into a fixed sort. The java.io streams are the canonical Decorator, where a BufferedReader wraps an InputStreamReader which wraps a FileInputStream, each layer adding behaviour. And Runnable and Callable are Command objects.

Why this matters at National Bank of India is concrete. Knowing that @Transactional is a Proxy explains the real bug where calling a transactional method from the same class bypasses the proxy and the transaction silently never starts. Knowing the container is a Factory and beans are Singletons tells Rajesh to keep services stateless. And knowing events are Observer lets Pankaj add a new audit reaction by adding a listener without ever touching the transfer logic.

Remember one sentence: Spring and the JDK are patterns in disguise, and recognising them is what separates reading a framework fluently from treating it as a black box.

---

Share
Use ← → arrow keys to navigate