引言:设计模式是我们项目中经常会涉及到的项目进行重构、解构时的一种方法,像常见的单例模式、工厂模式、策略模式、装饰器模式都是比较常用的,关于 23 种设计模式,大家可以找本书专门去翻看一下,在 Java 框架的源码中也不例外,设计模式的使用实在是太多了,本文就来分析 Spring 中用到的设计模式。

题目

你了解的 Spring 都用到哪些设计模式?

推荐解析

你了解的 Spring 都用到哪些设计模式?_工厂类

工厂设计模式

优点

1)封装对象创建过程:工厂模式将对象的创建过程封装在工厂类中,客户端代码无需关心具体的创建细节,只需通过工厂类获取所需对象的实例,从而简化了对象的实例化过程。

2)解耦合:客户端代码与具体产品类解耦,减少了代码的依赖性,提高了代码的灵活性和可维护性。如果需要更换产品类,只需修改工厂类即可,而不必修改客户端代码。

3)复用性:由于对象的创建逻辑集中在工厂类中,可以在多个地方重复使用,避免了重复的创建代码,提高了代码的复用性。

4)单一职责原则:工厂类专门负责对象的创建,遵循了单一职责原则,每个类都专注于自己的任务,代码结构清晰。

5)更好的扩展性:如果需要添加新的产品类,只需扩展工厂类即可,符合开闭原则,系统扩展性较好。

缺点

1)类数量增加:引入了工厂类之后,类的数量将会增加,增加了系统的抽象性和理解难度。

2)增加了系统复杂度:虽然工厂模式能够将对象的创建过程封装起来,但也引入了额外的复杂度和间接性,不适合创建简单的对象。

3)不易于理解:对于初学者来说,理解工厂模式可能会增加一定的学习成本,需要理解工厂类、抽象产品类等概念。

使用场景

  • 当一个类不知道它所需要的对象的类时(例如需要根据条件动态创建对象),可以使用工厂模式。
  • 当一个类希望由它的子类来指定所创建的对象时,可以使用工厂模式。
  • 当类中的某些部分依赖于具体创建对象的时候,可以考虑使用工厂模式。
  • 当系统需要动态地决定创建哪些类的对象时,可以使用工厂模式。

Spring 源码

在 Spring 中,Bean 的创建通常通过工厂方法模式来实现。即通过一个工厂类(通常是实现了 FactoryBean 接口的类)来创建和管理 Bean 对象。

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

在 Spring 中,Bean 的创建也可以通过抽象工厂模式来实现。Spring 提供了多种抽象工厂来管理不同类型的 Bean,例如 BeanFactory 和 ApplicationContext。

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    boolean containsBean(String name);
    ...
}

单例设计模式

优点

1)全局唯一实例:确保一个类只有一个实例存在,可以节省系统资源,避免频繁创建和销毁对象。

2)全局访问点:提供一个全局的访问点来访问唯一实例,方便对实例进行统一的管理和操作。

3)延迟实例化:可以延迟对象的实例化,只有在第一次访问时才会创建实例,提高了系统的性能和资源利用率。

4)避免多次实例化:在多线程或分布式环境下,可以避免多次实例化相同的对象,确保对象的唯一性。

缺点

1)可能引起资源浪费:如果单例对象过早地创建并长时间占用内存,可能会引起资源浪费,特别是在对象占用大量资源时。

2)不适合动态环境:单例对象一旦创建之后,其状态可能会一直保持,不适合需要频繁改变状态的场景。

3)隐藏依赖关系:单例模式会隐藏类之间的依赖关系,增加了类之间的耦合度,使得代码难以理解和维护。

使用场景

  • 资源共享:需要共享某个资源(如数据库连接池、线程池等)的场景,可以使用单例模式确保全局唯一性,避免资源的多次创建和销毁。
  • 工具类:某些工具类只需要一个实例来提供全局的功能,例如日志工具类、配置管理类等。
  • 控制实例数目:某些类只需要一个实例来控制其实例数目,例如线程池、缓存管理器等。
  • 全局状态配置类:某些类需要保存全局的配置信息,例如系统配置类、全局状态类等。

Spring 源码

在 Spring 源码中,最常见的使用单例设计模式的场景就是在创建 Bean 实例时使用单例模式。Spring 容器默认情况下会将所有的 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);
        }
    }
    
    // 其他方法...
}

下一期讲解 Spring 的代理模式、模板方法、观察者模式,可以关注面试鸭公众号后收到第一时间的推文。

其他补充

你了解的 Spring 都用到哪些设计模式?_工厂模式_02

Spring 框架在设计和实现过程中广泛运用了多种设计模式,以下是一些常用的设计模式在 Spring 中的应用:

  1. 单例模式(Singleton Pattern):在 Spring 中,默认情况下 Bean 是单例的,即容器中只会创建一个实例。通过单例模式,Spring 确保了 Bean 在整个应用程序中只会创建一次,节省资源,提高性能。
  2. 工厂模式(Factory Pattern):Spring 中的 BeanFactory 和 ApplicationContext 接口都是工厂模式的典型应用,用于创建和管理 Bean 实例。
  3. 代理模式(Proxy Pattern):Spring AOP(面向切面编程)功能中使用了代理模式,通过代理对象来实现横切关注点的切面功能。
  4. 观察者模式(Observer Pattern):Spring 事件机制基于观察者模式,通过发布-订阅的方式实现事件的监听和处理。
  5. 模板模式(Template Pattern):Spring 中的 JdbcTemplate 和 RestTemplate 等模板类都是基于模板模式实现的,提供了统一的模板方法供开发者使用。
  6. 策略模式(Strategy Pattern):Spring AOP 中的 Advice 和 Pointcut 就是策略模式的应用,通过不同的策略实现不同的横切逻辑。
  7. 装饰者模式(Decorator Pattern):Spring 中的装饰者模式用于在 Bean 的初始化过程中添加额外的功能,如事务管理、日志记录等。
  8. 适配器模式(Adapter Pattern):Spring MVC 中的 HandlerAdapter 就是适配器模式的应用,用于适配不同类型的处理器。

除了上述列举的设计模式外,Spring 框架还涵盖了更多设计模式的应用,比如组合模式、模板方法模式、享元模式等。通过合理运用设计模式,Spring 实现了松耦合、可扩展、易维护的特性,成为一个功能强大、灵活性高的框架。

欢迎交流

本文主要讲解两个设计模式,工厂模式和单例模式,可以详细看下 Spring 的源码进行分析,在面试时候提及设计模式,可以用源码进行举例,是一个不错的选择,在文末还有三个问题,欢迎在评论区留言!

1)Spring 框架中的依赖注入是如何实现的?它与传统的依赖查找有什么区别和优势?

2)单例设计模式可以应用在哪些场景?

3)工厂模式一般可以配合哪些设计模式?