跟着Spring学设计模式

一、创建型模式

1.单例模式

核心思想:旨在确保一个类只有一个实例,并提供一个全局访问点以访问该实例。

Spring实现:Spring容器中存储Bean的结构是名为singletonObjects的ConcurrentHashMap,用于缓存所有单例Bean实例。这样可以确保在整个应用程序中只有一个实例存在,避免了对象重复的创建,节省了系统资源并提高了性能。

代码体现

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    @Override
    public Object getSingleton(String beanName) {
        return this.singletonObjects.get(beanName);
    }
    
    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            Object oldObject = this.singletonObjects.get(beanName);
            if (oldObject != null) {
                throw new IllegalStateException("Could not register object [" + singletonObject + "] under bean name '" + beanName +
                        "': there is already object [" + oldObject + "] bound");
            }
            this.singletonObjects.put(beanName, singletonObject);
        }
    }
    
    // 其他方法...
}

##2.工厂方法模式
设计思想:工厂方法模式定义一个抽象工厂接口,由具体的工厂类来实现该接口,从而创建不同的具体产品对象。客户端可以通过该抽象工厂接口来调用工厂方法,从而获得具体产品的实例。工厂方法模式具有良好的扩展性,可以通过添加新的工厂类和产品类来灵活地扩展系统的功能。

Spring实现:BeanFactory和ApplicationContext是所有模式的根基。它们定义了getBean()方法,这就是一个工厂方法。它们负责根据配置信息或注解来创建和管理 bean 实例,隐藏了具体对象的创建细节,提供了一种灵活的方式来管理对象的生命周期。由具体的实现类(如DefaultListableBeanFactory, AnnotationConfigApplicationContext)决定了如何创建(createBean())和获取Bean。

以及FactoryBean接口,它是一个能生产对象的工厂Bean。实现了FactoryBean接口的类,Spring在通过getBean()获取时,返回的不是工厂本身,而是其getObject()方法所返回的对象。

代码体现

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    boolean containsBean(String name);
    ...
}
public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

3.抽象工厂模式

设计思想:和工厂方法不同的是,工厂方法是生产的单一种类的对象,而抽象工厂模式生产的是一组相关或相互依赖的对象。

4.建造者模式

设计思想:建造者模式用于创建复杂对象,通过建造者模式可以简化复杂对象的构建过程。

Spring实现:Spring提供了大量Builder类来简化复杂对象的配置,如RestTemplateBuilder, 用于构建 RestTemplate 实例。

代码体现

下面是使用 RestTemplateBuilder 创建 RestTemplate 对象的示例代码:

RestTemplate restTemplate = new RestTemplateBuilder()
        .rootUri("https://api.example.com")
        .basicAuthentication("username", "password")
        .build();

在这个示例中,RestTemplateBuilder 提供了 rootUri()、basicAuthentication() 等方法用于设置 RestTemplate 的属性,最后调用 build() 方法构建出一个 RestTemplate 对象。

RestTemplateBuilder 类源码:

public class RestTemplateBuilder {
    private String rootUri;
    private CredentialsProvider credentialsProvider;
 
    public RestTemplateBuilder rootUri(String rootUri) {
        this.rootUri = rootUri;
        return this;
    }
 
    public RestTemplateBuilder basicAuthentication(String username, String password) {
        this.credentialsProvider = new BasicCredentialsProvider();
        this.credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        return this;
    }
 
    public RestTemplate build() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(this.rootUri));
        if (this.credentialsProvider != null) {
            restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.custom().setDefaultCredentialsProvider(this.credentialsProvider).build()));
        }
        return restTemplate;
    }
}

在 RestTemplateBuilder 类中,通过链式调用一系列方法来设置 RestTemplate 的属性,并在 build() 方法中构建出一个 RestTemplate 对象。在 build() 方法中,根据设置的属性创建 RestTemplate 对象,并设置相应的请求工厂、URI 模板处理器等属性。

通过使用建造者模式,Spring 实现了 RestTemplate 的创建过程,使得构建过程更加灵活,同时保证了 RestTemplate 对象的一致性和可复用性。

5.原型模式

