Spring用到了哪些设计模式?

科技   2024-11-19 09:08   广东  

松哥的 Spring 源码分析课程结束好久了,今天和大伙总结下 Spring 中都用到了哪些设计模式。

Spring 作为企业级应用开发中最受欢迎的框架之一,其内部广泛采用了多种设计模式,使得框架不仅功能强大,而且具有很高的可扩展性和灵活性。是我们学习设计模式不可多得的优质材料。

一 单例模式 (Singleton Pattern)

在 Spring 框架中,单例模式被广泛应用于各种组件和工具类,以确保在整个应用程序生命周期中,这些对象只有一个实例,从而节省内存和提高性能。

松哥这里给大家举几个常见的 Spring 中单例的应用。

BeanFactory

BeanFactory 是 Spring 框架中的另一个核心接口,它负责创建和管理 bean。BeanFactory 的实现类(如 DefaultListableBeanFactory)也通常以单例模式存在。

源码示例:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactoryBeanDefinitionRegistrySerializable 
{

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    @Override
    public Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    singletonObject = getEarlyBeanReference(beanName, mbd, bean);
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

DefaultListableBeanFactory 本身的初始化逻辑如下:

可以看到,如果存在 BeanFactory,则先销毁,再创建新的 BeanFactory。

二 工厂模式 (Factory Pattern)

工厂模式提供了一种创建对象的接口,但让子类决定实例化哪一个类。Spring 中的 BeanFactory 接口及其实现类(如 DefaultListableBeanFactory)就是工厂模式的应用。通过这些工厂,我们可以方便地管理和创建bean实例。

Spring 源码案例

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
}

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactoryBeanDefinitionRegistrySerializable 
{
    
    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, nullnullfalse);
    }
}

DefaultListableBeanFactoryBeanFactory 的一个实现,负责创建和管理 bean 的实例。

三 原型模式 (Prototype Pattern)

原型模式通过复制现有对象来创建新对象,而无需知道任何创建细节。在 Spring 中,我们可以通过设置 bean 的 scope 属性为 prototype 来实现每次请求时都创建一个新的 bean 实例。

Spring 源码案例

<bean id="exampleBean" class="com.example.ExampleBean" scope="prototype"/>

这个配置表示每次请求 exampleBean 时,都会创建一个新的实例。

四 模板方法模式 (Template Method Pattern)

在 Spring 框架中,模板方法模式被广泛应用于多个模块,以提供灵活且可扩展的解决方案。模板方法模式的核心思想是定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。这样,子类可以不改变算法结构的情况下重新定义算法的某些特定步骤。

这里松哥和大家分享两个经典的模版方法模式:JdbcTemplate 和 PlatformTransactionManager。

JdbcTemplate

JdbcTemplate 是 Spring JDBC 模块中的一个核心类,它使用模板方法模式来简化数据库操作。

模板方法:

  • execute:执行 SQL 语句的基本方法。
  • query:查询数据库的基本方法。
  • update:执行更新操作的基本方法。

具体实现:

  • queryForObject:查询单个对象。
  • queryForList:查询列表。
  • batchUpdate:批量更新。

源码示例:

public abstract class JdbcOperations {
    public <T> queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
        return queryForObject(sql, args, getJdbcOperations().new SingleColumnRowMapper(rowMapper));
    }

    public int update(String sql, PreparedStatementSetter pss) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL update [" + sql + "]");
        }
        Connection con = DataSourceUtils.getConnection(getDataSource());
        PreparedStatement ps = null;
        try {
            ps = con.prepareStatement(sql);
            pss.setValues(ps);
            int rows = ps.executeUpdate();
            if (logger.isDebugEnabled()) {
                logger.debug(rows + " rows affected");
            }
            return rows;
        } catch (Throwable ex) {
            // Handle exception
            throw translateException("PreparedStatement", sql, ex);
        } finally {
            JdbcUtils.closeStatement(ps);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
}

PlatformTransactionManager

PlatformTransactionManager 接口定义了事务管理的基本方法,具体的事务管理实现类(如 DataSourceTransactionManager)则提供了具体的实现。

模板方法:

  • getTransaction:获取事务。
  • commit:提交事务。
  • rollback:回滚事务。

具体实现:

  • DataSourceTransactionManager:基于数据源的事务管理。
  • JtaTransactionManager:基于JTA的事务管理。

源码示例:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {
    @Override
    protected TransactionStatus doBegin(Object transaction, TransactionDefinition definition) {
        // 获取数据库连接
        ConnectionHolder conHolder = (ConnectionHolder) transaction;
        Connection con = conHolder.getConnection();

        // 设置事务隔离级别
        Integer previousIsolationLevel = DataSourceUtils.storeIsolationLevelIfNotSet(con, definition.getIsolationLevel());

        // 开启事务
        boolean newTransaction = false;
        if (!con.getAutoCommit()) {
            logger.debug("Not switching JDBC Connection [" + con + "] to manual commit because already manually committed");
        } else {
            newTransaction = true;
            if (logger.isDebugEnabled()) {
                logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }
            con.setAutoCommit(false);
        }

        // 返回事务状态
        return new DataSourceTransactionObject(conHolder, previousIsolationLevel, newTransaction);
    }
}

