Spring中常用设计模式简介

1. 单例模式

单例模式是一种创建型设计模式单例模式确保一个类只有一个实例,并提供全局访问点。例如,在应用程序中创建数据库连接池、缓存服务、日志管理器等,这些只需要单一实例的组件常采用单例模式实现,以避免资源浪费和状态混乱。

写法:

  • 双重检查锁+volatile懒汉式
Java 复制代码
public class Singleton {
    // 使用 volatile 关键字确保当 uniqueInstance 被初始化成 Singleton 实例时,
    // 多个线程可以正确处理 uniqueInstance 对象的可见性
    private volatile static Singleton uniqueInstance;

    // 构造函数私有化,防止外部直接实例化
    private Singleton() {}

    // 提供一个全局可访问的静态方法,返回唯一的 Singleton 实例
    public static Singleton getInstance() {
        if (uniqueInstance == null) { // 第一次检查:如果实例未创建
            synchronized (Singleton.class) { // 加锁
                if (uniqueInstance == null) { // 第二次检查:在同步块内再次检查
                    uniqueInstance = new Singleton(); // 创建唯一实例
                }
            }
        }
        return uniqueInstance;
    }
}
  • 静态内部类懒汉式
Java 复制代码
public class Singleton {
    // 私有构造函数,防止外部直接实例化
    private Singleton() {}

    // 定义一个静态内部类,该类仅在首次访问时被加载,且只会加载一次
    private static class SingletonHolder {
        // 静态内部类中创建Singleton的唯一实例
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供一个全局可访问的静态方法,返回唯一的Singleton实例
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE; // 直接返回静态内部类中已创建的实例
    }
}

例子: Spring Bean的单例模式。对于单例Bean,Spring会确保只创建一次实例,并将其缓存起来。这通常通过一个内部Map(如DefaultSingletonBeanRegistrysingletonObjects)来实现。对于后续的相同Bean请求,Spring容器直接从缓存中获取已创建的单例实例返回,无需再次创建

2. 工厂模式

工厂模式是一种创建型设计模式工厂模式提供了一种创建对象的封装机制,隐藏对象的具体创建过程,使用者只需关注所需产品的接口。例如,在数据库连接场景中,我们可以创建一个ConnectionFactory,根据配置信息返回MySQLConnection或OracleConnection,无需用户关心底层连接细节。

例子:

Spring框架的源码中广泛采用了工厂模式,无论是基础的BeanFactory接口,还是更具体的实现类如DefaultListableBeanFactory,都是工厂模式在Spring框架中的具体体现。通过提供getBean方法,让用户不用关心具体细节就能得到Bean对象,这极大地简化了对象的创建和管理,实现了依赖的解耦和配置的集中化。

3. 工厂方法模式

工厂方法模式是一种创建型设计模式是工厂模式的一种变体,抽象工厂(接口或抽象类)提供创建某一类对象的方法,对应的实际创建逻辑推迟到具体工厂(实现类)中。 例如,不同类型的图形(如圆形、矩形)可以由各自的图形工厂类生成,客户端仅调用抽象工厂的创建方法,由子类决定具体创建哪种图形对象。

例子:

BeanFactory和ApplicationContext接口两个接口就像工厂方法模式中的抽象工厂,提供了创建Bean的通用接口,它们的实现类(如DefaultListableBeanFactoryXmlBeanFactoryAnnotationConfigApplicationContext等)相当于具体工厂,在创建Bean时遵循了工厂方法模式,根据配置信息(如XML配置文件、注解配置等)来决定创建哪些Bean以及如何创建。

4. 抽象工厂模式

抽象工厂模式是一种创建型设计模式 ,是工厂方法模式的一种变体,与工厂方法模式思想类似,工厂方法模式关注于创建单一产品族的不同产品,抽象工厂模式关注于创建多个产品族的整套产品。例如,操作系统主题包中,一个抽象工厂可以提供创建窗口、按钮、菜单等一系列风格一致的UI组件,客户端通过选择不同的具体工厂(如Windows主题工厂、Mac主题工厂)来获得整套风格的组件。

例子:

JDBC。JDBC(Java Database Connectivity)规范定义了DriverManager类,它负责加载并管理各种数据库驱动。当需要连接数据库时,开发人员调用DriverManager.getConnection()方法,传入数据库URL和凭据。DriverManager会根据URL识别所需的驱动类型,通过驱动工厂(即实现了java.sql.Driver接口的类)创建与特定数据库系统的连接对象。此外,通过连接对象可以获取到StatementPreparedStatementResultSet等对象,这些对象也是由驱动工厂提供的,形成了一个与特定数据库系统相关的对象集合。

5. 建造者模式