设计思想:通过复制现有对象来创建新对象,而无需通过明确的构造函数进行实例化。它的核心思想是通过克隆已有的对象来创建新的对象,从而避免了重复创建对象的过程。

Spring实现:通过将Bean的作用域(Scope)定义为prototype。每次向容器请求该Bean时,BeanFactory都会执行一次创建逻辑(调用初始化方法等),返回一个新的实例。

代码体现

@Component
@Scope("prototype")
public class PrototypeBean {
    private String state;
    private static int instanceCount = 0;
    
    public PrototypeBean() {
        instanceCount++;
        System.out.println("PrototypeBean instance created: " + instanceCount);
    }
    
    public void setState(String state) {
        this.state = state;
    }
}

@Service
public class BusinessService {
    
    @Autowired
    private ApplicationContext context;
    
    public void usePrototype() {
        PrototypeBean bean1 = context.getBean(PrototypeBean.class);
        bean1.setState("state1");
        
        PrototypeBean bean2 = context.getBean(PrototypeBean.class);
        bean2.setState("state2");
        
        System.out.println("Different instances: " + (bean1 != bean2)); // true
    }
}

二、结构型模式

1.代理模式

基本思想:通过创建代理对象来控制对实际对象的访问

Spring实现:Spring AOP、@Transactional、@Async等声明式服务。

详细说明:在 Spring 框架中,AOP(面向切面编程)的实现主要依赖于代理模式。Spring 使用动态代理来实现 AOP,主要有两种代理方式:基于 JDK 动态代理和基于 CGLIB 动态代理。

下面简要介绍 Spring 源码中如何使用代理模式实现 AOP:

基于 JDK 动态代理的 AOP:
当目标对象实现了接口时,Spring 使用 JDK 动态代理来创建 AOP 代理对象。在 JDK 动态代理中,代理对象实现了目标对象的所有接口,并将方法调用委托给 MethodInvocation 接口的实现类来处理

java.lang.reflect.Proxy:是 Java 反射机制的一部分,它提供了一个静态方法用于在运行时动态地创建代理类和对象,它的作用就是:在程序运行期间,动态地创建一个实现了指定接口的新类(代理类)的实例。

代码体现

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 1. 定义业务接口
interface UserService {
    void addUser(String name);
    String getUser(int id);
    void deleteUser(int id);
}

// 2. 定义真实对象(实现类)
class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("真实调用: 添加用户 - " + name);
    }
    
    @Override
    public String getUser(int id) {
        String result = "用户ID: " + id;
        System.out.println("真实调用: 获取用户 - " + result);
        return result;
    }
    
    @Override
    public void deleteUser(int id) {
        System.out.println("真实调用: 删除用户 - " + id);
    }
}

public class AnonymousInvocationHandlerDemo {
    
    public static void main(String[] args) {
        
        // 3. 创建真实对象
        UserService realService = new UserServiceImpl();
        
        // 4. 使用【匿名内部类】创建 InvocationHandler
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                
                // 前置增强:记录日志
                System.out.println("[日志] 方法调用前: " + method.getName());
                if (args != null) {
                    System.out.println("[日志] 参数: " + java.util.Arrays.toString(args));
                }
                
                // 性能监控:开始计时
                long startTime = System.nanoTime();
                
                // 【核心】通过反射调用真实对象的方法
                Object result = method.invoke(realService, args);
                
                // 性能监控:结束计时并输出
                long endTime = System.nanoTime();
                System.out.println("[性能] 方法执行耗时: " + (endTime - startTime) + " 纳秒");
                
                // 后置增强:记录结果
                System.out.println("[日志] 方法调用完成,返回值: " + result);
                System.out.println("----------------------------------");
                
                return result;
            }
        }; // 注意:这里有一个分号,因为是赋值语句
        
        // 5. 创建代理对象
        UserService proxyService = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(), // 使用接口的类加载器
            new Class<?>[] { UserService.class }, // 代理类要实现的接口列表
            handler // 传入我们定义的匿名InvocationHandler
        );
        
        // 6. 使用代理对象(所有调用都会被匿名内部类中的invoke方法处理)
        System.out.println("=== 调用 addUser ===");
        proxyService.addUser("Alice");
        
        System.out.println("=== 调用 getUser ===");
        String userInfo = proxyService.getUser(123);
        System.out.println("主程序收到结果: " + userInfo);
        
        System.out.println("=== 调用 deleteUser ===");
        proxyService.deleteUser(456);
        
        // 7. 验证代理类类型
        System.out.println("代理对象的类: " + proxyService.getClass().getName());
        System.out.println("代理对象是否是Proxy的子类? " + (proxyService instanceof Proxy));
    }
}

