Spring + 设计模式 (五) 创建型 - 原型模式

68 阅读7分钟

原型模式

引言

原型模式是一种创建型设计模式,其核心在于通过复制现有对象来创建新对象,无需依赖复杂的构造函数或初始化逻辑。它利用“克隆”思想,让对象能够自我复制,特别适合创建成本高或配置复杂的对象。原型模式强调高效与灵活,仿佛为对象赋予了“分裂术”,使创建过程既快速又可控。

实际开发中的用途

在实际开发中,原型模式常用于需要频繁创建相似对象的场景,如配置对象、模板实例或复杂数据结构的复制。它避免了重复的初始化开销,特别适合高并发环境或对象构造复杂的情况。例如,在报表系统中,基于模板快速生成多份报表副本,或在游戏开发中复制敌人实例,都能显著提升性能。原型模式通过复制解耦了对象创建与客户端代码,增强了系统的可扩展性。

开发中的示例

设想一个文档管理系统,用户需要基于模板快速生成多份合同文档。每份合同内容相似,但部分字段(如客户名)不同。通过原型模式,可定义一个合同模板对象,客户端只需克隆模板并修改特定字段即可生成新合同。这种方式避免了重复构造复杂对象,提高了效率,同时保持模板一致性。

Spring 源码中的应用

Spring 框架中,原型模式在 Bean 作用域(prototype 作用域)的实现中体现得淋漓尽致。当 Bean 定义为 prototype 作用域时,Spring 容器每次请求该 Bean 都会创建一个全新实例,类似于通过原型克隆生成新对象。这种机制由 AbstractBeanFactorydoGetBean 方法及相关创建逻辑实现,确保每次返回的 Bean 都是独立的,客户端无需关心底层构造细节。

以下是 Spring 源码的典型片段(AbstractBeanFactory.java):

// Spring 框架中的 AbstractBeanFactory
protected <T> T doGetBean(
        String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) throws BeansException {
    // 获取规范化 Bean 名称并检查别名
    String beanName = transformedBeanName(name);
    Object beanInstance;

    // 检查是否存在单例实例(prototype 作用域不会缓存)
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    } else {
        // 获取 Bean 定义
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        checkMergedBeanDefinition(mbd, beanName, args);

        // 检查是否为 prototype 作用域
        if (mbd.isPrototype()) {
            // 创建新的原型 Bean 实例
            Object prototypeInstance = null;
            try {
                // 前置处理,记录原型创建状态
                beforePrototypeCreation(beanName);
                // 创建 Bean 实例,调用 createBean 方法
                prototypeInstance = createBean(beanName, mbd, args);
            } finally {
                // 后置处理,清理原型创建状态
                afterPrototypeCreation(beanName);
            }
            beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        } else {
            // 单例或其他作用域的处理逻辑
            // ...
        }
    }
    return (T) beanInstance;
}

这段代码展示了 Spring 如何通过原型模式实现 prototype 作用域的 Bean 创建。详细分析如下:

  1. 原型模式的体现
    • 当 Spring 检测到 Bean 为 prototype 作用域(mbd.isPrototype()),它不会从单例缓存中获取实例,而是通过 createBean 方法生成全新实例。这与原型模式的核心思想一致:通过“复制”或“新建”生成独立对象,而非复用已有对象。
    • createBean 方法(定义在 AbstractAutowireCapableBeanFactory 中)负责实例化 Bean,包括调用构造函数、处理依赖注入和执行初始化回调。每次调用生成新对象,类似于原型模式的“克隆”过程。
  2. 关键方法解析
    • beforePrototypeCreation(beanName)afterPrototypeCreation(beanName) 是原型模式生命周期管理的关键。它们记录当前正在创建的原型 Bean,防止高并发场景下的重复创建或状态混乱。
    • createBean 方法内部调用 doCreateBean,完成实例化、属性填充和初始化,确保新生成的 Bean 完全独立,符合原型模式要求。
  3. 解耦与扩展性
    • 原型模式在 Spring 中解耦了客户端与 Bean 创建过程。客户端只需通过 getBean 方法请求 Bean,无需了解构造逻辑或依赖关系。
    • Spring 的 IoC 容器通过 BeanDefinition 存储原型模板的元信息,每次创建时基于模板生成新实例,支持动态扩展,如通过配置文件或注解调整 Bean 属性,无需修改客户端代码。
  4. 实际问题解决
    • 原型模式解决了单例作用域无法满足的场景,如需要独立状态的对象(例如用户会话数据或临时配置)。通过 prototype 作用域,Spring 确保每个 Bean 实例互不干扰,适合高动态性需求。
    • 它还降低了复杂对象创建的性能开销。Spring 通过 BeanDefinition 缓存模板信息,避免重复解析配置,仅在需要时生成新实例。

