下面简单的基于反射机制,演示一下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
首先创建一个类
然后在类中创建静态变量以及静态方法
// 创建过的实体类都存在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 则代表开启多例模式
附上截图
方法主体
自定义方法1
自定义方法2