设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)

2023-12-19

原创 疯狂的狮子Li 狮子领域 程序圈 2023-12-19 10:30 发表于辽宁

原型模式

原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。

原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。

工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone

克隆羊问题

现有一只羊,姓名:tom,年龄:1,颜色:白色。克隆5只属性完全相同的羊

传统方式实例 这里使用lombok简化代码

优点: 好理解,无脑操作,啪啪啪。

缺点: 在创建新对象时,总是需要重新获取原始对象的属性,如果创建的对象属性比较多时就贼麻烦。总是重新初始化对象,而不是动态获取对象运行时的状态,不灵活。

改进思路: Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须实现一个接口 Cloneable ,改接口表示该类能够复制且具有复制的能力,即原型模式。

/**
 * @author LionLi
 */
@Data
public class Sheep {

    private String name;
    private int age;
    private String color;

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
}
/**
 * @author LionLi
 */
public class Test {

    public static void main(String[] args) {
        //传统的方法
        Sheep sheep1 = new Sheep("tom", 1, "白色");
        Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep3 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep4 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());
        Sheep sheep5 = new Sheep(sheep1.getName(), sheep1.getAge(), sheep1.getColor());

        System.out.println("sheep1:" + sheep1);
        System.out.println("sheep2:" + sheep2);
        System.out.println("sheep3:" + sheep3);
        System.out.println("sheep4:" + sheep4);
        System.out.println("sheep5:" + sheep5);
    }
}

浅拷贝与深拷贝

浅拷贝

对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。

因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

浅拷贝是使用默认的clone()方法来实现的,即 sheep = super.clone() ;

深拷贝

复制对象的所有基本数据类型的成员变量值,为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。

也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝

深拷贝实现方式1:重写clone方法来实现深拷贝

深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)

原型模式(浅拷贝)

/**
 * 注意使用原型模式实现clone克隆方法必须实现 Cloneable 接口不然会报错
 *
 * @author LionLi
 */
@Data
public class Sheep implements Cloneable {

    private String name;
    private Integer age;
    private String color;

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    @Override
    protected Sheep clone() {
        Sheep obj = null;
        try {
            obj = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }

}
/**
 * @author LionLi
 */
public class Test {

    public static void main(String[] args) {
        // 原型浅克隆
        Sheep sheep1 = new Sheep("tom", 1, "白色");
        Sheep sheep2 = sheep1.clone();
        Sheep sheep3 = sheep1.clone();
        Sheep sheep4 = sheep1.clone();
        Sheep sheep5 = sheep1.clone();

        System.out.println(sheep1 == sheep2);
        System.out.println(sheep2 == sheep3);
        System.out.println(sheep3 == sheep4);
        System.out.println(sheep4 == sheep5);
        System.out.println(sheep5 == sheep1);
    }
}

运行结果 五个对象内存地址全都不一样 克隆成功

图片

浅拷贝存在的问题

实体类中的对象 例如 Object List Map 等均为引用传递 浅拷贝是无法处理引用传递对象的

我们在Sheep中增加一个List对象 演示问题所在

/**
 * 注意使用原型模式实现clone克隆方法必须实现 Cloneable 接口不然会报错
 *
 * @author LionLi
 */
@Data
public class Sheep implements Cloneable {

    private String name;
    private Integer age;
    private String color;
    private List<String> strList;

    public Sheep(String name, int age, String color, List<String> strList) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.strList = strList;
    }

    @Override
    protected Sheep clone() {
        Sheep obj = null;
        try {
            obj = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }

}
/**
 * @author LionLi
 */
public class Test {

    public static void main(String[] args) {
        // 原型浅克隆
        List<String> list = new ArrayList<>();
        list.add("测试1");
        list.add("测试2");
        Sheep sheep1 = new Sheep("tom", 1, "白色", list);
        Sheep sheep2 = sheep1.clone();

        System.out.println(sheep1 == sheep2);
        System.out.println(sheep1.getStrList() == sheep2.getStrList());
    }
}

测试结论 对于引用对象List的内存地址是想同的 克隆失败

图片

深拷贝 方法一 重写clone方法

弊端: 编码复杂 存在硬编码 不利于扩展 改动较大

    @Override
    protected Sheep clone() {
        Sheep obj = null;
        try {
            obj = (Sheep) super.clone();
            // 这里我们重新创建一个 List 对象 将所有数据 copy 到新对象内
            obj.setStrList(new ArrayList<>(strList));
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }

重新测试 发现引用对象List也克隆成功了

图片

深拷贝 方法二 通过序列化/反序列化实现

首先 使用序列化需要实现JDK的序列化接口 Serializable 我们对实体类进行改造

这里我们讲述使用JDK自带方式进行序列化 也可以使用JSON工具进行序列化这里不多赘述

import java.io.Serializable;
import java.util.List;

/**
 * @author LionLi
 */
@Data
public class Sheep implements Serializable {

    private String name;
    private Integer age;
    private String color;
    private List<String> strList;

    public Sheep(String name, int age, String color, List<String> strList) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.strList = strList;
    }

}

然后对实体类增加 serializableClone 序列化克隆方法

