java实现 ioc_java:手动实现一个springIOC

面试官特别爱问SpringIOC底层实现,Spring源码晦涩难懂 怎么办呢? 跟着老师手动实现一个mini ioc容器吧,实现后再回头看Spring源码事半功倍哦~,就算直接和面试官讲也完全可以哦,类名完全按照源码设计,话不多说 开干~!

手动实现IOC容器的设计

需要实现的IOC功能:

可以通过xml配置bean信息可以通过容器getBean获取对象能够根据Bean的依赖属性实现依赖注入可以配置Bean的单例多例实现简易IOC设计的类

86c4d5f66271b7c24e308ddbf7ea8b8d.png

类之间关系模型

39189e59392ed180566a7f7f8c1a0fb1.png

前期准备

创建maven项目引入依赖

dom4jdom4j1.1commons-beanutilscommons-beanutils1.9.3

准备3个bean的实体类

/*** 学生类* 学生类依赖班级对象* 并提供 sayHello() 方法* @作者 itcast* @创建日期 2020/3/7 19:46**/publicclassStudent {private String name;private TClass tClass;publicvoidsayHello(){System.out.println("大家好,我是" +this.name+" 我的班级是==>"+tClass.getCname() + " 我的老师是"+tClass.getTeacher().getTname());}public String getName() {return name;}publicvoidsetName(String name) {this.name = name;}public TClass gettClass() {return tClass;}publicvoidsettClass(TClass tClass) {this.tClass = tClass;}}/*** 班级类* 班级类依赖教师对象* @作者 itcast* @创建日期 2020/3/7 19:45**/publicclassTClass {private String cname;// 班级名称private Teacher teacher; // 老师public String getCname() {return cname;}public void setCname(String cname) {this.cname = cname;}public com.itcast.ioc.bean.Teacher getTeacher() {return teacher;}public void setTeacher(com.itcast.ioc.bean.Teacher teacher) {this.teacher = teacher;}}/*** 教师类* @作者 itcast* @创建日期 2020/3/7 19:44**/public class Teacher {private String tname;// 老师名称public String getTname() {return tname;}public void setTname(String tname) {this.tname = tname;}}

xml配置对象

配置学生对象: 小明

依赖班级对象: 3年2班

依赖教师对象: 陈老师

mini-IOC容器-定义类

定义BeanFactory

/*** 容器的基础接口* 提供容器最基本的功能*/publicinterfaceBeanFactory {// 核心方法 获取对象Object getBean(String beanName);}

定义DefaultListableBeanFactory

package com.itcast.ioc.core;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/*** 基础容器的核心实现* 提供 beanDefinitionMap 存储bean的定义* 提供 singletonObjects 存储bean的对象实例* @作者 itcast* @创建日期 2020/7/8 15:37**/publicclassDefaultListableBeanFactoryimplementsBeanFactory{// 提供 beanDefinitionMap 存储bean的定义private Map beanDefinitionMap = new ConcurrentHashMap<>();// 提供 singletonObjects 存储bean的对象实例 (scope为singleton的)private Map singletonObjects = new ConcurrentHashMap<>();/*** 实现getBean方法* @param beanName* @return*/@Overridepublic Object getBean(String beanName) {return null;}/*** 将bean注册到容器中* @param beanDefinition*/public void registerBeanDefinition(BeanDefinition beanDefinition){beanDefinitionMap.put(beanDefinition.getBeanName(),beanDefinition);}}

定义BeanDefnition

/*** 用于描述Bean的定义* @作者 itcast* @创建日期 2020/7/8 15:41**/publicclassBeanDefinition {private String beanName; // bean标签的ID 作为bean的唯一标识private String className; // bean的所属classprivate String scope = "singleton"; // bean的scope作用域private List propertyList = new ArrayList<>();public String getBeanName() {return beanName;}public void setBeanName(String beanName) {this.beanName = beanName;}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public List getPropertyList() {return propertyList;}public void setPropertyList(List propertyList) {this.propertyList = propertyList;}

}

定义Property

/*** 用于封装一个property标签* 属性数据* @作者 itcast* @创建日期 2020/7/8 15:44**/publicclassProperty {private String name; // 属性名称private String value; // 属性的值private String ref; // 属性的引用public String getName() {return name;}public void setName(String name) {this.name = name;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getRef() {return ref;}public void setRef(String ref) {this.ref = ref;}}

定义XmlBeanFactory

/*** 继承核心实现类* 基于xml配置bean的实现类* @作者 itcast* @创建日期 2020/7/8 15:47**/publicclassXmlBeanFactoryextendsDefaultListableBeanFactory{/*** 将解析配置文件 注册bean的所有工作交给reader对象*/final XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(this);/*** 构造器需要传入xml配置文件* @param configPath*/public XmlBeanFactory(String configPath) {// 使用reader对象 解析配置 注册Beanthis.xmlBeanDefinitionReader.loadBeanDefinitions(configPath);}}

定义XmlBeanDefinitionReader

/*** 解析配置* 注册到容器中* @作者 itcast* @创建日期 2020/7/8 15:51**/publicclassXmlBeanDefinitionReader {// 核心beanfactory对象 用于将解析后的bean注册到beanfactory中final DefaultListableBeanFactory beanFactory;public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {this.beanFactory = beanFactory;}/*** 根据传递的配置文件* 解析配置* 注册bean* @param configPath*/void loadBeanDefinitions(String configPath){}}

mini-IOC容器--解析注册

实现步骤

1. 通过dom4j解析xml得到Document文档 2. 遍历文档所有Bean标签 3. 解析每一个Bean标签 封装一个BeanDefinition对象 4. 解析每一个Bean标签下的所有Property标签 封装一个Property对象 5. 将BeanDefinition和Property对象注册到容器

实现xml解析及bean注册

/*** 解析配置* 注册到容器中* @作者 itcast* @创建日期 2020/7/815:51**/public class XmlBeanDefinitionReader {// 核心beanfactory对象 用于将解析后的bean注册到beanfactory中final DefaultListableBeanFactory beanFactory;public XmlBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {this.beanFactory = beanFactory;}/*** 根据传递的配置文件* 解析配置* 注册bean* @param configPath*/void loadBeanDefinitions(String configPath){// 1. 通过dom4j解析xml得到Document文档Document document = doLoadDocument(configPath);// 2. 遍历文档所有Bean标签Element rootElement = document.getRootElement();List list = rootElement.selectNodes("//bean");for (Element element : list) {// 3. 解析每一个Bean标签 封装一个BeanDefinition对象BeanDefinition beanDefinition = parseBeanDefinition(element);// 5. 将BeanDefinition和Property对象注册到容器beanFactory.registerBeanDefinition(beanDefinition);}}/*** 3. 解析每一个Bean标签 封装一个BeanDefinition对象* 4. 解析每一个Bean标签下的所有Property标签 封装一个Property对象*/BeanDefinition parseBeanDefinition(Element element){BeanDefinition beanDefinition = new BeanDefinition();String beanName = element.attributeValue("id");String className = element.attributeValue("class");String scope = element.attributeValue("scope");beanDefinition.setBeanName(beanName);beanDefinition.setClassName(className);if(scope!=null&&!"".equals(scope)){beanDefinition.setScope(scope);}List propertyList = element.elements("property");for (Element propertyEle : propertyList) {Property property = new Property();property.setName(propertyEle.attributeValue("name"));property.setValue(propertyEle.attributeValue("value"));property.setRef(propertyEle.attributeValue("ref"));beanDefinition.getPropertyList().add(property);}return beanDefinition;}/*** 解析Document文档* @param configPath* @return*/Document doLoadDocument(String configPath){InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath);SAXReader saxReader = new SAXReader();try {return saxReader.read(inputStream);} catch (DocumentException e) {e.printStackTrace();System.out.println("解析xml出现异常==>"+e.getMessage());throw new RuntimeException(e.getMessage());}}}

准备测试类

/**

* 测试类

* @作者 itcast

* @创建日期 2020/7/8 16:19

**/

publicclassIocTest {

publicstaticvoidmain(String[] args){

// 创建IOC容器

BeanFactory beanFactory = new XmlBeanFactory("applicationContext.xml");

// 通过容器获取对象

Student student = (Student)beanFactory.getBean("student");

// 调用对象sayHello方法

student.sayHello();

}

}

断点查看注册情况

可以看到我们配置的xml内容 已经解析成了BeanDefinition对象,注册到了核心容器的map中

fdf12ce6388ea04ccdfb72f36c761048.png

mini-IOC容器-getBean

实现步骤

1. 先从单例的map集合中获取 是否有指定beanName的对象

有直接返回

没有下一步

2. 从注册集合中获取bean的定义对象

有下一步

没有抛异常NoSuchBeanDefinition

3. 判断bean的scope作用域

singleton单例

createBean对象

存入单例集合

返回对象

prototype多例

createBean对象

返回对象

4. createBean方法

获取BeanDefinition中的className

通过反射API得到Class对象

通过反射API得到bean实例

获取BeanDefinition中依赖的属性列表

实现属性的依赖注入

实现getBean及createBean方法

/*** 基础容器的核心实现* 提供 beanDefinitionMap 存储bean的定义* 提供 singletonObjects 存储bean的对象实例* @作者 itcast* @创建日期 2020/7/815:37**/public class DefaultListableBeanFactory implements BeanFactory {// 提供 beanDefinitionMap 存储bean的定义private Map beanDefinitionMap = new ConcurrentHashMap<>();// 提供 singletonObjects 存储bean的对象实例 (scope为singleton的)private Map singletonObjects = new ConcurrentHashMap<>();/*** 实现getBean方法* @param beanName* @return*/@Overridepublic Object getBean(String beanName) {// 1. 先从单例的map集合中获取 是否有指定beanName的对象Object singletonObj = singletonObjects.get(beanName);// 有直接返回if(singletonObj!=null){return singletonObj;}// 没有下一步// 2. 从注册集合中获取bean的定义对象BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);// 有下一步// 没有抛异常NoSuchBeanDefinitionif(beanDefinition==null){throw new RuntimeException("NoSuchBeanDefinition : 你找的 "+beanName+" 对象 不存在");}// 3. 判断bean的scope作用域String scope = beanDefinition.getScope();// singleton单例if("singleton".equals(scope)){// createBean对象Object obj = createBean(beanDefinition);// 存入单例集合singletonObjects.put(beanName,obj);// 返回对象return obj;}else {// prototype多例// createBean对象return createBean(beanDefinition);// 返回对象}}/*** //4. createBean方法* //获取BeanDefinition中的className* //通过反射API得到Class对象* //通过反射API得到bean实例* //获取BeanDefinition中依赖的属性列表* //实现属性的依赖注入* 创建对象* @param beanDefinition* @return*/Object createBean(BeanDefinition beanDefinition){String className = beanDefinition.getClassName();Class> aClass;try {aClass = Class.forName(className);} catch (ClassNotFoundException e) {e.printStackTrace();throw new RuntimeException("类未找到"+e.getMessage());}// 创建对象:Object obj;try {obj = aClass.newInstance();} catch (InstantiationException e) {e.printStackTrace();throw new RuntimeException("创建对象失败"+e.getMessage());} catch (IllegalAccessException e) {e.printStackTrace();throw new RuntimeException("非法访问"+e.getMessage());}// 依赖注入List propertyList = beanDefinition.getPropertyList();for (Property property : propertyList) {String name = property.getName();String value = property.getValue();String ref = property.getRef();// 属性名不为空 进行注入if(name!=null&&!"".equals(name)){// 如果配置的是value值 直接注入if(value!=null&&!"".equals(value)){Map params = new HashMap<>();params.put(name,value);try {BeanUtils.populate(obj,params);} catch (IllegalAccessException e) {e.printStackTrace();throw new RuntimeException("非法访问"+e.getMessage());} catch (InvocationTargetException e) {e.printStackTrace();throw new RuntimeException("调用目标对象失败"+e.getMessage());}}// 如果配置的是ref需要获取其它对象注入if(ref!=null&&!"".equals(ref)){try {BeanUtils.setProperty(obj,name,getBean(ref));} catch (IllegalAccessException e) {e.printStackTrace();throw new RuntimeException("非法访问"+e.getMessage());} catch (InvocationTargetException e) {e.printStackTrace();throw new RuntimeException("调用目标对象失败"+e.getMessage());}}}}return obj;}/*** 将bean注册到容器中* @param beanDefinition*/public void registerBeanDefinition(BeanDefinition beanDefinition){beanDefinitionMap.put(beanDefinition.getBeanName(),beanDefinition);}}

mini-IOC容器-单例对象初始化

DefaultListableBeanFactory增加初始化方法

public void preInstaniceSingletons(){beanDefinitionMap.forEach((beanName,beanDefinition)->{ String scope = beanDefinition.getScope();// 判断单例 非抽象 不懒加载if("singleton".equals(scope)){this.getBean(beanName); } }); }

XmlBeanFactory增加单例对象初始化

publicXmlBeanFactory(String configPath){// 使用reader对象 解析配置 注册Beanthis.xmlBeanDefinitionReader.loadBeanDefinitions(configPath); // 初始化单例对象 this.preInstaniceSingletons();}

mini-IOC容器-测试和小结

测试对象能否获取

02d1e79cefcf2512c628ce98264d4e0f.png

查看bean的注册及单例集合信息

可以通过变更scope的值查看对应的变化

4cfc8169952f30d0edbd941167edd68c.png

IOC容器源码及其它面试细节

扩展: 容器如何创建对象

IOC容器在准备创建对象时, 会判断是否有配置 factory-method方法

如果有配置 会调用factory-method所指向的方法构建对象.

如果没配置,会检查是否有配置构造参数

无构造参数: 调用默认构造器创建对象

有构造参数: 根据参数情况匹配对应的构造器

扩展: bean的生命周期

spring 容器中的bean的完整生命周期一共分为十一步完成。

1.bean对象的实例化2.封装属性,也就是设置properties中的属性值3.如果bean实现了BeanNameAware,则执行setBeanName方法,也就是bean中的id值4.如果实现BeanFactoryAware或者ApplicationContextAware ,需要设置setBeanFactory或者上下文对象setApplicationContext5.如果存在类实现BeanPostProcessor后处理bean,执行postProcessBeforeInitialization,可以在初始化之前执行一些方法6.如果bean实现了InitializingBean,则执行afterPropertiesSet,执行属性设置之后的操作7.调用执行指定的初始化方法8.如果存在类实现BeanPostProcessor则执行postProcessAfterInitialization,执行初始化之后的操作9.执行自身的业务方法10.如果bean实现了DisposableBean,则执行spring的的销毁方法11.调用执行自定义的销毁方法。扩展: bean的循环依赖问题

A 依赖 B B 依赖 A 产生闭环,称为循环依赖

Spring 默认允许单例对象的属性注入 所产生的循环依赖单例对象的循环依赖 Spring通过3级缓存来解决 比如一个类A中有一个属性是B类,B类中有一个属性是A类,这时看Spring是怎么解决他们的相互依赖的。Spring注入一个类的大体步骤分为两部分,一是先完成对类的构造工作,二是会对类的属性进行设置和填充。首先Spring构造A类,通过AbstractAutowireCapableBeanFactory的doCreateBean方法中调用addSingletonFactory方法将A类曝光到singletonFactories中。

这时完成A的构造后,需要填充B属性,继续第二步,发现B还没有构造,于是开始B流程的构造过程,构造的时候发现需要填充A,从第三层缓存singletonFactories中找到A(此时的A还没有完全构造完成,但是可以拿到A的一个引用),B拿到A的引用后,完成B自己的填充属性工作,完成初始化工作,把自己放到第一层缓存singletonObjects中。这时回到A的这边,在拿到B对象后,完成自己的填充属性工作。

c59b909b6312beb6a740f1f628f5de9a.png

如果是构造器依赖属性 会报循环依赖异常如果对象都是多例对象 会报循环依赖异常如果设置allowCircularReferences为false 会报循环依赖异常protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); }if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); }}

扩展: bean的覆盖问题

默认情况: 同一个配置文件中出现id相同的bean会报错,不同的配置文件出现id相同的bean 后加载的bean会将先加载的bean覆盖掉,称为bean的覆盖,bean的覆盖不会报错,但可能影响我们的项目 , 可以通过属性设置 不允许bean的覆盖allowBeanDefinitionOverriding设置为false

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值