设计模式在Spring源码中的经典应用场景

242 阅读12分钟

目前Spring的核心源码看了后,相信很多人跟我一样会有一个感受,就是代码设计很优雅,我就经常会想人和人的差距真的很大,哈哈!!!

好的代码主要体现在可维护性高扩展性高冗余性低等等,这些很大程度上都是设计模式的体现,设计模式是我们从初级开发者提高技术能力必不可少的一个环节,在学习阶段我们需要多去看一下大佬们的设计与思考。下面我就把Spring源码里设计模式的经典场景给大家罗列出来,做一个抛砖引玉吧!

image.png

1. 工厂模式

我们在接触Spring的时候,最开始就会接触到Spring容器这个概念,那其实这里 BeanFactoryApplicationContext则作为Spring容器的核心接口,则通过getBean实现了工厂模式的应用。

首先,工厂模式顾名思义,工厂主要就是定义了统一的创建模式,在Java中,我提供给你一个接口或者抽象类来让你统一创建,但具体创建A、B还是C由你自己决定。那有什么好处呢?

我们先来看传统模式下我们如何创建一个对象:

UserService userService = new UserServiceImpl();

那这样有什么问题呢?我们可以看到,在这里对象的创建和使用是耦合的,如果UserServiceImpl发生改变,那就要修改所有创建该对象的地方.

那我们看一下Spring是怎么做的:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");

我们可以看到,ApplicationContext相当于一个工厂,getBean()方法在这里就负责创建和返回对象。我们只需要去向工厂去要对象,不管它是如何创建的,这样就实现了创建和使用能够解耦的作用。

另外上面我提到,你可以自己选择创建A、B、C,这就是工厂模式的多态性

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 
UserService userService = context.getBean(UserService.class);

这里UserService是一个接口,getBean方法会根据配置文件中定义的实现类来返回具体的对象。这样就可以实现运行时多态

总而言之,工厂模式主要是属于创建型设计模式,核心思想就是将对象的创建和使用进行分离。这里大家可以仔细品味,感受一下!

2. 代理模式

代理模式,初次见它是在AOP中,在AOP的时候我们说JDK动态代理CGLIB字节码增强共同构成了Spring AOP的代理体系,后续在学习事物的时候也可以接触到,那我们这里也从最经典的AOP里来说代理模式。

首先,我们要知道代理模式在AOP中的核心作用是:

  • 解耦:将横切点从业务中分离出来
  • 增强:在不修改目标对象的情况下,动态的增添额外功能

我们来看AOP中代理模式的2种实现方式:

2.1 JDK动态代理

适用场景:目标对象实现了接口

原理:Java反射机制,基于接口生成代理类

// 目标接口
public interface UserService {
    void createUser(String username);
}
// 目标实现类
public class UserServiceImpl implements UserService {
    @Override
    public void createUser(String username) {
        System.out.println("创建用户: " + username);
    }
}
// 代理处理器
public class LoggingHandler implements InvocationHandler {
    private final Object target;

    public LoggingHandler(Object target) {
        this.target = target;
    }

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置通知:方法调用前记录日志
        System.out.println("Before method: " + method.getName());
        
        // 调用目标方法
        Object result = method.invoke(target, args);
        
        // 后置通知:方法调用后记录日志
        System.out.println("After method: " + method.getName());
        
        return result;
    }
}
// 创建代理对象
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new LoggingHandler(target)
);

// 调用代理方法
proxy.createUser("test");

在这里我们可以看到,代理对象的生成是基于接口通过反射来生成代理类的。

2.2 CGLIB代理

适用场景:目标对象没有实现接口(即普通类)

原理:继承该目标类生成子类代理

// 目标类(无接口)
public class UserService {
    public void createUser(String username) {
        System.out.println("创建用户: " + username);
    }
}

// 方法拦截器
public class LoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置通知
        System.out.println("Before method: " + method.getName());
        
        // 调用目标方法
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置通知
        System.out.println("After method: " + method.getName());
        
        return result;
    }
}
// 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new LoggingInterceptor());
UserService proxy = (UserService) enhancer.create();

// 调用代理方法
proxy.createUser("test");

这里和JDK代理的区别就是在创建代理对象的时候是通过继承该类实现的,通过子类来代理该类。