2.装饰器模式

设计思路:动态地给一个对象添加一些额外的功能,而不改变其结构。提供比继承更灵活的功能扩展方式。

Spring实现: 在 Spring 源码中,BeanPostProcessor 接口的实现是基于装饰者模式的,它允许我们在 bean 实例化、依赖注入和初始化阶段对 bean 进行额外的处理,而不需要修改原始的 bean 类。

源码体现

BeanPostProcessor 接口定义:

package org.springframework.beans.factory.config;
 
public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
 
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

在 Spring 源码中,AbstractApplicationContext 类实现了 BeanFactory 和 ApplicationContext 接口,并且提供了对 BeanPostProcessor 的支持

package org.springframework.context.support;
 
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean {
    // ...
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // ...
        // 注册 BeanPostProcessor,对 bean 进行装饰
        for (BeanPostProcessor processor : beanFactory.getBeanPostProcessors()) {
            beanFactory.addBeanPostProcessor(processor);
        }
        // ...
    }
    // ...
}

在 AbstractApplicationContext 类中,通过调用 finishBeanFactoryInitialization 方法,会注册所有的 BeanPostProcessor 实例到 BeanFactory 中,从而对 bean 进行装饰。

AbstractAutowireCapableBeanFactory 类,它是 DefaultListableBeanFactory 类的父类,负责实现了 BeanFactory 的核心功能。

package org.springframework.beans.factory.support;
 
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    // ...
    protected void invokeAwareMethods(final String beanName, final Object bean) {
        // ...
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof Aware) {
                if (processor instanceof BeanNameAware) {
                    ((BeanNameAware) processor).setBeanName(beanName);
                }
                // ...
            }
        }
        // ...
    }
    // ...
}

在 AbstractAutowireCapableBeanFactory 类中,通过调用 invokeAwareMethods 方法,会遍历所有的 BeanPostProcessor 实例,如果发现实现了特定的 Aware 接口(如 BeanNameAware),则调用其对应的方法来设置 bean 的相关属性。

Spring 源码中使用装饰者模式实现了 BeanPostProcessor 接口,允许我们在 bean 实例化、依赖注入和初始化阶段对 bean 进行额外的处理,从而实现了更灵活、可扩展的 bean 处理机制

3.适配器模式

设计思想:允许将一个类的接口转换为客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而无法一起工作的类能够协同工作。

Spring实现: Spring MVC 中的 HandlerAdapter 就是适配器模式的应用,通过适配器将不同类型的处理器适配成统一的处理器接口,以统一的方式来处理请求

代码体现

HandlerAdapter 接口定义

HandlerAdapter 接口定义了对处理器的调用方式,一般包括多个方法,如 supports() 方法用于判断适配器是否支持某种类型的处理器,handle() 方法用于执行处理器。

public interface HandlerAdapter {
    boolean supports(Object handler);
 
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

具体适配器类

Spring 源码中提供了多个具体的适配器类,用于适配不同类型的处理器,如 SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter、AnnotationMethodHandlerAdapter 等。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public boolean supports(Object handler) {
        return (handler instanceof SimpleController);
    }
 
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 处理 SimpleController 类型的处理器
    }
}

DispatcherServlet 中的调用:

在 DispatcherServlet 中,根据请求的处理器类型选择合适的 HandlerAdapter 来执行处理器。

protected ModelAndView haHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha.handle(request, response, handler);
        }
    }
    return null;
}

DispatcherServlet 会遍历所有注册的 HandlerAdapter,通过supports方法找到支持当前处理器类型的适配器,并调用其 handle() 方法执行处理器。

