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

news2024/10/5 0:49:53

原型模式

原型模式(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 的请参考另一篇文章 Spring 的工厂模式 BeanFactory 是什么源码刨析

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

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

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

进入 createBeanInstance 创建bean实例方法
找到最后两行查看逻辑

文章内容参考

Java设计模式——原型模式(实例)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1321981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

技术分享-Jenkins

持续集成及Jenkins介绍 软件开发生命周期叫SDLC&#xff08;Software Development Life Cycle&#xff09;&#xff0c;集合了计划、开发、测试、部署过程。 在平常的开发过程中&#xff0c; 需要频繁地&#xff08;一天多次&#xff09;将代码集成到主干&#xff0c;这个叫持…

电子烟单片机方案开发,32位单片机PY32F030电子烟解决方案

电子烟是一种低压的微电子雾化设备。可以通过加热液体产生雾状物质&#xff0c;供用户吸入使用的新型电子产品。它是由微控制器&#xff08;MCU&#xff09;、超声波雾化发生器、充电管理IC、锂离子电池、发热棒等器件构成&#xff0c;主要用于替代传统香烟和戒烟&#xff0c;与…

一文读懂什么是智能工厂?

引言 在当今快速变革的制造业中&#xff0c;智能工厂如一盏明灯&#xff0c;照亮着未来生产的道路。它们不仅代表着技术的进步&#xff0c;更是制造业向前迈进的里程碑。智能工厂利用先进的技术和创新方法&#xff0c;将传统工厂转化为高度自动化、数字化和智能化的生产中心。…

13英寸MacBook Pro停产后 Touch Bar功能被废弃

新款M3 MacBook Pro的发布标志着苹果13英寸MacBook Pro正式停产。这款13英寸MacBook Pro是最后一款搭载Touch Bar的苹果笔记本&#xff0c;这意味着苹果已经放弃了使用Touch Bar。 Touch Bar是一块OLED触控显示屏&#xff0c;位于MacBook Pro键盘的顶部。尽管苹果对它抱有很高的…

MyBatis的查询方法!!!

准备工作&#xff1a;1.创建一个maven工程&#xff0c;然后将pojo类导入到项目中去。 2.导入依赖到pom.xml文件中 3.在resources中创建log4j.properites和mybatis-config.xml 4.创建UserMapper接口和UserMapper.xml文件 5.创建测试类MyBatisTest 1.创建Maven工程&#xff0c;还…

蓝桥杯嵌入式——串口

CUBE里配置成异步模式&#xff0c;设置波特率&#xff0c;打开中断&#xff08;先配置LCD再配置串口&#xff09;&#xff1a; 串口发送 main.c #include "string.h" char temp[20]; sprintf(temp,"Hello World\r\n"); HAL_UART_Transmit(&huart1,(…

绝地求生是国际服吗?

绝地求生&#xff08;PlayerUnknowns Battlegrounds&#xff0c;简称PUBG&#xff09;是一款多人在线的生存竞技类游戏&#xff0c;由韩国的蓝洞公司开发和发行。该游戏于2017年正式发布&#xff0c;以其创新的游戏模式和激烈的战斗场景迅速走红全球&#xff0c;并成为全球最具…

魔众文库系统v5.8.0版本发布:水印、分类与移动端升级,打造更高效文档管理体验

魔众文库系统迎来了全新的v5.8.0版本更新&#xff01;此次更新不仅对水印功能进行了升级&#xff0c;还新增了辅助分类样式&#xff0c;同时优化了移动端体验。让我们一起来看看这次更新的亮点吧&#xff01; 一、水印功能全新升级 在v5.8.0版本中&#xff0c;魔众文库系统的…

Java实现一个在windows环境下的文件搜索引擎

以下是一个简单的Java实现的Windows文件搜索引擎的示例代码&#xff1a; import java.io.File; import java.util.ArrayList; import java.util.List;public class FileSearchEngine {public static void main(String[] args) {String searchDirectory "C:/"; // …

element plus 日期范围 自定义内容

问题&#xff1a; 按照官网上的自定义内容示例&#xff0c;修改日期选择器没有问题&#xff0c;如果修改日期范围选择器&#xff0c;修改后会丢失日期范围选择时的样式。 解决&#xff1a; 从F12中不难看出日期范围的选择样式来自于.el-date-table-cell 而示例中写的是.cell&…

23--数据结构简述

常见的数据结构 数据存储的常用结构有&#xff1a;栈、队列、数组、链表和红黑树。 1、栈 特点&#xff1a;先进后出 2、队列 特点&#xff1a;先进先出 3、数组 数组特点&#xff1a;查询快 &#xff0c; 增删慢 整形数组&#xff1a; 对象数组&#xff1a; 4、链表 链…

案例分享|企业为什么要选择数字化转型?

数字化在现代社会中扮演着重要的角色&#xff0c;成为企业转型的必由之路。随着科技的发展和信息化的进程&#xff0c;越来越多的企业开始拥抱数字化转型&#xff0c;以应对市场的变化和竞争的压力。数字化带来了诸多好处&#xff0c;不仅提高了企业的效率和生产力&#xff0c;…

阿里云登录镜像仓库报错: Error response from daemon: Get https://

阿里云登录镜像仓库报错: Error response from daemon: Get https:// 1. 故障现象 开发反应自用笔记本连接镜像仓库是正常的,但通过服务器连接镜像仓库一直失败. 报错信息如下: Error response from daemon: Get https://registry.cn-hangzhou.aliyuncs.com/v2/: unauthoriz…

力扣刷题记录(16)LeetCode:62、63、343、96

目录 62. 不同路径 63. 不同路径 II 343. 整数拆分 96. 不同的二叉搜索树 总结 这题比较简单&#xff0c;直接声明一个二维数组来保存到达该点有几种路径。到达当前点的方法由当前点的左边格子和右边格子决定。 class Solution { public:int uniquePaths(int m, int n)…

美国联邦机动车安全标准-FMVSS

FMVSS标准介绍&#xff1a; FMVSS是美国《联邦机动车安全标准》&#xff0c;由美国运输部下属的国家公路交通安全管理局(简称NHTSA)具体负责制定并实施。是美国联邦政府针对机动车制定的安全标准&#xff0c;旨在提高机动车的安全性能&#xff0c;减少交通事故中的人员伤亡。F…

构建智慧储能物联网,4G工业路由器远程监测在线管理

物联网技术的发展为智慧储能管理带来了革命性的变化。其中&#xff0c;4G工业路由器IR5000通过丰富的连接能力如串口RS485/232或网口的方式&#xff0c;实现了与储能现场各设备的连接&#xff0c;包括电表、电能检测器、防孤岛装置、BMS电池管理系统、监控服务器、储能控制器、…

万户OA text2Html接口存在任意文件读取漏洞

文章目录 产品简介漏洞概述指纹识别漏洞利用修复建议 产品简介 ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品&#xff0c;统一的基础管理平台&#xff0c;实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网…

react基于antd二次封装spin组件

目录 react基于antd二次封装spin组件组件使用组件效果 react基于antd二次封装spin组件 组件 import { Spin } from antd; import propTypes from "prop-types"; import React from react; import styleId from "styled-components"; // 使用 父div必须加…

HTML5+CSS3小实例:纯CSS实现网站置灰

实例:纯CSS实现网站置灰 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content="…

【1.10计算机组成与体系结构】校验码

目录 1.奇偶校验2.循环校验码CRC3.各个校验码的区别&#xff08;海明校验&#xff09; 1.奇偶校验 &#x1f7e2; 奇偶校验码的编码方法是: 由若干位有效信息 (如一个字节) &#xff0c;再加上一个二进制位 (校验位) 组成校验码。 &#x1f7e2; 奇校验:整个校验码 (有效信息位…