IOC原理:
- IOC是什么?
- IOC容器是什么?(IOC容器就是依赖注入技术的核心bean的集合。)
- 为什么有IOC?
- 要实现IOC需要哪些必要条件?
一般大家提到spring都知道他的2大功能,一个是IOC叫控制反转,更准确的应该叫依赖注入,另一个便是AOP,这里不讨论AOP。
那么如何实现IOC,早期注解还不是很流行的时候,大家肯定配置过spring的配置文件,applicationContext.xml(spring设计实现的源头),
例如:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" default-autowire="byName" default-lazy-init="true"> <bean class="bean的完全限定名" name/id="bean在容器内的唯一名称" scope="bean的生命周期" lazy-init="是否为延迟加载" init-method="bean的setter被调用之后调用该方法进行初始化" destroy-method="容器在销毁该Bean后的调用的方法" abstract="是否为抽象Bean,spring对于抽象bean不产生实例,主要用于继承" parent="父Bean的名称,会继承父Bean的属性,与Java的Class无任何关系" factory-method="工厂方法的名字" factory-bean="工厂Bean的名字" depends-on ="依赖Bean的名字,保证初始化顺序。” > <!-- Constructor-arg给属性赋值写法一 --> <constructor-arg type="int" value="10"/> <!-- Constructor-arg给属性赋值写法二 --> <constructor-arg name="age" value="10"/> <!-- Constructor-arg给属性赋值写法三 --> <constructor-arg index="0" value="10"/> <!-- Properties给属性赋值写法一 --> <property name="bean1"> <ref bean="另外一个bean的id"/> </property> <!-- Properties给属性赋值写法二 --> <property name="bean1" ref="另外一个bean的id"/> <!-- Properties给属性赋值写法三 --> <property name="age" value="10"/> </bean>
或者通过注解:
@Component 进行配置bean; @Autowired进行bean注入。
通过配置我们知道<beans>标签下还有多个<bean>,同时bean下面还有<constructor-arg>和<property>,这2个属性分别对应构造器注入和setter注入(这2种是spring推荐的方式)
另外还有通过bean工厂方式注入的,并不推荐。
- 这些bean装入容器的时候,<bean>标签下的各种属性(例如:class、id、scope等等属性)该怎么办?
- 另外分析上面的红色加粗字体,一个体现的是多个bean,一个体现的是bean的注入存在引用关系。既然,IOC容器是装入bean的容器,如果是你,你该怎么将bean装入这个容器呢?
- 既然是一个容器(能称作容器的必要条件是什么?),里面的bean我们是不是随时可以取用?容器中的bean是不是该有有效时间(对象的生命周期)?
先看下面的几个例子:
public class ClassB { private ClassA classA; public ClassB(ClassA classA) { this.classA = classA; } public String getStr() { return classA.getStr(); } // public static void main(String[] args) { // ClassA classA = new ClassA(); // ClassB classB = new ClassB(classA); // classB.getStr(); // } }
类B通过构造方法能够获取到类A,从依赖上来讲,类B的创建需要依赖类A,这个是从代码上体现,尤其是main方法中,类B进行
实例的时候必须先获取一个类A的实例(不要纠结无参构造,实际生产中存在这样的关系,B类的功能必须依赖A类),这里体现的
就是类B依赖类A,这就是依赖的体现,现在将main方法中的显示构造
ClassA classA = new ClassA(); ClassB classB = new ClassB(classA);//配置到spring配置文件中,我们就可以不用自己去实例化这个类B就可以直接使用
替换通过spring配置文件进行配置,这就是注入
现在,我们从实现配置文件出发开始模拟实现IOC功能(多个bean,并且bean之间有依赖注入关系),
- 定义一个配置文件(applicationContext.xml),文件名要有一定的限制(为后面准确读取特定文件准备)ResourceLoader
- 获取到这个文件,并且能够读取到里面的内容 XmlBeanDefinitionReader
- 文件中的内容要想正确读取,需要一定的格式支持,定义一个数据格式BeanDefinition,格式定义了能够读取的属性(例如:class、id、scope等等属性)
- 读取到bean以及bean中的属性,并且正确创建这个bean,我们通过AutowireCapableBeanFactory进行bean的装配
- 正确读取到单个的bean和bean之间的引用关系,我们需要 HierarchicalBeanFactory 进行关系的维系。
- 将多个bean进行集中管理通过 ListableBeanFactory进行集合管理
实现的流程大致如图:
以上就是演变过程,其中定义的接口都是spring源码中的结构,spring beans包中包含以上所有内容,但是整个的协作过程
体现在spring context包中,spring core包中包含了实现以上功能的底层实现。
其中:
bean的定义:
bean的解析:
bean的创建:
- ListableBeanFactory(可列表)
- HierarchicalBeanFactory(层级关系)
- AutowireCapableBeanFactory(自动装配)
这3个接口都是继承自 BeanFactory(很重要)这个顶级接口,最终落地实现类 DefaultListableBeanFactory
类层级关系如图:
协作过程在ApplicationContext接口中,
最终实现类为 AbstractApplicationContext(核心方法 refresh(),该类为抽象类,部分可定制化内容由实际实现子类完成)