3. 模版方法模式

顾名思义,模版方法就是把一些流程固定,把不变的资源获取/释放流程固化,通过回调接口开放变化的数据访问逻辑。

模版方法模式实际上在JdbcTemplate有体现,它是Spring框架提供的一个用于简化JDBC操作的工具类,充分体现了模版方法模式的设计思想,具体体现在:

  • 抽象类:定义算法的模版和基本操作(抽象方法或钩子方法
  • 具体子类:实现抽象方法,完成算法的特点步骤

Jdbc模版定义的具体流程是这样的:获取数据库连接、创建Statement/PreparedStatement、执行SQL、处理结果集、关闭资源

我们来看一下JDBC的模版方法源码: 核心方法query()

public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
    return execute(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
            // 创建 PreparedStatement(具体步骤1)
            return con.prepareStatement(sql);
        }
    }, new PreparedStatementCallback<List<T>>() {
        @Override
        public List<T> doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
            // 设置参数(具体步骤2)
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    StatementCreatorUtils.setParameterValue(ps, i + 1, args[i]);
                }
            }
            
            // 执行查询(具体步骤3)
            ResultSet rs = ps.executeQuery();
            
            try {
                // 处理结果集(具体步骤4)
                List<T> results = new ArrayList<>();
                int rowNum = 0;
                while (rs.next()) {
                    results.add(rowMapper.mapRow(rs, rowNum++));
                }
                return results;
            } finally {
                // 关闭 ResultSet(具体步骤5的一部分)
                JdbcUtils.closeResultSet(rs);
            }
        }
    });
}

模版方法execute()

public <T> T execute(PreparedStatementCallback<T> action) throws DataAccessException {
    Assert.notNull(action, "Callback object must not be null");
    
    // 获取连接(公共步骤1)
    Connection con = DataSourceUtils.getConnection(getDataSource());
    PreparedStatement ps = null;
    
    try {
        // 创建 PreparedStatement(公共步骤2)
        ps = con.prepareStatement(sql);
        
        // 应用 Statement 设置(公共步骤3)
        applyStatementSettings(ps);
        
        // 执行回调(延迟到子类/回调的步骤)
        T result = action.doInPreparedStatement(ps);
        
        // 处理 SQL warnings(公共步骤4)
        handleWarnings(ps);
        
        return result;
    } catch (SQLException ex) {
        // 异常处理(公共步骤5)
        JdbcUtils.closeStatement(ps);
        ps = null;
        DataSourceUtils.releaseConnection(con, getDataSource());
        con = null;
        throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
    } finally {
        // 关闭资源(公共步骤6)
        JdbcUtils.closeStatement(ps);
        DataSourceUtils.releaseConnection(con, getDataSource());
    }
}

从这里我们可以看出来,模版模式可以让我们在开发时只关注SQL结果集处理,无需编写重复的资源管理代码。

4. 观察者模式

观察者模式是一种很重要的设计模式,在很多组件的设计时都有涉及,在Spring里主要是用于实现事件驱动的架构。通过ApplicationEventApplicationListener接口来实现这一模式,让组件之间可以松耦合。

观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生变化的时候,所有依赖他的对象(也就是观察者)都会收到通知并自动更新。这整个模式有三个重要组件:

  • Subject:发布事件的对象,维护观察者列表
  • Observer:订阅事件并处理通知
  • Event:状态变化的载体,包含了事件源和相关数据

Spring中事件机制就是基于观察者模式,主要由以下部分组成:

4.1 核心接口和类

  • ApplicationEvent:所有事件的基类,继承自 java.util.EventObject
  • ApplicationListener<E extends ApplicationEvent> :观察者接口,定义 onApplicationEvent(E event) 方法。
  • ApplicationEventPublisher:主题接口,定义 publishEvent(ApplicationEvent event) 方法。
  • ApplicationEventMulticaster:事件多播器,负责管理监听器并广播事件。
  • ApplicationContext:实现了 ApplicationEventPublisher 接口,作为事件发布者。

4.2 事件类型

