自己实现Spring IoC容器(四)IoC容器的Bug
来源:互联网 发布:asp.net crm系统源码 编辑:程序博客网 时间:2024/06/06 03:52
Bug的发现
之前我自己写了一个类似Spring中的IoC容器 自己实现Spring IoC容器(三)完成IoC容器,然后最近想在这个项目基础上把Spring的AOP也实现一下,然后就悲剧的发现了一句错误代码……
这个错误代码就在edu.jyu.core.ClassPathXmlApplicationContext
类的Object createBeanByConfig(Bean bean)
方法中,下面是这个方法
/** * 根据bean的配置信息创建bean对象 * * @param bean * @return */private Object createBeanByConfig(Bean bean) { // 根据bean信息创建对象 Class clazz = null; Object beanObj = null; try { clazz = Class.forName(bean.getClassName()); // 创建bean对象 beanObj = clazz.newInstance(); // 获取bean对象中的property配置 List<Property> properties = bean.getProperties(); // 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中 for (Property prop : properties) { Map<String, Object> params = new HashMap<>(); 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 new RuntimeException("创建" + bean.getClassName() + "对象失败"); } return beanObj;}
错误就在如果依赖对象还未被加载条件成立后,ref = createBeanByConfig(bean);
这句代码的问题是什么了,很明显我一不小心又把当前要创建的bean对象的配置信息传入createBeanByConfig
方法中了,所以就会无限递归下去,最后发生StackOverflowError
错误。
至于为什么我的测试代码能通过也是比较凑巧,我的测试bean是一个A类,一个B类,其中B类依赖A类对象,所以我们要把A类对象注入到B类中,但是就是这么巧,读取配置文件的时候先读到了A类,所以在要创建B类对象时,A类对象已经创建好了,ref == null
就为false,也就是说没执行到那句错误代码,所以就没发现……
要是我改为A类依赖B类,那就可以发现问题了,因为要创建A类对象时,B类对象还没创建。
A类
package edu.jyu.bean;public class A { private String name; private B b; public B getB() { return b; } public void setB(B b) { this.b = b; } public String getName() { return name; } public void setName(String name) { this.name = name; }}
B类
package edu.jyu.bean;public class B { private int age; public int getAge() { return age; } public void setAge(int age) { 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的解决
解决上面的那个Bug并不难,只需要把那句错误代码ref = createBeanByConfig(bean);
换成ref = createBeanByConfig(config.get(prop.getRef()));
完整方法
/** * 根据bean的配置信息创建bean对象 * * @param bean * @return */private Object createBeanByConfig(Bean bean) { // 根据bean信息创建对象 Class clazz = null; Object beanObj = null; try { clazz = Class.forName(bean.getClassName()); // 创建bean对象 beanObj = clazz.newInstance(); // 获取bean对象中的property配置 List<Property> properties = bean.getProperties(); // 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中 for (Property prop : properties) { Map<String, Object> params = new HashMap<>(); 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) { //下面这句的错误在于传入了当前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 new RuntimeException("创建" + bean.getClassName() + "对象失败"); } return beanObj;}
现在运行测试类TestApplicationContext
的测试方法就没问题了
- 自己实现Spring IoC容器(四)IoC容器的Bug
- 自己实现Spring IoC容器(三)完成IoC容器
- 自己实现的Spring IOC容器
- Spring IOC容器的实现
- Spring的IoC容器实现
- Spring IOC容器实现
- Spring实现IOC容器
- Spring IOC容器实现
- spring源码初步学习-自己实现的ioc容器结构
- spring的Ioc容器
- Spring的IoC容器
- spring的IOC容器
- Spring 的IOC 容器
- 自己实现Spring IoC容器(一)概述
- 自己实现Spring IoC容器(二)读取配置文件
- Spring学习(四)Spring容器的IOC应用
- andriod实现自己的ioc容器
- IoC容器的实现
- 线程池
- 性能调优攻略
- qml放大缩小,进度条
- Delaunay三角剖分
- 使用oracle自带工具sqlloader导入文本
- 自己实现Spring IoC容器(四)IoC容器的Bug
- uic: Error in line 1, column 38 : Encountered incorrectly encoded
- HTTP Header 响应实例
- 像素鸟简单的背景设置_01
- Tensorflow学习笔记
- java___for Arrays.sort插入一个元素
- 496. Next Greater Element I
- linux vi/vim编辑器常用命令与用法总结
- php实现socket