通过适配器模式,Spring 实现了 HandlerAdapter 接口的多种具体适配器类,每个适配器类负责将不同类型的处理器适配到 HandlerAdapter 接口上,从而统一处理器的调用方式,提高了处理器的复用性和灵活性。

4.桥接模式

设计思想:将抽象部分与它的实现部分分离,使它们都可以独立地变化。当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。可以使用桥接模式。

比如需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。

Spring实现:Spring 的 JdbcTemplate 与数据源 (DataSource)

桥接模式角色 Spring JDBC 中的对应物
抽象化 (Abstraction) JdbcTemplate 及其父类 JdbcAccessor
扩展抽象化 (Refined Abstraction) NamedParameterJdbcTemplate (扩展了 JdbcTemplate 的功能)
实现化 (Implementor) DataSource 接口 (来自 javax.sql)
具体实现化 (Concrete Implementor) 各种 DataSource 实现: • DriverManagerDataSource (Spring) • HikariDataSource (HikariCP) • BasicDataSource (Apache DBCP) • 应用服务器提供的 DataSource

代码体现

如何体现“桥接”?

  1. JdbcTemplate (抽象部分) 持有一个对 DataSource (实现部分) 的引用。查看 JdbcTemplate 的源码:
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    // ... 
    public JdbcTemplate(DataSource dataSource) { // 构造函数注入“桥”的另一端
        setDataSource(dataSource); // 这个方法在父类JdbcAccessor中
        afterPropertiesSet();
    }
    // ...
}
  1. 其父类 JdbcAccessor
public abstract class JdbcAccessor implements InitializingBean {
    // 关键的“桥”!维护一个对实现化对象(DataSource)的引用。
    private DataSource dataSource; 

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    public DataSource getDataSource() {
        return this.dataSource;
    }
    // ...
}
  1. 两个维度独立变化
  • 抽象维度 (JdbcTemplate) 的变化:你可以使用基础的 JdbcTemplate,也可以使用功能更强大的 NamedParameterJdbcTemplate(支持命名参数而非占位符?)。它们都基于相同的 DataSource 工作。
// 使用基础JdbcTemplate
JdbcTemplate jt1 = new JdbcTemplate(hikariDataSource);
jt1.queryForObject("SELECT * FROM users WHERE id = ?", ...);

// 使用扩展的NamedParameterJdbcTemplate(扩展抽象化)
NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate(hikariDataSource);
npjt.queryForObject("SELECT * FROM users WHERE id = :id", paramMap, ...);
  • 实现维度 (DataSource) 的变化:你可以更换底层的数据源实现,而上层的 JdbcTemplate 代码完全不需要修改。这就是“桥接”带来的巨大优势。
// 使用HikariCP数据源
DataSource hikariDataSource = new HikariDataSource(hikariConfig);
JdbcTemplate jt1 = new JdbcTemplate(hikariDataSource);

// 使用Apache DBCP数据源(切换实现)
DataSource dbcpDataSource = new BasicDataSource(dbcpConfig);
JdbcTemplate jt2 = new JdbcTemplate(dbcpDataSource); // JdbcTemplate代码无需变化

// 甚至在测试时使用简单的嵌入式数据源
DataSource embeddedDataSource = new EmbeddedDatabaseBuilder()...build();
JdbcTemplate jt3 = new JdbcTemplate(embeddedDataSource);

5.外观模式

设计思想:为一个复杂的子系统提供一个统一的、更简洁的高级接口(外观),从而降低客户端与子系统之间的耦合度,并简化客户端的使用。

Spring实现Spring JdbcTemplate&DispatcherServlet

代码体现

  1. Spring JdbcTemplate (最经典的例子)

子系统:原生的 JDBC API (DriverManager, Connection, Statement, ResultSet, SQLException 等)。使用原生 JDBC 非常繁琐且容易出错,需要处理大量的资源打开关闭、异常处理、事务管理。

外观:**JdbcTemplate** 类。

  • JdbcTemplate 提供了一个简洁、高级的接口(如 query(), update(), execute())。
  • 它在内部封装了所有繁琐的流程:获取连接、创建语句、执行SQL、处理结果集、处理异常、关闭资源。
  • 它将检查异常 SQLException 转换为了非检查异常 DataAccessException,简化了异常处理。