Spring里有很多标准事件:

  • ContextRefreshedEvent:ApplicationContext 初始化或刷新完成时发布。
  • ContextStartedEvent:调用 start() 方法启动 ApplicationContext 时发布。
  • ContextStoppedEvent:调用 stop() 方法停止 ApplicationContext 时发布。
  • ContextClosedEvent:调用 close() 方法关闭 ApplicationContext 时发布。
  • RequestHandledEvent:Web 请求处理完成后发布(仅适用于 Web 应用)。

4.3 从源码来看事件发布与监听流程

4.3.1 事件发布流程

容器在发布事件时,调用的是获得事件多播器方法:

// AbstractApplicationContext.java
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    // ... 省略部分代码
    
    // 获取事件多播器
    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    
    // ... 省略部分代码
}

4.3.2 事件多播器实现

SimpleApplicationEventMulticaster 是默认的事件多播器实现,它会遍历所有注册的监听器并调用其 onApplicationEvent() 方法:

// SimpleApplicationEventMulticaster.java
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    
    // 获取所有适合处理该事件的监听器
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 异步或同步执行监听器
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    // 调用监听器的 onApplicationEvent 方法
    listener.onApplicationEvent(event);
}

4.3.3 监听器注册

监听器可以通过多种方式注册到 ApplicationContext 中: 通过注解方式:

@Component
public class MyEventListener {
    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        // 处理事件
        System.out.println("Context refreshed: " + event.getApplicationContext());
    }
}

Spring的事件机制是观察者模式的典型应用,通过 ApplicationEventApplicationListener 和 ApplicationEventPublisher 实现了组件间的松耦合通信。

5. 适配器模式

适配器模式就像是一个转换插头一样,在Spring MVC里,核心体现在:HandlerAdapter接口及其实现类是适配器的模式的应用。DispatcherServlet在收到请求后,需要调用不同类型的处理器(Handler)来处理请求。但是不同的处理器的接口和方法是不一样的,为了让DispatcherServlet能够以统一的方式调用这些处理器,Spring MVC引入了HandlerAdapter

5.1 接口代码

我们来看一下HandlerAdapter接口是如何做的:

public interface HandlerAdapter {
    // 判断该适配器是否能处理给定的处理器
    boolean supports(Object handler);
    // 执行处理器并返回ModelAndView
    ModelAndView handle(HttpServletRequest request, 
                        HttpServletResponse response, 
                        Object handler) throws Exception;
    // 获取处理器的最后修改时间
    long getLastModified(HttpServletRequest request, Object handler);
}

5.2 具体适配器的实现

  • SimpleControllerHandlerAdapter:用于处理实现了Controller接口的处理器。
  • RequestMappingHandlerAdapter:用于处理使用@RequestMapping注解的方法。
  • HttpRequestHandlerAdapter:用于处理实现了HttpRequestHandler接口的处理器。

5.3 适配器的工作流程

// DispatcherServlet中的部分伪代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 1. 确定处理器
    HandlerExecutionChain mappedHandler = getHandler(request);
    // 2. 获取能处理该处理器的适配器
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    // 3. 通过适配器调用处理器
    ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
    // 4. 处理视图和模型
    processDispatchResult(request, response, mappedHandler, mv, dispatchException);
}

6. 策略模式

策略模式在Spring源码中主要体现在ResourceLoader接口及其实现是策略模式的典型应用。通过将 资源加载算法 抽象为可互换的策略,Spring 实现了对不同协议资源(如 Classpath、文件系统、URL、ServletContext 等)的统一加载能力。

首先整个策略模式是有几个核心结构的:

角色Spring 实现类功能说明
策略接口ResourceLoader定义资源加载的统一接口
具体策略DefaultResourceLoader 等实现不同协议的加载逻辑
策略选择器ResourcePatternResolver扩展策略选择能力(支持通配符等)
策略上下文ApplicationContext持有并管理资源加载策略

下面这个流程图就是处理流程

deepseek_mermaid_20250513_ae7c99.png

所以我们这里可以得出策略模式的特点:

  • 定义统一策略接口(ResourceLoader)
  • 提供多种实现策略(文件系统、类路径、Servlet上下文)
  • 运行时动态切换策略(通过依赖注入或配置)
  • 解耦资源加载逻辑与客户端代码(如ApplicationContext)

我们来看一下具体的源码:

策略接口定义

public interface ResourceLoader {
    // 核心策略方法:根据位置标识符加载资源
    Resource getResource(String location);

