简单演示spring单例模式原理

下面简单的基于反射机制,演示一下spring的单例思想,同时也等于演示一下简单版springIOC思想

在模拟场景下,默认对象均是单接口实现类,不考虑太复杂的情况

①真实的spring容器也是各种hashmap

②要实现单例,核心就是判断想创建的对象在spring容器中是否已经存在 这里简单演示时判断的依据就是Class对象 . getinterfaces()的返回值(所以不考虑多接口情况)

③方法中的赋值操作和spring单例没有关系,只是希望在创建对象时,如果对象的属性是自定义的类,并且之前创建了,那么就拿之前创建的过来用而不是再创建一个新的

当时写这个简单演示的方法时,思路依据是“自动创建一个serviceImpl对象” 由于这个对象里有自定义类“DaoImpl 所以写了这个赋值操作

Class.forName(全类名)创建的Class对象和new对象不同,当全类名相同时,反复执行这个方法创建的都是同一个Class对象 new对象每次创建都是一个新对象

@AutoWired@Resource都是“注入注解”,从spring容器中取出对象赋给某个变量的属性,它们的实现方式就是Class反射、在spring容器中判断是否有值、取值赋值

  • 前者直接按类型查找 Class对象.getType
  • 后者在不设置名字时,先按照名字后按照类型 即先Class.getName Class.getType

首先创建一个类