这种实现使 Spring 的原型模式高效且灵活,广泛应用于需要动态生成对象的场景,如测试环境中的 mock 对象或多租户系统中的独立配置。

代码案例

以下是一个完整的、独立的 Java 代码案例,模拟一个图形编辑器场景,通过原型模式克隆图形对象。编辑器需要生成多个相似但位置或颜色不同的矩形图形,通过克隆原型模板高效创建新图形,清晰体现原型模式的“克隆”特性。

// 图形接口,支持克隆
public interface Shape {
    void setPosition(int x, int y);
    void setColor(String color);
    void draw();
    Shape cloneShape();
}

// 具体图形实现 - 矩形
public class Rectangle implements Shape, Cloneable {
    private int x;
    private int y;
    private String color;
    private int width;       // 模拟复杂配置
    private int height;      // 模拟复杂配置
    private String borderStyle; // 模拟复杂配置

    public Rectangle() {
        // 模拟复杂初始化过程
        this.width = 100;
        this.height = 50;
        this.borderStyle = "Solid";
        this.color = "Black";
        this.x = 0;
        this.y = 0;
    }

    @Override
    public void setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public void draw() {
        System.out.println("绘制矩形: 位置=(" + x + ", " + y + "), 颜色=" + color +
                ", 宽度=" + width + ", 高度=" + height + ", 边框样式=" + borderStyle);
    }

    @Override
    public Shape cloneShape() {
        try {
            return (Rectangle) super.clone(); // 浅克隆,适用于此场景
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("克隆失败", e);
        }
    }
}

// 原型管理器
public class ShapePrototypeManager {
    private Shape prototype;

    public ShapePrototypeManager(Shape prototype) {
        this.prototype = prototype;
    }

    public Shape createShape() {
        return prototype.cloneShape();
    }
}

// 主程序
public class GraphicsEditor {
    public static void main(String[] args) {
        // 创建矩形原型
        Shape rectanglePrototype = new Rectangle();

        // 使用原型管理器
        ShapePrototypeManager manager = new ShapePrototypeManager(rectanglePrototype);

        // 克隆生成矩形1
        Shape shape1 = manager.createShape();
        shape1.setPosition(10, 20);
        shape1.setColor("Red");
        shape1.draw();

        // 克隆生成矩形2
        Shape shape2 = manager.createShape();
        shape2.setPosition(50, 60);
        shape2.setColor("Blue");
        shape2.draw();

        // 克隆生成矩形3
        Shape shape3 = manager.createShape();
        shape3.setPosition(100, 120);
        shape3.draw();
    }
}

此 Java 示例通过图形编辑器场景清晰展示了原型模式。Rectangle 类实现了 Shape 接口和 Cloneable,通过 cloneShape 方法使用 Java 的 clone() 实现浅克隆(适用于此简单场景)。构造函数模拟了复杂初始化过程,设置了 widthheightborderStyle 等属性。ShapePrototypeManager 管理原型模板并提供克隆方法,解耦了客户端与克隆过程。在 GraphicsEditor 主程序中,通过克隆原型创建多个矩形图形,仅修改位置和颜色,避免了重复的复杂初始化。

运行代码将输出:

绘制矩形: 位置=(10, 20), 颜色=Red, 宽度=100, 高度=50, 边框样式=Solid
绘制矩形: 位置=(50, 60), 颜色=Blue, 宽度=100, 高度=50, 边框样式=Solid
绘制矩形: 位置=(100, 120), 颜色=Black, 宽度=100, 高度=50, 边框样式=Solid

原型模式在此通过克隆原型高效生成相似对象,保留复杂配置(如 widthheightborderStyle),仅需定制动态属性(如位置、颜色)。此案例直观、独立,完美体现了原型模式降低创建开销、提升灵活性的优势,适用于需要快速生成相似对象的场景。

总结

原型模式如同一台高效的“对象复印机”,通过克隆现有对象大幅降低创建成本,赋予系统无与伦比的灵活性。在 Spring 中,prototype 作用域将原型模式融入 IoC 核心,通过 AbstractBeanFactory 的精细实现,确保每次请求生成独立实例,完美支持动态对象生成。在实际开发中,原型模式通过克隆模板应对复杂对象创建的性能挑战,让代码如行云流水般优雅。掌握原型模式,不仅能优化性能,更能实现“一次定义,无限复制”的开发哲学。

(对您有帮助 && 觉得我总结的还行) -> 受累点个免费的赞👍,谢谢