    // 获取类加载器(为策略实现提供上下文)
    @Nullable
    ClassLoader getClassLoader();
}

具体的策略实现

    Assert.notNull(location, "Location must not be null");

    // 1. 检查自定义协议解析器(扩展点)
    for (ProtocolResolver protocolResolver : this.protocolResolvers) {
        Resource resource = protocolResolver.resolve(location, this);
        if (resource != null) {
            return resource;
        }
    }

    // 2. 内置策略决策逻辑
    if (location.startsWith("/")) {
        return getResourceByPath(location);
    } else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    } else {
        try {
            // 尝试解析为URL资源(文件/http等)
            URL url = new URL(location);
            return new UrlResource(url);
        } catch (MalformedURLException ex) {
            // 兜底策略:文件系统资源
            return getResourceByPath(location);
        }
    }
}

7. 装饰器模式

装饰器模式是一种结构型设计模式,顾名思义就是通过包装动态的为对象添加新功能。且不改变原有结构,Spring里有大量应用,用来实现功能扩展,也是开闭原则的典型应用。我们来看一下具体的实现代码:

7.1 HTTP 请求装饰器:ServletServerHttpRequest

应用场景:将原生HttpServletRequest 装饰为 Spring 的 HttpInputMessage

public class ServletServerHttpRequest implements HttpInputMessage {
    private final HttpServletRequest request; // 被装饰对象
    private final Headers headers;

    public ServletServerHttpRequest(HttpServletRequest request) {
        this.request = request;
        this.headers = new ServletRequestHeaders(request); // 功能增强
    }

    // 保持原有接口方法
    public InputStream getBody() throws IOException {
        return this.request.getInputStream(); // 直接委托
    }

    // 新增功能:标准化头信息处理
    public HttpHeaders getHeaders() {
        return this.headers;
    }
}

我们可以看到,这里是保留了原有功能,又新增了功能。

7.2 事务中的缓存装饰器

这里是事物提交后才真正执行缓存操作:

public class TransactionAwareCacheDecorator implements Cache {
    private final Cache targetCache; // 被装饰缓存对象

    public void put(final Object key, final Object value) {
        // 事务感知增强
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            TransactionSynchronizationManager.registerSynchronization(
                new TransactionSynchronizationAdapter() {
                    public void afterCommit() {
                        targetCache.put(key, value); // 延迟到事务提交后执行
                    }
                });
        } else {
            targetCache.put(key, value);
        }
    }
}

原有的功能是基础的缓存存取能力,新增了事物同步处理。

8. 责任链模式

我们在学习Spring MVC的源码的时候,里面的拦截器链就是典型的责任链模式的应用。

8.1 Spring MVC拦截器链

HandlerExecutionChain 维护多个 HandlerInterceptor 构成的链式结构:

public class HandlerExecutionChain {
    private final Object handler;
    private final List<HandlerInterceptor> interceptors = new ArrayList<>();

    boolean applyPreHandle(HttpServletRequest request, 
                          HttpServletResponse response) throws Exception {
        for (int i = 0; i < this.interceptors.size(); i++) {
            HandlerInterceptor interceptor = this.interceptors.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false; // 中断链式传递
            }
        }
        return true;
    }
}

整个的执行链条是这样的:

deepseek_mermaid_20250513_43e848.png

其实这种执行链的应用场景有很多,比如日志记录权限校验审批流程数据处理流水线等。另外,我们可以进行自定义,将一些高频的校验处理器放在链首。

8.2 Spring Security 过滤器链

public class FilterChainProxy extends GenericFilterBean {
    private List<SecurityFilterChain> filterChains;

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
        
        List<Filter> filters = getFilters(request);
        if (filters == null || filters.size() == 0) {
            chain.doFilter(request, response);
            return;
        }

        VirtualFilterChain vfc = new VirtualFilterChain(filters); // 虚拟链
        vfc.doFilter(request, response);
    }
}

9. 总结

这里基本上就把重要的设计模式都说了,但是请大家谨记,任何技术以及设计方案都不是完美的,即使是这里我们介绍的看起来很优秀的设计模式也是有很多不足之处,大家要理性去看待技术。技术是服务于业务,不同业务有不同的处理模式,切记不可生搬硬套!!!

image.png