publi class SimpleBeanFactory {

 

然后在类中创建静态变量以及静态方法

    // 创建过的实体类都存在factory这个map集合里 模拟spring的容器
    // 如果需要创建的对象在map里存在,并且设置创建方法为单例模式 就直接从map集合中读取对象并返回
    private static finalHashMap<Class,Object>factory =newHashMap<>();
    //方法重载 默认开启单例模式
    public static Object createBean(String className) {
        return createBean(className,true);
    }
    //方法主体 模拟spring自动创建对象 不再需要手动new对象
    public static Object createBean(String className,booleansingle) {
        //声明一个对象
        Object instance =null;
        try{
            //根据外部调用方法时传入的路径 创建一个Class对象
            Class<?>aClass =Class.forName(className);
            //默认为单例模式 根据外部传入的布尔值变更
            if(single) {
                //将aClass传入自定义方法 自定义方法的目的是搜索map集合中是否存在已经创建好的对象
                Object instanceByInterface =getInstanceByInterface(aClass);
                //如果返回的object不为空 代表从map中找到了需要创建的对象 直接返回这个对象
                if(instanceByInterface !=null) {
                    returninstanceByInterface;
                }
            }
            //如果设置为多例模式 或者需要创建的是新的对象 则继续执行下面的代码
            //根据Class对象获取类的无参构造器
            Constructor<?>constructor =aClass.getConstructor();
            //根据无参构造器实例对象 然后赋值给已经声明的变量instance
            instance =constructor.newInstance();
            //使用自定义方法 为新创建的对象设置属性值
            setField(aClass,instance);
            //获取Class对象指向的类实现的所有接口(由于该案例只是简单模拟 因此在这里只有一个接口)
            Class<?>[]interfaces =aClass.getInterfaces();
            //将接口作为key object对象作为value 存入map中 作为记录
            //以后只要Class对象指向的类有相同的接口 就默认指向的是同一个类 直接返回对应的value对象
            //这种判断在单接口的情况下没问题 算是一种简化
            for(Class interfac :interfaces) {
                factory.put(interfac,instance);
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        //返回创建好的对象
        return instance;
    }

 

接着是两个自定义的方法说明

	    //自定义方法的目的是搜索map集合中是否存在已经创建好的对象
    //自定义方法判定外部希望创建的对象是否已经存在于map集合中的依据是:
    //只要新传入的Class所指向的类的接口与map中的key有一个相同,就认为是指向同一个类,这是存在问题的,没有考虑类的多接口情况
    //但该案例只简单的模拟springIOC以及单例模式 所以大致思路了解即可
	    //外部传入的路径基于Class.forName生成的Class对象每次都不一样,但Class对象所指向的类的接口是确定的
    //遍历map集合 看Class对象所指向的类的接口是否与map中的key存在相同的情况 如果有则说明响应的value即object已经创建 此时直接返回这个对象
    private static Object getInstanceByInterface(Class aClass) {
	        Object o =null;
	        //获取传入的Class对象指向的类的所有接口 获得的是一个Class对象集合
        //只要接口是固定的 这样创建的Class对象也是固定的 每个Class对象都指向一个接口
        Class<?>[]interfaces =aClass.getInterfaces();
	        //循环接口集合
        for(Class cla :interfaces) {
	            //将指向接口的Class对象作为key传入map集合 获得value
            o =factory.get(cla);
	            //如果o不为空 说明此时的o就是外部需要创建的对象 直接返回这个对象
            if(o !=null) {
	                return o;
	            }
	        }
	        //如果o为空 则此时等于返回null
        return o;
	    }
	    //基于无参构造器创建对象后 对象的属性类型可能是自定义的类 如果这个自定义的类已经创建过一次并且存在map中
    //就通过遍历map找到这个对象 然后赋给新创建的对象的属性
    private static void setField(Class clazz,Object instance) {
	        try{
	            //获取Class对象指向的类的所有成员属性
            Field[]declaredFields =clazz.getDeclaredFields();
	            //遍历数组
            for(Field field :declaredFields) {
	                //getType() 返回Field对象指向的成员变量的类型 返回的是一个Class对象
                Class<?>type =field.getType();
	                //将Class对象作为key 去map中找是否有相同的key
                Object o =factory.get(type);
	                //如果o不为空 说明找到了相同的key 此时对应的value 即object对象为新创建的对象需要的成员属性
                if(o !=null) {
	                    //暴力访问当前属性 (针对private类的属性)
                    field.setAccessible(true);
	                    //利用set 将对象o传给instance对象中对应的属性
                    field.set(instance,o);
	                }
	            }
	        }catch(Exception e) {
	            e.printStackTrace();
	        }
	    }

此时的SimpleBeanFactory就是一个简单的工具类,能用于模拟springIOC以及单例

外部调用这个类时,传入一个全类名,就能自动创建一个对象,如果顺带着传入一个布尔值false 则代表开启多例模式

public class SimpleBeanFactoryTest { 
public static void main(StringC] args) { 
String className 
"com.i.eyæ.!l.dao.impl.UserInfoDaoImp1"; 
SimpleBeanFactory. c reateBean ( className ) 
SimpleBeanFactory. c reateBean ( className ) 
SimpleBeanFactory. c reateBean ( className ) 
Obj ect 
beanl 
System. out. println(beanl) ; 
String 
className2 
"com.j.gyg.ä!!.service.impl.UserInfoServiceImp1"; 
SimpleBeanFactory. createBean(c1assName2, single: false); 
Obj ect 
bean2

 

附上截图

方法主体

自 定 义 旄 “ 工 厂 
publi class SimpleBeanFactory { 
/ / 创 建 过 的 实 体 类 都 存 在 fa 匚 t 艹 y 这 个 map 集 合 里 模 拟 s “ 土 ng 的 容 器 
/ / 如 果 需 要 创 建 的 对 象 在 map 里 存 在 , 并 且 设 置 创 建 方 法 为 单 例 模 式 就 直 接 从 map 集 合 中 读 取 对 象 并 返 回 
private static final HashMap<C1ass, Object> fa 匚 亡 00 ' : new HashMap<>(), 
/ / 方 法 重 载 默 认 开 启 单 例 模 式 
P 
ublif static Object 、 eateBean(Stt 、 ing className) { 
single: true 
/ / 方 法 主 体 模 拟 sp ng 自 动 创 建 对 象 不 冉 需 要 手 动 n 宀 对 象 
ub1iC static Object C' 、 eateBean(Stt 、 ing className, 
/ / 声 明 一 个 对 象 
Object instance 
null; 
boolean single) { 
/ / 根 据 外 部 调 用 方 法 时 传 入 的 路 径 创 建 一 个 彐 s 对 象 
Class.forName(c1assName 
Class<?> aC1ass 
/ / 默 认 为 单 例 模 式 根 据 外 部 传 入 的 布 尔 值 变 更 
if (single) { 
/ / 将 传 入 自 定 义 方 法 自 定 义 方 法 的 目 的 是 扌 叟 索 map 集 合 中 是 否 存 在 已 经 创 建 好 的 对 象 
Object instanceByInterface : ge 亡 Ins 亡 anceByIn 亡 erface(aC1ass 
/ / 如 果 返 回 的 。 bject 不 为 空 代 表 从 map 中 找 到 了 需 要 创 建 的 对 象 直 接 返 回 这 个 对 象 
if (instance8yInterface ! : null) { 
r 、 etur 、 n instance8yInterface 
/ / 如 果 设 置 为 多 例 模 式 或 者 需 要 创 建 的 是 新 的 对 象 则 继 续 执 行 下 面 的 代 码 
/ / 根 据 彐 s 对 象 获 取 类 的 无 参 造 器 
aC1ass.getConstr 、 uctor 、 (), 
Constructor<?> constructor 
/ / 根 据 无 参 造 器 实 例 对 象 然 后 贝 武 值 给 已 经 声 明 的 变 量 土 nstance 
/ / 使 用 自 定 义 方 法 为 新 创 建 的 对 象 设 置 属 性 值 
setFieLd(aC1ass, instance 
/ / 获 取 彐 s 对 象 指 向 的 类 实 现 的 所 有 接 囗 ( 由 于 该 案 例 只 是 简 单 模 拟 因 此 在 这 里 只 有 一 个 接 囗 ) 
Class<?>C] interfaces 
aC1ass.getInterfaces

 

自定义方法1

/ / 自 定 义 方 法 的 目 的 是 扌 叟 索 m “ 集 合 中 是 否 存 在 已 经 创 建 好 的 对 象 
/ / 自 定 义 方 法 判 定 外 部 希 望 创 建 的 对 象 是 否 已 经 存 在 于 map 集 合 中 的 依 据 是 
/ / 只 要 新 传 入 的 c 所 指 向 的 类 的 接 囗 与 map 中 的 key 有 一 个 相 同 , 就 认 为 是 指 向 同 一 个 类 , 这 是 存 在 间 题 的 , 没 有 考 虑 类 的 多 接 囗 情 况 
/ / 但 该 案 例 只 简 单 的 模 拟 s “ 土 ng10C 以 及 单 例 模 式 所 以 大 致 思 路 了 解 即 可 
/ / 外 部 传 入 的 路 径 基 于 c . f 艹 me 生 成 的 c 对 象 每 次 都 不 一 样 , 但 c 对 象 所 指 向 的 类 的 接 囗 是 確 定 的 
/ / 谝 历 map 集 合 看 c 对 象 所 指 向 的 类 的 接 囗 是 否 与 map 中 的 key 存 在 相 同 的 情 况 如 果 有 则 说 明 响 应 的 IL 旧 即 。 bje 匚 t 已 经 创 建 此 时 直 接 返 回 这 
private static Object getInstanceByIntet 、 face Class aclass) { 
Obj ect 0 
null; 
/ / 获 取 传 入 的 c 对 象 指 向 的 类 的 所 有 接 囗 获 得 的 是 一 个 c 对 象 集 合 
/ / 只 要 接 囗 是 固 定 的 这 样 创 建 的 c 对 象 也 是 固 定 的 每 个 c 对 象 都 指 向 一 个 接 囗 
Class<?>C] interfaces 
aC1ass.getInterfaces 
/ / 循 环 接 囗 集 合 
for 、 (Class cla 
interfaces) { 
/ / 将 指 向 接 囗 的 c 对 象 作 为 key 传 入 map 集 合 获 得 IL 旧 
0 : factory.get(cla), 
/ / 如 果 。 不 为 空 说 明 此 时 的 。 就 是 外 部 需 要 创 建 的 对 象 直 接 返 回 这 个 对 象 
0 ; 
/ / 如 果 。 为 空 则 此 时 等 于 返 回 null 
对 象

 

自定义方法2

/ / 基 于 无 参 造 器 创 建 对 象 后 对 象 的 属 性 类 型 可 能 是 自 定 义 的 类 如 果 这 个 自 定 义 的 类 已 经 创 建 过 一 次 并 且 存 在 map 中 
/ / 就 涌 过 谝 历 map 找 到 这 个 对 象 然 后 贝 武 给 新 创 建 的 对 象 的 属 性 
private static void setFie1d(C1ass clazz, Object instance) { 
/ / 获 取 c 对 象 指 向 的 类 的 所 有 成 员 属 性 
clazz.getDec1at 、 edFie1ds(), 
FieldC] declaredFie1ds 
/ / 谝 历 数 组 
for 、 (Field field 
declaredFie1ds) { 
//getType() 返 回 eld 对 象 指 向 的 成 员 变 量 的 类 型 返 回 的 是 一 个 彐 s 对 象 
Class<?> type : field.getType(), 
//%C1ass 对 象 作 Y)key 去 map 中 找 是 否 有 相 同 的 key 
Object 0 : factory.get(type), 
/ / 如 果 鈈 为 空 说 明 找 到 了 相 同 的 key 此 时 对 应 的 1 艹 即 。 bject 对 象 为 新 创 建 的 对 象 需 要 的 成 员 属 性 
/ / 暴 力 访 间 当 前 属 性 ( 针 对 “ 毓 e 类 的 属 性 ) 
field.setAccessib1e()r 、 ue 
/ / 利 用 t 将 对 象 。 传 给 土 nstance 对 象 中 对 应 的 属 性 
field.set(instance, 0 ) 
} catch (Exception e) { 
e.printStackTrace

posted @ 2021-08-31 21:15  夏·舍  阅读(378)  评论(0编辑  收藏  举报