建造者模式是一种创建型设计模式建造者模式将复杂对象的构建与表示分离,使得相同的构建过程可以创建不同的表示。例如,构建复杂的订单对象时,可以使用Builder类逐步添加商品、设置收货地址、选择支付方式等,最后得到完整订单。

例子:

  1. 在Spring的IoC容器中,BeanDefinitionBuilder类被用来构建BeanDefinition对象 ,这是描述Bean配置信息的一个复杂数据结构。通过BeanDefinitionBuilder,开发者可以逐步设置Bean的各种属性、构造函数参数、依赖项等,最终得到完整的BeanDefinition,用于注册到Spring容器中。这种构建过程遵循建造者模式,隐藏了BeanDefinition内部的复杂性,提供了清晰、易用的API。
typescript 复制代码
public final class BeanDefinitionBuilder {

   /**
    * Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
    */
   public static BeanDefinitionBuilder genericBeanDefinition() {
      return new BeanDefinitionBuilder(new GenericBeanDefinition());
   }

   /**
    * Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
    * @param beanClassName the class name for the bean that the definition is being created for
    */
   public static BeanDefinitionBuilder genericBeanDefinition(String beanClassName) {
      BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
      builder.beanDefinition.setBeanClassName(beanClassName);
      return builder;
   }
......
......
......
}   
  1. Configuration对象的构建 : MyBatis的核心配置对象Configuration包含了数据库连接信息、映射器、类型处理器、插件等各种复杂的配置细节。由于配置信息的多样性与复杂性,直接通过一个构造函数来初始化Configuration对象是不切实际的。因此,MyBatis使用了XMLConfigBuilder(解析XML配置文件)和SqlSessionFactoryBuilder(进一步处理配置信息并创建SqlSessionFactory)等类作为构建器,它们按照一定的步骤逐步构建并填充Configuration对象,实现了建造者模式。
Java 复制代码
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(reader);

6. 适配器模式

适配器模式是一种行为型设计模式。适配器模式将一个类的接口转换为客户期望的另一个接口,使得原本不兼容的类可以一起工作。例如,将第三方库的API封装为项目中统一的接口,或者在不同数据源之间进行数据格式转换时,都可以应用适配器模式。

例子:

  1. Advice适配Spring AOP提供了若干个内置的Advice适配器类,如MethodBeforeAdviceAdapterAfterReturningAdviceAdapterThrowsAdviceAdapter等,它们分别实现了对不同类型的Advice(如前置通知、后置通知、异常通知等)的适配,使得这些Advice能够被Spring AOP代理机制正确识别和应用。
Java 复制代码
public DefaultAdvisorAdapterRegistry() {
   registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
   registerAdvisorAdapter(new AfterReturningAdviceAdapter());
   registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
Java 复制代码
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

   @Override
   public boolean supportsAdvice(Advice advice) {
      return (advice instanceof MethodBeforeAdvice);
   }

   @Override
   public MethodInterceptor getInterceptor(Advisor advisor) {
      MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
      return new MethodBeforeAdviceInterceptor(advice);
   }

}
Java 复制代码
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

   @Override
   public boolean supportsAdvice(Advice advice) {
      return (advice instanceof AfterReturningAdvice);
   }

   @Override
   public MethodInterceptor getInterceptor(Advisor advisor) {
      AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
      return new AfterReturningAdviceInterceptor(advice);
   }

}
Java 复制代码
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {

   @Override
   public boolean supportsAdvice(Advice advice) {
      return (advice instanceof ThrowsAdvice);
   }

   @Override
   public MethodInterceptor getInterceptor(Advisor advisor) {
      return new ThrowsAdviceInterceptor(advisor.getAdvice());
   }

}
  1. MVC(模型-视图-控制器) : Spring MVC中的HandlerAdapter接口及其实现类构成了适配器模式的一个典型应用场景。HandlerAdapter的作用是将各种符合特定约定的处理器(如ControllerHttpRequestHandler等)适配到Spring MVC的请求处理流程中。这样,用户可以编写符合自己习惯的控制器类,而Spring MVC通过选择合适的HandlerAdapter来调用这些控制器的方法,处理HTTP请求并返回响应,无需控制器直接与MVC框架的内部细节耦合。
  2. 日志模块 : MyBatis的日志系统是适配器模式的一个经典示例。MyBatis并未直接实现自己的日志功能,而是提供了一个抽象的Log接口,然后针对市面上流行的日志框架(如SLF4J、Log4j、Log4j2、JUL、JCL等)编写了对应的适配器类,如Slf4jImplLog4jImpl等,这些类实现了Log接口并将日志操作委托给相应的日志框架。这样,无论用户项目中使用何种日志库,MyBatis都能通过适配器找到合适的日志实现进行日志记录,保持了与外部日志系统的松耦合。

