在使用 IoC 容器时,我们需要先配置容器,包括注册需要管理的对象、配置对象之间的依赖关系以及对象的生命周期等。
然后,IoC 容器会根据这些配置来动态地创建对象,并把它们注入到需要它们的位置上。
当我们使用 IoC 容器时,需要将对象的配置信息告诉 IoC 容器,这个过程叫做依赖注入(DI),而 IoC 容器就是实现依赖注入的工具。
因此,理解 IoC 容器就是理解它是如何管理对象,如何实现 DI 的过程。
以下几个大点,则是在实现方案时的流程与难点。
从XML读取Bean配置
首先是要读取xml,这部分内容其实就是 ClassPathXmlApplicationContext
类的构造函数。
1 2
| Resource res = new ClassPathXmlResource(fileName);
|
在 ClassPathXmlResource
中,使用了SAXReader插件,可以更好的读取xml结构的文档。
1 2 3 4 5 6 7 8 9 10 11 12
| public ClassPathXmlResource(String fileName) { SAXReader saxReader = new SAXReader(); URL xmlPath = this.getClass().getClassLoader().getResource(fileName); try { this.document = saxReader.read(xmlPath); this.rootElement = document.getRootElement(); this.elementIterator = this.rootElement.elementIterator(); } catch (Exception e) { throw new RuntimeException(e); } }
|
然后再声明:
1 2 3 4 5 6
| DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
reader.loadBeanDefinitions(res);
|
在 XmlBeanDefinitionReader
中,会循环判断 while (res.hasNext())
,
是否有bean ? 若有,是 constructor-arg
还是 property
类型?
依次将bean的 id 、 class
,以及其中的 type 、name 、value 、ref(property类型)
存储到 ConstructorArgumentValues
或 PropertyValues
中,最后汇总存入beanDefinition
中。
这样,就实现了bean的配置的读取。主要是两步 :
- 读取xml ,使用
ClassPathXmlResource
,主要依赖SAXReader类
- 根据处理完的xml结果读取 bean配置,主要是遍历循环
Element element = (Element)res.next();
中的元素,来将配置存入 beanDefinition
。
实现依赖注入
实现依赖注入这个时机,就是在createBean的时候。
传入的是BeanDefinition,通过doCreateBean()
和handleProperties()
两个函数,分别 先后对Constructor方法和Property方法 进行依赖注入。
分两阶段的原因就是为了解决循环依赖问题。
依赖注入的具体方法,是通过反射:
- 获取bean类型 :
clz = Class.forName(bd.getClassName());
- 获取构造器参数 :
ConstructorArgumentValues argumentValues = bd.getConstructorArgumentValues();
,遍历这些argumentValues,可以获得一个 paramTypes列表 和 paramValues列表 。
- 获取类的构造函数 :
con = clz.getConstructor(paramTypes);
。通过调用 clz.getConstructor(paramTypes)
,你可以获得一个与这些参数类型匹配的构造函数。
- 创建类的实例:
obj = con.newInstance(paramValues);
con.newInstance()
方法允许你使用构造函数来创建类的实例
- 获取
property
标签的参数名、类型、值。,得到paramValues列表。
- 通过拼接属性名生成相应的 setter 方法名 ,
String methodName = "set" + pName.substring(0,1).toUpperCase() + pName.substring(1);
- 通过反射获取 setter 方法,
method = clz.getMethod(methodName, paramTypes);
- 执行方法,实现依赖注入,
method.invoke(obj, paramValues);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| private Object createBean(BeanDefinition bd) { Class<?> clz = null; Object obj = doCreateBean(bd);
this.earlySingletonObjects.put(bd.getId(), obj);
try { clz = Class.forName(bd.getClassName()); } catch (ClassNotFoundException e) { e.printStackTrace(); }
populateBean(bd, clz, obj);
return obj; }
private Object doCreateBean(BeanDefinition bd) { Class<?> clz = null; Object obj = null; Constructor<?> con = null;
try { clz = Class.forName(bd.getClassName());
ConstructorArgumentValues argumentValues = bd.getConstructorArgumentValues(); if (!argumentValues.isEmpty()) { Class<?>[] paramTypes = new Class<?>[argumentValues.getArgumentCount()]; Object[] paramValues = new Object[argumentValues.getArgumentCount()]; for (int i=0; i<argumentValues.getArgumentCount(); i++) { ConstructorArgumentValue argumentValue = argumentValues.getIndexedArgumentValue(i); if ("String".equals(argumentValue.getType()) || "java.lang.String".equals(argumentValue.getType())) { paramTypes[i] = String.class; paramValues[i] = argumentValue.getValue(); } ······ } try { con = clz.getConstructor(paramTypes); obj = con.newInstance(paramValues); } } else { obj = clz.newInstance(); }
} System.out.println(bd.getId() + " bean created. " + bd.getClassName() + " : " + obj.toString());
return obj;
}
private void populateBean(BeanDefinition bd, Class<?> clz, Object obj) { handleProperties(bd, clz, obj); } private void handleProperties(BeanDefinition bd, Class<?> clz, Object obj) { System.out.println("handle properties for bean : " + bd.getId()); PropertyValues propertyValues = bd.getPropertyValues(); if (!propertyValues.isEmpty()) { for (int i=0; i<propertyValues.size(); i++) { PropertyValue propertyValue = propertyValues.getPropertyValueList().get(i); String pName = propertyValue.getName(); String pType = propertyValue.getType(); Object pValue = propertyValue.getValue(); boolean isRef = propertyValue.getIsRef(); Class<?>[] paramTypes = new Class<?>[1]; Object[] paramValues = new Object[1]; if (!isRef) { if ("String".equals(pType) || "java.lang.String".equals(pType)) { paramTypes[0] = String.class; } ······ else { paramTypes[0] = String.class; }
paramValues[0] = pValue; } else { try { paramTypes[0] = Class.forName(pType); } catch (ClassNotFoundException e) { e.printStackTrace(); } try { paramValues[0] = getBean((String)pValue); } catch (BeansException e) { e.printStackTrace(); } } String methodName = "set" + pName.substring(0,1).toUpperCase() + pName.substring(1);
Method method = null; try { method = clz.getMethod(methodName, paramTypes); } c try { method.invoke(obj, paramValues); } } }
}
|
解决依赖循环
在某个 Bean 需要注入另一个 Bean 的时候,如果那个 Bean 还不存在,该怎么办?
Spring是三层缓存解决依赖循环,这里使用了两层 。
主要代码体现在AbstractBeanFactory类中,此类实现了循环依赖问题,体现在**getBean(), createBean()* *和* *doCreateBean()*
*三个方法中。
- createBean()* 方法中调用了一个 doCreateBean(bd) *方法,专门负责创建早期的毛胚实例,毛坯中包含了这个bean的constructor参数。
- 毛胚实例创建好后会放在 earlySingletonObjects 结构中,然后 createBean() 方法再调用 handleProperties() 补齐这些 property *的值。
- 在 getBean() 方法中,首先要判断有没有已经创建好的 *bean**,
- *** -*有的话直接取出来,如果没有就检查 earlySingletonObjects 中有没有相应的毛胚 Bean,
- -在毛胚中有的话直接取出来,没有的话就去创建,
- *** -*并且会根据 Bean 之间的依赖关系把相关的 Bean 全部创建好。
所谓的三级缓存:
- singletonObjects:用于存储完全创建好的单例bean实例。
- earlySingletonObjects:用于存储早期创建但未完成初始化的单例bean实例。即毛坯
- singletonFactories:用于存储创建单例bean实例的工厂对象。(miniSpring没有这一层缓存,这一层体现在 new instance操作
obj = clz.newInstance();
,只不过没有用factory封装。)
支持注解
首先要创造一个注解类,叫做 @Autowired
,其逻辑如以下代码 :
- 判断这个bean是否有
@Autowired
注解:boolean isAutowired = field.isAnnotationPresent(Autowired.class);
- 如果有的话,则通过
getBean()
方法从 Bean 工厂中获取对应的依赖对象,字段的名字被用作 Bean 的名称。
- 使用反射将依赖对象注入到字段中。首先将字段设置为可访问状态,然后使用
field.set(bean, autowiredObj)
将依赖对象设置到 Bean 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { Object result = bean; Class<?> clazz = bean.getClass(); Field[] fields = clazz.getDeclaredFields(); if(fields!=null){ for(Field field : fields){ boolean isAutowired = field.isAnnotationPresent(Autowired.class); if(isAutowired){ String fieldName = field.getName(); Object autowiredObj = this.getBeanFactory().getBean(fieldName); try { field.setAccessible(true); field.set(bean, autowiredObj); System.out.println("autowire " + fieldName + " for bean " + beanName); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }
} } } return result; }
|
构建体系
增强BeanFactory的扩展性
- ListableBeanFactory
- ConfigurableBeanFactory
- ConfigurableListableBeanFactory:AutowireCapableBeanFactory、ListableBeanFactory、ConfigurableBeanFactory
- 设计原则: interface segregation (接口隔离原则)
增加环境因素
- 意义:使一些容器整体所需要的属性有个地方存储访问
- core目录–>新建env目录 :
- 增加PropertyResolver.java接口
- 增加EnvironmentCapable.iava接口
- 增加Environment.java接口
loC引擎: DefaultListableBeanFactory
- 继承了其他BeanFactory类来实现bean的创建管理功能
- ClassPathXmlApplicationContext运行时,注入DefaultListableBeanFactory
完善事件的发布与监听
- ApplicationEvent
- ApplicationListener
- ApplicationEventPublisher
- ContextRefreshEvent
- 实现一个最简单的事件发布者SimpleApplicationEventPublisher