之前我自己写了一个类似 Spring 中的 IoC 容器 自己实现 Spring IoC 容器(三)完成 IoC 容器,然后最近想在这个项目基础上把 Spring 的 AOP 也实现一下,然后就悲剧的发现了一句错误代码……
这个错误代码就在
类的
- edu.jyu.core.ClassPathXmlApplicationContext
方法中,下面是这个方法
- Object createBeanByConfig(Bean bean)
- /**
- * 根据bean的配置信息创建bean对象
- *
- * @parambean
- * @return*/
- privateObjectcreateBeanByConfig(Bean bean) {// 根据bean信息创建对象Class clazz =null;
- Object beanObj =null;try{
- clazz = Class.forName(bean.getClassName());// 创建bean对象beanObj = clazz.newInstance();// 获取bean对象中的property配置List properties = bean.getProperties();
- // 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中
- for(Property prop : properties) {
- Map params = newHashMap<>();if(prop.getValue() !=null) {
- params.put(prop.getName(), prop.getValue());// 将value值注入到bean对象中BeanUtils.populate(beanObj, params);
- }else if(prop.getRef() !=null) {
- Object ref = context.get(prop.getRef());// 如果依赖对象还未被加载则递归创建依赖的对象
- if(ref ==null) {
- ref = createBeanByConfig(bean);
- }
- params.put(prop.getName(), ref);// 将ref对象注入bean对象中BeanUtils.populate(beanObj, params);
- }
- }
- }catch(Exception e1) {
- e1.printStackTrace();throw newRuntimeException("创建"+ bean.getClassName() +"对象失败");
- }returnbeanObj;
- }
错误就在如果依赖对象还未被加载条件成立后,
这句代码的问题是什么了,很明显我一不小心又把当前要创建的 bean 对象的配置信息传入
- ref = createBeanByConfig(bean);
方法中了,所以就会无限递归下去,最后发生
- createBeanByConfig
错误。
- StackOverflowError
至于为什么我的测试代码能通过也是比较凑巧,我的测试 bean 是一个 A 类,一个 B 类,其中 B 类依赖 A 类对象,所以我们要把 A 类对象注入到 B 类中,但是就是这么巧,读取配置文件的时候先读到了 A 类,所以在要创建 B 类对象时,A 类对象已经创建好了,
就为 false,也就是说没执行到那句错误代码,所以就没发现……
- ref == null
要是我改为 A 类依赖 B 类,那就可以发现问题了,因为要创建 A 类对象时,B 类对象还没创建。
A 类
- package edu.jyu.bean;public classA {privateString name;privateB b;publicBgetB() {returnb;
- }public void setB(B b) {this.b = b;
- }publicStringgetName() {returnname;
- }public void setName(String name) {this.name = name;
- }
- }
B 类
- package edu.jyu.bean;public classB {private intage;public int getAge() {returnage;
- }public void setAge(intage) {this.age = age;
- }
- }
此时配置文件
也需要修改一下
- applicationContext.xml
- <?xml version="1.0" encoding="UTF-8" ?>
- <beans>
- <bean name="A" class="edu.jyu.bean.A">
- <property name="name" value="Jason">
- </property>
- <property name="b" ref="B">
- </property>
- </bean>
- <bean name="B" class="edu.jyu.bean.B" scope="prototype">
- <property name="age" value="13">
- </property>
- </bean>
- </beans>
测试类
也改一下
- TestApplicationContext
- package edu.jyu.core;import org.junit.Test;import edu.jyu.bean.A;import edu.jyu.bean.B;public class TestApplicationContext {
- @Test
- public void test() {
- BeanFactory ac = new ClassPathXmlApplicationContext("/applicationContext.xml");A a = (A) ac.getBean("A");A a1 = (A) ac.getBean("A");B b = (B) ac.getBean("B");B b1 = (B) ac.getBean("B");System.out.println(a.getB());System.out.println("a==a1 : "+(a==a1));System.out.println("b==b1 : "+(b==b1));}
- }
运行这个测试,你就会惊喜地发现爆栈了
解决上面的那个 Bug 并不难,只需要把那句错误代码
换成
- ref = createBeanByConfig(bean);
- ref = createBeanByConfig(config.get(prop.getRef()));
完整方法
- /**
- * 根据bean的配置信息创建bean对象
- *
- * @param bean
- * @return
- */
- privateObjectcreateBeanByConfig(Bean bean) {// 根据bean信息创建对象Class clazz =null;
- Object beanObj =null;try{
- clazz = Class.forName(bean.getClassName());// 创建bean对象beanObj = clazz.newInstance();// 获取bean对象中的property配置List properties = bean.getProperties();
- // 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中
- for(Property prop : properties) {
- Map params=newHashMap<>();if(prop.getValue() !=null) {params.put(prop.getName(), prop.getValue());// 将value值注入到bean对象中BeanUtils.populate(beanObj,params);
- }else if(prop.getRef() !=null) {
- Objectref= context.get(prop.getRef());// 如果依赖对象还未被加载则递归创建依赖的对象
- if(ref==null) {//下面这句的错误在于传入了当前bean配置信息,这会导致不断递归最终发生StackOverflowError
- //解决办法是传入依赖对象的bean配置信息
- //ref = createBeanByConfig(bean);
- ref= createBeanByConfig(config.get(prop.getRef()));
- }params.put(prop.getName(),ref);// 将ref对象注入bean对象中BeanUtils.populate(beanObj,params);
- }
- }
- }catch(Exception e1) {
- e1.printStackTrace();throw newRuntimeException("创建"+ bean.getClassName() +"对象失败");
- }returnbeanObj;
- }
现在运行测试类
的测试方法就没问题了
- TestApplicationContext
来源: http://blog.csdn.net/timheath/article/details/71515508