五 适配器模式 (Adapter Pattern)

适配器模式将一个类的接口转换成客户希望的另一个接口。SpringMVC 中的 HandlerAdapter 接口及其多个实现类(如 RequestMappingHandlerAdapter)就是适配器模式的应用,它们负责处理不同类型的控制器方法。

Spring 源码案例

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

public class RequestMappingHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof HandlerMethod;
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((HandlerMethod) handler).invokeAndHandle(request, response);
    }
}

RequestMappingHandlerAdapter 适配了 HandlerMethod 类型的控制器方法,使其能够处理HTTP请求。

六 装饰者模式 (Decorator Pattern)

装饰者模式允许动态地给一个对象添加一些额外的职责。Spring AOP 中的切面实现可以看作是对原有对象的一种装饰。通过 @Around 注解定义的环绕通知可以在不改变原有业务逻辑的情况下增加额外的功能。

Spring 源码案例

public class TransactionInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 开始事务
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            Object result = invocation.proceed();
            transactionManager.commit(status);
            return result;
        } catch (RuntimeException ex) {
            transactionManager.rollback(status);
            throw ex;
        }
    }
}

TransactionInterceptor 是一个典型的装饰者模式应用,它在方法调用前后添加了事务管理的逻辑。

七 观察者模式 (Observer Pattern)

观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。Spring  中的 ApplicationEventApplicationListener 接口共同实现了观察者模式。

Spring 源码案例

public interface ApplicationListener<E extends ApplicationEvent{
    void onApplicationEvent(E event);
}

public class ContextRefreshedEvent extends ApplicationEvent {
    public ContextRefreshedEvent(Object source) {
        super(source);
    }
}

public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("Context refreshed!");
    }
}

MyApplicationListener 监听了 ContextRefreshedEvent 事件,当上下文刷新时,会输出一条消息。

八 代理模式 (Proxy Pattern)

代理模式为其他对象提供一个代理以控制对这个对象的访问。Spring AOP 使用动态代理技术(JDK 动态代理或 CGLIB)来实现代理模式。例如,当你在方法上添加事务管理注解 @Transactional 时,Spring 会自动创建一个代理对象来管理事务的开始和结束。

Spring 源码案例

public class DefaultAopProxyFactory implements AopProxyFactory {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            return new ObjenesisCglibAopProxy(config);
        } else {
            return new JdkDynamicAopProxy(config);
        }
    }
}

DefaultAopProxyFactory 根据配置选择使用 CGLIB 或 JDK 动态代理来创建代理对象。

九 组合模式 (Composite Pattern)

组合模式允许将对象组合成树形结构以表示“部分-整体”的层次结构。在 Spring 配置中,可以将多个 bean 组合在一起形成一个复杂的结构。

Spring 源码案例

<beans>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/testdb"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

这个配置文件中,jdbcTemplate 依赖于 dataSource,形成了一个简单的组合结构。

十 策略模式 (Strategy Pattern)

策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以互换。Spring 中的 Resource 接口及其多个实现类(如 ClassPathResource, FileSystemResource)就是策略模式的应用,可以根据需要选择不同的资源访问方式。

Spring 源码案例

public interface ResourceLoader {
    Resource getResource(String location);
}

public class DefaultResourceLoader implements ResourceLoader {
    @Override
    public Resource getResource(String location) {
        if (location.startsWith("classpath:")) {
            return new ClassPathResource(location.substring("classpath:".length()));
        } else {
            return new FileSystemResource(location);
        }
    }
}

DefaultResourceLoader 根据资源路径的前缀选择合适的 Resource 实现类。

十一 小结

通过上述案例,我们可以看到 Spring 框架巧妙地运用了多种设计模式,不仅提高了代码的复用性和可维护性,还增强了框架的灵活性和扩展性。希望这篇文章能帮助大家更好地理解和掌握 Spring 中的设计模式,如果有任何疑问或建议,欢迎在评论区留言交流。


SpringSecurity+OAuth2 实战

最新版的 Spring Security 和之前旧版的写法有了很大的变化,针对目前最新版的 Spring Security,松哥录制了一套视频教程:SpringSecurity+OAuth2 精讲。

点击查看视频目录。

关于松哥

9 年程序员生涯,Java 畅销书作者,华为云最具价值专家,华为开发者社区之星,GitHub 知名项目作者。

目前产品有 Java 项目课程、Java 简历指导、1V1 模拟面试等,如有需求欢迎来勾搭。

感兴趣小伙伴加微信备注 ss


江南一点雨
一站式Java全栈技术学习平台!
 最新文章