7. 装饰器模式

装饰器模式是一种行为型设计模式装饰器模式即不改变原始组件的职责,而是通过动态地给组件添加"装饰"以增强其功能。在不改变原有类的情况下,通过组合而非继承的方式为对象扩展功能。如在咖啡订购系统中,通过添加各种调料(如糖、奶、巧克力酱)装饰基础咖啡,实现多种口味咖啡的组合。

例子:

  1. 装饰器模式在MyBatis的缓存机制中有典型应用 。MyBatis允许用户配置启用或禁用缓存,并且支持不同的缓存实现,例如一级缓存和二级缓存。当启用缓存时,MyBatis会使用装饰器模式来动态地增强Executor对象的能力,具体做法是在执行器之上添加一层缓存装饰器,如CachingExecutor。原始的Executor负责处理SQL执行的基本逻辑,而CachingExecutor作为装饰器在其基础上增加了缓存读写的功能 。这样做的好处在于,无论何时需要切换缓存策略或者禁用缓存,都不需要修改Executor的原有代码,而是通过添加或移除装饰器来完成。

  2. HTTP请求的过滤器链(如Servlet规范中的Filter)、拦截器链(如Spring MVC中的Interceptor)就是装饰器模式在实际应用中的经典示例。

    • 在Servlet规范中,Filter接口允许开发者编写自定义的过滤器,它们可以在HTTP请求到达Servlet之前以及离开Servlet之后执行某些操作,比如身份验证、日志记录、字符编码转换等。多个Filter可以串联起来形成一个过滤器链,每一个Filter都在原始请求处理流程的基础上添加了一层额外的功能,这正是装饰器模式的体现。

    • 在Spring MVC中,Interceptor(拦截器)也可以在控制器方法执行前后添加额外的操作,如统一异常处理、性能监控、权限验证等。尽管拦截器在技术实现上可能借助了AOP(面向切面编程)的思想,但从设计模式的角度看,它仍然符合装饰器模式的特点

8. 代理模式

代理模式是一种行为型设计模式。代理模式为对象提供一个代理对象,以便控制对原对象的访问。例如,远程代理用于网络通信中为本地对象提供远程对象的访问接口;保护代理可以控制对敏感对象的访问权限;缓存代理可以缓存目标对象的昂贵计算结果。

例子:

  1. Spring AOP(面向切面编程) : Spring AOP利用代理模式来实现诸如事务管理、日志记录、性能监控等功能。Spring通过创建代理对象(JDK动态代理或CGLIB代理)来包裹目标对象,当客户端调用代理对象的方法时,会触发代理方法,代理方法会在目标方法执行前后插入相应的通知(Advice),从而实现切面逻辑的织入。

    • JDK动态代理 :如果目标类实现了至少一个接口,Spring会使用JDK的java.lang.reflect.Proxy类来创建一个代理对象,代理对象的方法调用会被转发至InvocationHandler实现类处理,该实现类内嵌了切面逻辑。
    • CGLIB代理:对于没有实现接口的目标类,Spring会选择CGLIB库来生成一个继承自目标类的子类代理,子类重写了父类方法并在方法内部加入切面逻辑。
  2. Spring容器对Bean的生命周期管理 : Spring IoC容器在管理Bean的生命周期时,也会使用代理来增强Bean的功能,比如通过后置处理器(PostProcessor)对Bean进行进一步的设置和初始化

9. 观察者模式

观察者模式定义了一种一对多的依赖关系,当主题对象状态变化时,所有依赖它的观察者都会收到通知并自动更新。例如,在实时股票报价系统中,多个订阅者(观察者)可以注册到某个股票(主题),当股票价格变动时,系统自动通知所有订阅者更新显示。

例子:

Spring框架中确实使用了观察者模式来实现其事件驱动模型,具体体现在ApplicationEventApplicationListener接口的设计上。下面是一个简化的描述:

  1. org.springframework.context.ApplicationEvent 是Spring事件类的基类,代表了应用程序中发生的某种事件。

    Java 复制代码
    public class ApplicationEvent extends EventObject {
        // 构造函数和其他相关方法...
    }
  2. org.springframework.context.ApplicationListener 是事件监听器接口,任何实现了此接口的类都可以监听特定类型的ApplicationEvent。

    Java 复制代码
    @FunctionalInterface
    public interface ApplicationListener<T extends ApplicationEvent> {
        void onApplicationEvent(T event);
    }
  3. 当某个事件发生时,例如在一个ApplicationContext上下文中,可以通过调用publishEvent()方法发布事件,所有注册过的监听该事件类型的ApplicationListener将会被通知并执行相应的方法

    Java 复制代码
    1// 在ApplicationContext中
    2public void publishEvent(ApplicationEvent event) {
    3    for (ApplicationListener<?> listener : getApplicationListeners()) {
    4        if (listener instanceof SmartApplicationListener) {
    5            SmartApplicationListener smartListener = (SmartApplicationListener) listener;
    6            if (smartListener.supportsEventType(event.getClass())) {
    7                smartListener.onApplicationEvent(event);
    8            }
    9        } else {
    10            // Legacy listener, assuming it supports all events
    11            listener.onApplicationEvent(event);
    12        }
    13    }
    14}

