Spring中的IOC深度解读
作者:猎户星座。
spring容器
spring容器的概念,容器这个名字起的相当好,容器可以放很多东西,我们的程序启动的时候会创建spring容器,会给spring容器一个清单,清单中列出了需要创建的对象以及对象依赖关系,spring容器会创建和组装好清单中的对象,然后将这些对象存放在spring容器中,当程序中需要使用的时候,可以到容器中查找获取,然后直接使用。
IOC:控制反转
使用者之前使用B对象的时候都需要自己去创建和组装,而现在这些创建和组装都交给spring容器去给完成了,使用者只需要去spring容器中查找需要使用的对象就可以了;这个过程中B对象的创建和组装过程被反转了,之前是使用者自己主动去控制的,现在交给spring容器去创建和组装了,对象的构建过程被反转了,所以叫做控制反转;IOC是是面相对象编程中的一种设计原则,主要是为了降低系统代码的耦合度,让系统利于维护和扩展。
DI:依赖注入
依赖注入是spring容器中创建对象时给其设置依赖对象的方式,比如给spring一个清单,清单中列出了需要创建B对象以及其他的一些对象(可能包含了B类型中需要依赖对象),此时spring在创建B对象的时候,会看B对象需要依赖于哪些对象,然后去查找一下清单中有没有包含这些被依赖的对象,如果有就去将其创建好,然后将其传递给B对象;可能B需要依赖于很多对象,B创建之前完全不需要知道其他对象是否存在或者其他对象在哪里以及被他们是如何创建,而spring容器会将B依赖对象主动创建好并将其注入到B中去,比如spring容器创建B的时候,发现B需要依赖于A,那么spring容器在清单中找到A的定义并将其创建好之后,注入到B对象中。
spring中依赖注入主要分为手动注入和自动注入:
手动注入需要由程序员自己配置、描述好依赖关系,来实现自动注入(但是实际开发中手动装配的场景比较少,比如在缺少源码的情况下可能会使用这种手动装配情况)。
自动注入采用约定大于配置的方式来实现的,程序和spring容器之间约定好,遵守某一种都认同的规则,来实现自动注入。
DI:依赖注入的实现方式
分别是基于构造方法的依赖注入和基于setter(setXxxx(…))的依赖注入。
不管是手动装配还是自动装配都是基于这两种方式或者变体方式来的;但是这里一定要回答到主要和变体两个名词,因为有的注入方式就不是这两种,而是这两种其中一种的变体方式;比如在一个类的属性上面加@Autowired,这种方式注入属性的方式就是利用了java的反射知识,field.set(value,targetObject);关于这个我在后面的文章中对spring源码解析的时候会说明@Autowired的原理;所以@Autowired这种注入的方式是setter注入方式的一种变体
DI:依赖注入的自动装配模型
依赖注入是一个过程,主要通过setter或构造方法以及一些变体的方式完成把对象依赖、或者填充上的这个过程叫做依赖注入,不管手动装配还是自动装配都有这个过程;
手动装配通过ref标签来指定依赖关系,而自动装配没有显示的指定依赖关系,所以需要通过一些规则,来从容器中查找到符合条件的bean进行自动装配的工作,而自动装配模型就是完成自动装配依赖的手段体现。
每一种模型都使用了不同的技术去查找和填充bean;而从spring官网上面可以看到spring只提出了4中自动装配模型(严格意义上是三种、因为第一种是no,表示不使用自动装配、使用),这四个模型分别用一个整形来表示,存在spring的beanDefinition当中,任何一个类默认是no这个装配模型
我们可以在AutowireCapableBeanFactory类中看下这几种自动装配模型对应的整形分别是多少
public interface AutowireCapableBeanFactory extends BeanFactory { int AUTOWIRE_NO = 0; int AUTOWIRE_BY_NAME = 1; int AUTOWIRE_BY_TYPE = 2; int AUTOWIRE_CONSTRUCTOR = 3; @Deprecated int AUTOWIRE_AUTODETECT = 4; }
@Autowired就是根据byType来进行自动装配
我们先抛出@Autowired的查找逻辑的结论:
首先spring根据类型去容器中找,找到了直接注入。如果根据类型找到了多个,那么spring不会立马异常,而是根据名字再去找,如果根据名字找到一个合理的则注入这个合理的。
如果没有找到,再根据名字去找,找到了则注入,没有找到则报异常。
我们再来用代码来验证下
定义一个空接口I,XY类分别实现I接口,Z类中注入I
public interface I { }
package com.yj.service.impl; import org.springframework.stereotype.Component; import com.yj.service.I; @Component("ix") public class X implements I{ }
package com.yj.service.impl; import org.springframework.stereotype.Component; import com.yj.service.I; @Component("iy") public class Y implements I{ }
@Component public class Z { @Autowired private I ix; public I getIx() { return ix; } public void setIx(I ix) { this.ix = ix; } }
扫描配置类
package com.yj.conf; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.yj") public class AppConfig { }
MyBeanFactoryProcessor
package com.yj.conf; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.stereotype.Component; @Component public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)beanFactory.getBeanDefinition("z"); //beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); System.out.println("beanDefinition.getAutowireMode():"+beanDefinition.getAutowireMode()); } }
首先,我们可以观察到被@Autowired注解的z类的getAutowireMode值为0,不是2。而且我们如果手动将z类的AutowireMode设置为ByType,只是根据类型来查找的话,会查出XY两个bean,程序会报错的。
但是采用@Autowired默认的注入方式,是不会报错的,也就是说@Autowired的查找逻辑并不能简单的认为等价于ByType,@Autowired首先根据类型然后再根据名字去查找,完成bean注入的过程。
到此这篇关于Spring中的IOC深度解读的文章就介绍到这了,更多相关IOC深度解读内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!