代码对比:

没有外观 (原生JDBC):

// 繁琐、易错、重复的样板代码
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = DriverManager.getConnection(DB_URL, USER, PASS);
    stmt = conn.prepareStatement("SELECT * FROM users WHERE id=?");
    stmt.setInt(1, userId);
    rs = stmt.executeQuery();
    // ... 处理结果集
} catch (SQLException e) {
    // 处理异常
} finally {
    // 小心翼翼地在finally块中关闭资源
    if (rs != null) try { rs.close(); } catch (SQLException e) { /* log */ }
    if (stmt != null) try { stmt.close(); } catch (SQLException e) { /* log */ }
    if (conn != null) try { conn.close(); } catch (SQLException e) { /* log */ }
}

有外观 (JdbcTemplate):

// 简洁、安全、专注于核心逻辑
@Autowired
JdbcTemplate jdbcTemplate; // 注入“外观”

public User getUser(int userId) {
    // 一行代码搞定所有!外观内部处理了所有复杂细节。
    return jdbcTemplate.queryForObject(
        "SELECT * FROM users WHERE id=?",
        new BeanPropertyRowMapper<>(User.class),
        userId
    );
}
  1. Spring MVC 的 DispatcherServlet

子系统:处理HTTP请求涉及的各种组件(处理器映射HandlerMapping、处理器适配器HandlerAdapter、视图解析器ViewResolver、异常处理器HandlerExceptionResolver等)。

外观:**DispatcherServlet** 类。

  • 作为前端控制器,它是整个Spring MVC的入口。
  • 客户端(浏览器)只需要发送请求到 DispatcherServlet
  • DispatcherServlet 内部协调所有子系统组件:它询问 HandlerMapping 该请求由哪个Controller处理,通过 HandlerAdapter 执行Controller方法,用 ViewResolver 解析视图,最后渲染响应。
  • 客户端完全不知道背后有这么多组件在协同工作。

6.享元模式

设计思想:运用共享技术来有效地支持大量细对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。

java中的实现:Java 包装类(如 IntegerLongCharacterByteShort)的缓存机制是享元模式(Flyweight Pattern)

代码体现:

public final class Integer extends Number implements Comparable<Integer> {
    
    // ... 其他代码 ...
    
    /**
     * 这就是享元工厂!
     * 它缓存了从low到high(默认-128到127)的Integer对象。
     */
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[]; // 享元对象池(缓存池)

        static {
            // high值可以通过JVM参数`-XX:AutoBoxCacheMax=<size>`配置
            int h = 127;
            // ... [这里会读取JVM配置,可能调整h的值] ...
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            // 预先创建好所有享元对象,并放入缓存池
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
    }