10. 策略模式

策略模式是一种行为型设计模式。策略模式定义了一系列算法,并将每个算法封装为一个单独的类,使它们可以互换。客户端根据需求选择合适的策略。例如,在排序算法中,可以定义快速排序、归并排序、冒泡排序等策略,根据数据特性和性能要求在运行时动态选择。

例子:

在Spring数据源管理上使用了策略模式,允许用户根据不同的配置选择不同的数据源策略。例如,org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 类就利用了策略模式,可以根据运行时条件动态切换数据源。

以下是一个抽象类的简化示例,展示了如何定义一个数据源路由策略接口:

Java 复制代码
// 抽象类AbstractRoutingDataSource部分代码简化
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {

    // ...其他代码...

    private Object determineCurrentLookupKey() {
        // 这里通常会委托给策略决定返回哪个数据源的键值
    }

    public Connection getConnection() throws SQLException {
        DataSource dataSource = determineTargetDataSource();
        // 根据策略获取实际的数据源并连接
        return dataSource.getConnection();
    }

    protected DataSource determineTargetDataSource() {
        // 这个方法可以由子类实现具体的策略逻辑
    }

    // ...其他代码...
}

在实际应用中,开发者需要重写 determineTargetDataSource 方法来实现数据源选择的策略。

11. 责任链模式

责任链模式是一种行为型设计模式,它将请求的发送者与接收者解耦,通过构建一系列处理对象形成一条链。每个处理对象(链中的节点)都包含对下一个节点的引用,并负责在接收到请求时决定是否对其进行处理,或者将请求传递给链中的下一个节点。这种模式允许请求沿着链路传递,直到找到能够处理它的节点为止。例如过滤器/拦截器链:Web应用中对HTTP请求进行逐级预处理和后处理。

例子:

在Spring MVC中,过滤器链(Filter Chain)和拦截器链(Interceptor Chain)使用了责任链模式

12. 模板方法模式

模板方法模式是一种行为型设计模式,它在一个抽象类中定义了算法的骨架(模板方法),并将一些步骤延迟到子类中实现。模板方法封装了算法的整体结构,而子类可以通过继承这个抽象类并覆盖特定的步骤(方法)来实现对算法部分环节的个性化定制,而不改变算法的整体逻辑。例如应用框架初始化流程:如框架类定义启动、配置、初始化等步骤,具体实现由子类完成。

例子:

Springz的JDBC中JdbcTemplate 类就是一个模板方法模式的典型应用,它定义了一系列执行数据库操作的方法,如查询、更新、批处理等,这些方法内部调用抽象方法来完成具体的操作,而子类可以重写这些抽象方法来实现数据库驱动相关的逻辑。

Java 复制代码
public abstract class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    // ...

    /**
     * Execute a query given static SQL.
     * <p>The results will be mapped to an ArrayList (one entry per row) of HashMaps
     * (each representing a row, keyed by column name).
     * @param sql the SQL query to execute
     * @return a List that contains a Map per row
     */
    public List<Map<String, Object>> queryForList(String sql) throws DataAccessException {
        return query(sql, this::extractData, ResultSet::next);
    }

    // 抽象方法,留给子类实现
    protected abstract <T> T doExecute(JdbcOperationsCallback<T> action) throws DataAccessException;

    // 内部使用的回调方法,由模板方法调用
    private <T> T query(String sql, RowMapper<T> rowMapper, ResultSetExtractor<Boolean> rowCallbackHandler) {
        return doExecute((ConnectionCallback<T>) conn -> {
            PreparedStatement ps = getPreparedStatement(conn, sql);
            try {
                return getJdbcOperations().query(ps, rowMapper, rowCallbackHandler);
            }
            finally {
                closeStatement(ps);
            }
        });
    }

    // 子类可以覆盖这个方法来改变提取数据的行为
    protected <T> T extractData(ResultSet rs) throws SQLException, DataAccessException {
        throw new UnsupportedOperationException("Implementations must provide an implementation for this method");
    }

    // ...
}
相关推荐
晨米酱5 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机10 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机11 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤12 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤1 天前
工厂模式
设计模式