简单演示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

首先创建一个类

简单演示spring单例模式原理

 

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

    // 创建过的实体类都存在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 则代表开启多例模式

简单演示spring单例模式原理

 

附上截图

方法主体

简单演示spring单例模式原理

 

自定义方法1

简单演示spring单例模式原理

 

自定义方法2

简单演示spring单例模式原理

简单演示spring单例模式原理

上一篇:[题解]LeetCode 1109. 航班预订统计(C++)


下一篇:多线程与单线程的效率对比