Design Patterns Used Inside Spring
Spring is a very good example of design patterns in real code. Knowing where each pattern lives helps you understand the framework and answer senior level interview questions.
Pattern map
| Pattern | Where Spring uses it | Why |
|---|---|---|
| Singleton (per container) | Default bean scope | Share one instance and save memory |
| Factory / Factory Method | BeanFactory, FactoryBean, @Bean methods | Hide how objects are created |
| Dependency Injection / IoC | The whole container | Loose coupling, easy testing |
| Proxy | AOP, @Transactional, @Cacheable, @Async, Spring Data repositories | Add behavior around a bean without changing it |
| Template Method | JdbcTemplate, RestTemplate, JmsTemplate, TransactionTemplate | Fixed steps (open, execute, close), you provide only the variable part |
| Observer | ApplicationEvent and @EventListener | Publish events to many listeners |
| Strategy | PasswordEncoder, HttpMessageConverter, Resource, AuthenticationProvider | Swap algorithms without changing callers |
| Front Controller | DispatcherServlet | One entry point for all web requests |
| Chain of Responsibility | Security filter chain, servlet filters, HandlerInterceptor chain | Each element handles or passes on |
| Adapter | HandlerAdapter, JpaVendorAdapter | Make different interfaces work together |
| Decorator | BeanWrapper, HttpServletRequestWrapper | Add features by wrapping |
| Builder | UriComponentsBuilder, HttpSecurity, RestClient.Builder, SpringApplicationBuilder | Readable step-by-step creation |
Proxy in action (what @Transactional really does)
caller ──► [ CGLIB proxy of OrderService ] ──► real OrderService
│ 1. begin transaction
│ 2. call real method
│ 3. commit or rollback
└─ the caller never sees the differenceTemplate Method: JdbcTemplate
JdbcTemplate does the boring work (get connection, create statement, handle exceptions, close resources) and calls your code only for the variable part.
@Repository
public class UserDao {
private final JdbcTemplate jdbc;
public UserDao(JdbcTemplate jdbc) { this.jdbc = jdbc; }
public List<String> findNames(int minAge) {
return jdbc.query("select name from users where age >= ?",
(rs, rowNum) -> rs.getString("name"), // your part: map one row
minAge);
}
}Strategy: pick behavior by name with Map injection
public interface DiscountStrategy { BigDecimal apply(BigDecimal price); }
@Component("regular")
class RegularDiscount implements DiscountStrategy { public BigDecimal apply(BigDecimal p) { return p; } }
@Component("festival")
class FestivalDiscount implements DiscountStrategy { public BigDecimal apply(BigDecimal p) { return p.multiply(new BigDecimal("0.80")); } }
@Service
public class PricingService {
private final Map<String, DiscountStrategy> strategies; // key = bean name
public PricingService(Map<String, DiscountStrategy> strategies) { this.strategies = strategies; }
public BigDecimal price(String type, BigDecimal price) {
return strategies.getOrDefault(type, strategies.get("regular")).apply(price);
}
}Factory: FactoryBean
@Component
public class HttpClientFactoryBean implements FactoryBean<HttpClient> {
@Override public HttpClient getObject() { return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(3)).build(); }
@Override public Class<?> getObjectType() { return HttpClient.class; }
}
// The bean named "httpClientFactoryBean" gives you an HttpClient (not the factory).
// Use "&httpClientFactoryBean" to get the factory itself.Observer: events
public record UserRegistered(String email) { }
@Service
class RegistrationService {
private final ApplicationEventPublisher publisher;
RegistrationService(ApplicationEventPublisher publisher) { this.publisher = publisher; }
void register(String email) { /* save user */ publisher.publishEvent(new UserRegistered(email)); }
}
@Component
class WelcomeMailListener { @EventListener void on(UserRegistered e) { System.out.println("Welcome mail to " + e.email()); } }
@Component
class AnalyticsListener { @Async @EventListener void on(UserRegistered e) { System.out.println("Track " + e.email()); } }Common mistake: Learning patterns only as textbook diagrams. Open the framework source (for example JdbcTemplate.execute or DispatcherServlet.doDispatch) once, and you will remember the pattern for life.
Interview questions
Which patterns explain how @Transactional works?
Proxy (the call is intercepted), Template Method (TransactionTemplate style), and Chain of Responsibility (the advisor chain in the interceptor).
Is a Spring singleton the Singleton pattern?
No. The Singleton pattern guarantees one instance per class loader. A Spring singleton is one instance per bean definition per container, and you can have several beans of the same class.
Where do you see Strategy in Spring Security?
PasswordEncoder, AuthenticationProvider, AuthorizationManager and SecurityContextRepository are interfaces with swappable implementations.