    protected Sheep serializableClone() {
        // try-resources 写法 自动关闭流
        try (
            // 字节输出流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 对象输出流
            ObjectOutputStream oos = new ObjectOutputStream(bos)
        ) {
            // 将当前对象序列化为二进制输出到对象流内
            oos.writeObject(this);
            try (
                // 字节输入流
                ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                // 对象输入流
                ObjectInputStream ois = new ObjectInputStream(bis)
            ) {
                // 从对象流读取二进制反序列化为对象
                return (Sheep) ois.readObject();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

测试成功 Sheep对象与List对象均为不相等

图片

原型模式的注意事项和细节

  • 创建新对象比较复杂是,可以利用原型模式简化对象的创建过程,同时也能提高效率

  • 不用重新初始化对象,而是动态地获得对象运行时的状态

  • 如果原始对象发生变化,其它克隆对象也会发生相应的变化,无需修改代码

  • 在实现深克隆时可能需要比较复杂的代码

  • 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难。但对已有的类进行改造时,需要修改其源代码,违背了开闭原则(OCP)。

Spring中原型模式应用

Spring 中原型 bean 的创建,就是使用得原型设计模式

// @Scope("singleton") // 单例模式 默认单例
@Scope("prototype") // 原型模式 每次注入都会创建一个新的
@Bean()
public Object obj() {
  return new Object();
}

找到 AbstractBeanFactory 这个是 BeanFactory Bean工厂的抽象工厂,我们找到 doGetBean 方法 这个是所有 getBean 的最终执行方法

不了解 BeanFactory 的请参考另一篇文章  https://lionli.blog.csdn.net/article/details/135052626

图片

往下翻我们找到下方有个判断

图片

然后我们进入到 createBean 方法实现 找到 AbstractAutowireCapableBeanFactory 此类为 AbstractBeanFactory 的下层抽象工厂实现 用于在执行 @Autowire 自动注入时查找或创建bean实例

我们找到 doCreateBean 方法 此方法是所有 createBean 方法的最终执行方法

图片

图片

进入 createBeanInstance 创建bean实例方法 找到最后两行查看逻辑 具体的创建方式这里

图片

文章内容参考

https://blog.csdn.net/GXL_1012/article/details/110697058

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程) 的相关文章

随机推荐

  • 物流运输CRM:让日常工作有条不紊

    很多物流行业的企业主都有这样的烦恼 客户来自各行各业 很难细分管理 业务量大庞大 工作很难细化 客户满意度低 缺乏售后跟踪 如果您也面临相同的问题 那么该让CRM管理系统 登场啦 下面说说 物流运输行业 CRM系统有什么作用 1 客户分类管
  • CRM系统在IT软件行业的重要性和价值 | 发展趋势和选择理由

    如今 IT软件行业面临诸多问题 如推广成本的增加和交易周期的延长 很多销售人员埋怨产品好 有需求 但最终没有做到买卖的流程 为了改善这种业务问题 CRM管理系统是一个不错的选择 那样 IT软件行业CRM系统的好处是什么呢 IT软件行业面临哪
  • 金融CRM有用吗?金融行业CRM有哪些功能

    市场形式波诡云谲 金融行业也面临着资源体系分散 竞争力后继不足 未知风险无法规避等问题 金融企业该如何解决这些问题 或许可以了解一下CRM管理系统 和其提供的 金融行业CRM解决方案 金融行业是银行业 保险业 信托业 证券业和租赁业的总称
  • 普通USB摄像头转为网络摄像头

    普通USB摄像头转为网络摄像头 2022 02 25 Raspberry Pi Zero W吃灰有一段时间了 想着能否废物利用 使用普通USB摄像头改成一个RTSP协议网络摄像头 1 查看摄像头是否可用 查看是否已识别USB摄像头 lsus
  • 井盖位移传感器作用一览,井盖出现位移如何预警

    在城市的公共马路 小区住宅或工厂区路面上随处可见各种各样的井盖 不仅材质不一 而且每一个井盖背后的存在意义也是不同的 但是这些井盖有一个共同的特点 便是地上地下城市生命线的关键连接点 无论哪一种类型的井盖出现问题 即便是轻微的位移或者翻转
  • 解决Electron应用中的白屏问题的实用方法

    在使用Electron构建应用程序时 一些开发者可能会面临窗口加载过程中出现的白屏问题 这种问题主要分为两个方面 Electron未加载完毕HTML 这时Electron自身产生的白色背景可能导致用户在启动应用时看到一片空白 HTML加载渲
  • Java——关于实现多线程的测试小题,帮助我们更好的理解多线程的使用方法

    前面讲解了关于多线程的使用方法 这篇文章则是进行实战 做几道测试题 感兴趣的情况下可以看一下 Java多线程 多线程练习1 卖电影票 一共有1000张电影票 可以在两个窗口领取 假设每次领取的时间为3000毫秒要求 请用多线程模拟卖票过程并
  • ubuntu 20.04 prometheus-alertmanager

    prometheus alertmanager prometheus alertmanager focal updates focal security 0 15 3 ds 3ubuntu1 1 amd64 prometheus xmpp
  • Ceph入门到精通-smartctl 查看硬盘参数

    smartctl 参数含义 Model Family Toshiba s Enterprise Capacity HDD Device Model TOSHIBA MG08ACss Serial Number sssssss LU WWN
  • 华为mpls vpn 跨域方案B

    跨域方案B原理 缺点是两个as如果有多个ce的话 asbr pe压力大 1 pe和P都和单域一样配置 只是asbr pe配置不同 2 2个asbr pe配置上面不需要建立ip vpn instance 实例 3 2个asbr pe互联接口上
  • 坦克大战(二)

    欢迎来到程序小院 坦克大战 二 玩法 键盘 A W S D 键来控制方向 空格键发射子弹 N 下一关 P 上一关 Enter 开始 赶紧去闯关吧 开始游戏 https www ormcc com play gameStart 221 htm
  • u盘突然乱码然后文件都不见了怎么办

    在我们日常使用电脑时 U盘作为常用的移动存储设备 扮演了重要的角色 然而 有时我们可能会遇到U盘突然出现乱码并无法访问文件的问题 这不仅让人感到困惑 还可能丢失重要的数据 本文旨在分享几种解决U盘乱码文件不可见问题的方法 帮助您尽快恢复U盘
  • 桥梁结构健康监测系统的效果和作用

    随着城市化进程的加速 基础设施的重要性日益凸显 其中桥梁作为连接城市各个区域的交通枢纽 其结构安全对于城市的正常运行至关重要 为了全方位的保障有关于桥梁结构健康的安全性 万宾科技采用全新的科技理念 打造全套桥梁监测系统 WITBEE 万宾
  • 一款批量Linux应急响应检查工具

    fireman 简介 fireman用于在维护多台服务器并且需要定时检查服务器状态的场景下 使用自带命令可一键获取相关资源信息 排查服务器是否存在可疑用户 非法外连 文件更改等高危事件 使用 res模块 用于管理资源信息 添加资源 编辑资源
  • 机器学习 项目结构

    需求 我的项目文件夹下有许多文件 我想把我的项目单独放到一个文件夹 我的封装的模块放到一个一个文件夹方便管理 我该怎么做 这样做之后 主程序调用子模块需要在接口函数中调整路径吧 解决 将项目单独放到一个文件夹并将封装的模块放到另一个文件夹是
  • 谷歌Chrome浏览器无法安装插件的解决方法

    Google Chrome是一款由Google公司开发的网页浏览器 该浏览器基于其他开源软件撰写 包括WebKit 目标是提升稳定性 速度和安全性 并创造出简单且有效率的使用者界面 使用谷歌浏览器安装扩展插件的时候有时会遇到无法安装问题 解
  • 机器学习---决策树

    介绍 决策树和随机森林都是非线性有监督的分类模型 决策树是一种树形结构 树内部每个节点表示一个属性上的测试 每个分支代表一个测试输出 每个叶子节点代表一个分类类别 通过训练数据构建决策树 可以对未知数据进行分类 随机森林是由多个决策树组成
  • 井盖出现位移怎么办?井盖传感器效果一览

    井盖位移 井盖倾斜 井盖翻转 各种各样的问题应该怎么解决呢 井盖是城市基础设施建设过程之中不容忽视的一个重要部分 但是由于各种外界影响或者是内部的原因 可能会导致井盖出现位移等异常的现象 这不仅影响了路面的平整度 而且还可能会对路过的行人和
  • orcle定时器表达式梳理

    Oracle 定时任务执行时间间隔学习笔记 oracle 定时任务每隔1小时 CSDN博客 Oracle job 定时器的执行时间间隔也是定时器job 的关键设置 在这一设置上 开始还没掌握 总是不知道怎么写 现总结如下 其实主要是使用了T
  • 设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)

    原创 疯狂的狮子Li 狮子领域 程序圈 2023 12 19 10 30 发表于辽宁 原型模式 原型模式 Prototype模式 是指 用原型实例指定创建对象的种类 并且通过拷贝这些原型 创建新的对象 原型模式是一种创建型设计模式 允许一个