    /**
     * 这是获取享元对象的工厂方法。
     * 客户端应该优先使用这个方法,而不是构造函数。
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            // 如果在缓存范围内,直接从享元池中返回共享的对象
            return IntegerCache.cache[i + (-IntegerCache.low)];
        // 否则,创建一个新的对象(这个新对象不会被共享)
        return new Integer(i);
    }
    
    // ... 其他代码 ...
}

三、行为型模式

1.模板方法模式

设计思想:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。通俗来讲:父类规定了一套流程(模板),并把流程中那些需要个性化实现的步骤留空(或提供默认实现),让子类去填空。

Spring应用:JdbcTemplate 及其它 XxxTemplate(最经典的应用)、Spring 容器初始化过程中的各种 InitializingBean 和 init-method

代码体现:

1. JdbcTemplate 及其它 XxxTemplate(最经典的应用)

这是 Spring 对模板方法模式最教科书式的实现,旨在消除重复的样板代码。

  • 抽象类JdbcTemplate 本身。更准确地说,其父类 JdbcAccessor 和它一起定义了模板。
  • 模板方法execute(), query(), update() 等方法。
  • 需要子类实现的步骤:这些方法接收 回调接口(如 PreparedStatementCreator, RowMapper, ResultSetExtractor)作为参数。这些回调接口的实现,就是“子类”要完成的步骤

工作流程(以 query() 为例):
JdbcTemplate 的模板方法固定了流程:

  1. 从数据源获取连接 (getConnection())
  2. 创建预处理语句 (createPreparedStatement(conn)) <- 由你的 PreparedStatementCreator 实现
  3. 执行查询 (executeQuery())
  4. 处理结果集 (extractData(rs)) <- 由你的 RowMapperResultSetExtractor 实现
  5. 处理异常(翻译SQLException为DataAccessException)
  6. 清理资源,关闭连接、语句、结果集 (close())

你不需要关心1,3,5,6步,只需要专注于实现第2步和第4步(通过匿名内部类或Lambda表达式):

// JdbcTemplate固定了流程骨架
List<User> users = jdbcTemplate.query(
    "SELECT * FROM users WHERE age > ?", 
    // 步骤1:如何创建PreparedStatement (由你实现)
    ps -> ps.setInt(1, 18),
    // 步骤2:如何映射结果集到对象 (由你实现)
    (rs, rowNum) -> new User(rs.getString("name"), rs.getInt("age"))
);
  1. Spring 容器初始化过程中的各种 InitializingBean 和 init-method
  • 模板方法:Spring IoC 容器初始化 Bean 的生命周期流程
  • 需要子类实现的步骤
    • InitializingBean 接口:定义了 afterPropertiesSet() 方法。这是一个“抽象步骤”,Bean 可以实现此接口,在该方法中编写初始化逻辑。
    • @PostConstruct 注解或 init-method 配置:这是另一种形式的“填空”,让你指定初始化时要调用的方法。

Spring 的模板方法保证了所有 Bean 都会按照固定流程(实例化 -> 属性填充 -> 初始化 -> 可用)进行创建,而你将自定义的初始化逻辑“挂载”到了流程的“初始化”这个步骤上。

2.策略模式

设计思想:定义一系列的算法,并且使它们可以相互替换。允许在运行时动态地选择算法的不实现方式,而不需要更改客户端的代码结构。

Spring实现:HandlerMapping 与 HandlerAdapter

  • HandlerMapping 策略:定义请求映射策略。接口是HandlerMapping,具体策略有RequestMappingHandlerMapping(用于@RequestMapping)、SimpleUrlHandlerMapping(用于XML配置的URL映射)等。DispatcherServlet持有一组HandlerMapping列表,会按顺序询问它们:“这个请求你能处理吗?”。
  • HandlerAdapter 策略:定义如何执行Controller。接口是HandlerAdapter,具体策略有RequestMappingHandlerAdapter(执行@Controller方法)、SimpleControllerHandlerAdapter(执行旧的Controller接口实现)、SimpleServletHandlerAdapter(执行Servlet)等。DispatcherServlet根据找到的Handler(控制器)的类型,选择一个合适的HandlerAdapter来实际调用它。

3.责任链模式

设计思想将一个请求沿着一个处理链依次传递,直到有一个处理者能够处理该请求为止。这种模式将请求的发送者和接收者解耦,并允许多个处理者都有机会处理请求。

Spring实现: Spring Security 中的过滤器链就是责任链模式的一个应用。每个过滤器都可以选择是否处理当前的请求,如果处理,则将请求传递给下一个过滤器,如果不处理,则将请求返回给调用者。

在 Spring Security 源码中,过滤器链的实现主要使用了责任链模式。即将多个过滤器链接成一条链,每个过滤器依次处理请求,并将请求传递给下一个过滤器或目标资源。这样,每个过滤器只需要关注自己的业务逻辑,而不需要关心其他过滤器的存在。Spring Security 中的过滤器链负责处理 Web 请求,并按照一定的顺序依次调用不同的过滤器来完成安全认证、授权等操作。

代码体现

Filter 接口:

Spring Security 中的过滤器都实现了 Filter 接口,它定义了过滤器的基本行为, Filter 接口中的 doFilter() 方法用于处理请求,并将请求传递给下一个过滤器或目标资源。

public interface Filter {
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
}

FilterChain 接口:

Spring Security 中的过滤器链由 FilterChain 接口来管理,它定义了过滤器链的执行顺序, FilterChain 接口中的 doFilter() 方法用于执行过滤器链中的下一个过滤器或目标资源。

public interface FilterChain {
    void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}

FilterChainProxy 类:

在 Spring Security 中,FilterChainProxy 类是过滤器链的关键实现。它负责管理所有的过滤器链,并按照一定的顺序调用这些过滤器来处理请求。在 FilterChainProxy 类中,通过遍历所有的过滤器链,按照顺序调用过滤器来处理请求。如果找到了匹配的过滤器链,则调用该过滤器链来处理请求;如果没有找到匹配的过滤器链,则调用默认的 FilterChain 继续处理请求。

public class FilterChainProxy extends GenericFilterBean {
    private List<SecurityFilterChain> filterChains;
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 遍历所有的过滤器链,按照顺序调用过滤器
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                chain.doFilter(request, response);
                return;
            }
        }
        // 如果没有匹配的过滤器链,则调用默认的 FilterChain 继续处理请求
        chain.doFilter(request, response);
    }
}

4.状态模式

设计思想允许一个对象在其内部状态改变时改变它的行为。一个对象有多种状态,在不同的状态下,它的同一个行为会有不同的实现。状态模式将每一种状态封装成一个独立的类,让对象通过切换不同的状态类来改变自身的行为。

Spring实现Bean 生命周期阶段, Spring 中的 Bean 生命周期包括多个阶段,主要包括实例化、属性赋值、初始化、使用和销毁等阶段。

Spring Bean的生命周期可以被清晰地划分为几个状态(State)

  • Instantiated(实例化):Bean对象被创建出来(调用构造方法)。
  • Populated(属性填充):Bean的属性被注入(@Autowired, @Value等)。
  • Initializing(初始化中):正在执行初始化回调(@PostConstruct, InitializingBean)。
  • Ready(就绪/可用):初始化完成,Bean处于可用状态,可以被其他Bean依赖。
  • Destroying(销毁中):容器关闭,正在执行销毁回调(@PreDestroy, DisposableBean)。
  • Destroyed(已销毁):Bean完全被销毁。

每个生命周期由一系列回调方法控制,这些回调方法可以由开发者自定义并在 Bean 的不同生命周期阶段被 Spring 容器调用。其中包括:

afterPropertiesSet() 方法:在所有属性设置完成之后,Spring 容器会调用该方法来执行一些初始化操作。

init-method 属性:可以在 XML 配置文件中使用 init-method 属性定义 Bean 的初始化方法。

destroy() 方法:在 Bean 被销毁之前,Spring 容器会调用该方法执行一些清理操作。

destroy-method 属性:可以在 XML 配置文件中使用 destroy-method 属性定义 Bean 的销毁方法。

5.观察者模式

设计思想观察者模式表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,依赖这个对象的所有对象也会做出反应。

通俗来讲:就像微信公众号和订阅者。公众号(主题)一旦发布了新文章(状态改变),所有订阅了它的人(观察者)就会立刻在微信里收到通知。

Spring实现: 在 Spring 源码中,观察者模式被广泛应用于实现事件机制。Spring框架中事件驱动模型(Event-Driven Model) 就是基于观察者模式实现的。Spring 的事件机制允许应用程序中的组件监听特定类型的事件,并在事件发生时执行相应的逻辑。

事件驱动模型角色可以分为:

  1. 事件类: Spring 中的事件通常由具体的事件类表示,这些事件类通常继承自 ApplicationEvent 类。每个事件类通常包含与事件相关的信息,例如事件源、时间戳等。

    在 Spring 中,事件类通常继承自 ApplicationEvent 类,例如 ContextRefreshedEvent、ContextStartedEvent 等。这些事件类包含了与事件相关的信息。

  2. 事件监听器接口:Spring 提供了一个 ApplicationListener 接口,用于监听特定类型的事件。监听器实现该接口,并通过泛型指定要监听的事件类型。

    Spring 提供了 ApplicationListener 接口,用于监听特定类型的事件。事件监听器实现该接口,并实现 onApplicationEvent() 方法来处理事件。

    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
        void onApplicationEvent(E event);
    }
  3. 事件发布器:Spring 提供了一个 ApplicationEventPublisher 接口,用于发布事件。ApplicationContext 接口继承了 ApplicationEventPublisher,因此 ApplicationContext 是事件发布器的一个实现。
    ApplicationContext 接口继承了 ApplicationEventPublisher 接口,因此 ApplicationContext 是事件发布器的一个实现。通过调用 publishEvent() 方法来发布事件。

    public interface ApplicationEventPublisher {
        void publishEvent(ApplicationEvent event);
    }
  4. 事件监听器注册:在 Spring 中,事件监听器可以通过两种方式进行注册:通过在 Spring 配置文件中配置监听器 bean、通过代码注册监听器。

  5. 事件发布:当某个事件发生时,通过事件发布器发布该事件。事件发布器会将事件发送给所有注册的事件监听器。

    context.publishEvent(new MyCustomEvent(this));
  6. 事件处理:事件监听器收到事件后,会执行相应的事件处理逻辑。每个事件监听器可以根据需要监听多个不同类型的事件。

代码体现

// 1. 自定义事件(状态改变)
public class OrderCreatedEvent extends ApplicationEvent {
    private String orderId;
    public OrderCreatedEvent(Object source, String orderId) {
        super(source);
        this.orderId = orderId;
    }
    public String getOrderId() { return orderId; }
}

// 2. 具体主题(发布事件的地方)- 通常在Service中
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher publisher; // ApplicationContext实现了这个接口

    public void createOrder(Order order) {
        // ... 持久化订单等业务逻辑
        System.out.println("订单创建成功,ID: " + order.getId());
        // 发布事件!
        publisher.publishEvent(new OrderCreatedEvent(this, order.getId()));
    }
}

// 3. 具体观察者(监听事件)- 方式一:实现接口
@Component
public class EmailNotificationListener implements ApplicationListener<OrderCreatedEvent> {
    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        String orderId = event.getOrderId();
        System.out.println("[邮件监听器] 收到订单创建事件,准备为订单 " + orderId + " 发送确认邮件...");
        // sendEmail(...);
    }
}

// 4. 具体观察者(监听事件)- 方式二:使用@EventListener注解(更现代)
@Component
public class CacheRefreshListener {
    @EventListener // 通过方法参数类型智能监听事件
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("[缓存监听器] 收到订单创建事件,刷新订单缓存...");
        // refreshCache(...);
    }
}

6.迭代器模式

设计思想迭代器模式提供一种方法来顺序地访问一个容器(如列表、数组、集合等)中的元素,而又无需暴露该容器的内部表示。其中Java的集合框架遍历使用的迭代器Iterator就是迭代器模式最经典的实现。

Spring实现:Spring 源码中通常会有一个 BeanDefinitionIterator 类来实现对 BeanDefinition 的迭代操作。这个类通常是一个迭代器模式的实现,它封装了对 BeanDefinition 集合的遍历逻辑,并提供了统一的访问接口。

代码体现

在 BeanDefinitionIterator 类中,通过实现 Iterator 接口,封装了对 BeanDefinition 集合的遍历逻辑,提供了 hasNext()、next() 等方法用于访问 BeanDefinition。

public class BeanDefinitionIterator implements Iterator<BeanDefinition> {
    private final BeanDefinitionRegistry registry;
    private final String[] beanDefinitionNames;
    private int index = 0;
 
    public BeanDefinitionIterator(BeanDefinitionRegistry registry) {
        this.registry = registry;
        this.beanDefinitionNames = registry.getBeanDefinitionNames();
    }
 
    public boolean hasNext() {
        return index < beanDefinitionNames.length;
    }
 
    public BeanDefinition next() {
        String beanName = beanDefinitionNames[index];
        BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
        index++;
        return beanDefinition;
    }
 
    public void remove() {
        throw new UnsupportedOperationException("remove");
    }
}

使用示例:

BeanDefinitionRegistry registry = ...; // 获取 BeanDefinitionRegistry 对象
BeanDefinitionIterator iterator = new BeanDefinitionIterator(registry);
while (iterator.hasNext()) {
    BeanDefinition beanDefinition = iterator.next();
    // 处理 BeanDefinition
}

跟着Spring学设计模式
https://xhablog.online/2025/03/21/跟着Spring学设计模式/
作者
Xu huaiang
发布于
2025年3月21日
许可协议