spring 中的ioc

初学spring的时候,虽然天天经常听到IoC容器,但是奈何刚接触,需要学习的东西太多,先大体过一遍有个印象,会用就成,但是 经常提及的 IoC 容器到底是什么,在哪儿,或许有不少人会懵逼。今天就一起来揭开spring IoC容易的神秘面纱,因为水平有限,如有错误,希望各路大佬拔斧相正(斧正),不胜感激。


1.控制反转和依赖注入

1).IoC(Inverse of Control):控制反转,“某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方决定,即由spring容器借由Bean配置来进行控制”[1]。

2).DI(Dependency Injection):依赖注入,其实和控制反转说的是一件事,“让调用类对某一接口实现类的依赖关系由第三方(容器或者协作类)注入,以移除调用类对某一接口实现类的依赖”[2]

因为上面两个概念说的是同一件事,所以你只要理解其中一个就好了,可以参考下面的例子:

过年了,要吃肉,如果要吃猪肉,就要new 一头猪,然后屠了它,要吃羊肉,你就自己new 只羊,屠之,person依赖了animal接口,这也是依赖倒置原则的一小小应用(程序要依赖于接口,不依赖于实现,上层模块不依赖不依赖底层实现类,要依赖这些实现类的抽象),调用者完全掌控了什么时候去new一个对象,new哪种类型的对象,现在有个问题,你觉得屠这个动作有点血腥暴力了,但是有不能不吃肉,所以你不干这个活了,你只想肉来张口,所以你就直接去了菜市场掏钱买肉了,猪和羊啥时候变成肉的你控制不了了,和你没关系了,于是乎就整个设计就变成了下面这样

如此一来,你=你就不再依赖animal接口了,而是依赖第三者,你原本可以控制任何的animal的对象,但是你将这个权利拱手让人,想做个体面人了,要什么肉直接上菜市场买,如此一来不就移除了调用类对接口的依赖了吗?不就转交给第三方控制了吗?菜市场不就是个ioc容器了吗?对应到spring中,IoC容器就是一个类,但是这个类能创建对象,还能管理对象。


2.spring中的 IoC容器

BeanFactory是spring的核心接口,可以通过各种方式满足的你的需要,提供了很多高级的服务和高级的IoC配置,至于多高级,以后再说。“我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文,为了行文方便,也将ApplicationContext为Spring 容器”[3],“BeanFactory是Spring框架的基础设施,面向spring本身;ApplicationContext面向框架开发者,几乎所有的场合都可以直接使用ApplicationContexte而非底层的BeanFactory”[3],抄书抄到此处,你可能还不太明白spring Ioc容器到是啥,那就继续往下看demo,下面的例子也是参考书本中的例子略有改动

public class BeanFactoryTest {
  //-1 使用spring的ioc容器创建和管理bean对象
    public static void getBean() throws IOException {
     //路径匹配资源模式解析器 ResourcePatternResolver resolver
= new PathMatchingResourcePatternResolver();
     //根据配置文件得到配置资源对象 Resource res
= resolver.getResource("classpath:com/smart/beanFactory/beans.xml");
     //输出配置文件的地址  System.out.println(
"resource url= " + res.getURL())
     //这个类也是BeanFactory的一个一个实现类下面的一个子类,实际就是这个类做了控制着xml中定义的对象的的创建和管理他就是实实在在的ioc容器 DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
     //解析xml中bean节点 XmlBeanDefinitionReader reader
= new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);
     //初始换容器 System.out.println(
"init BeanFactory");
     //获取在xml中国定义的bean实例 在第一次调用的时候IOC 容器才会对bean 进行初始化 Car car
= factory.getBean("car1",Car.class); System.out.println("car bean is ready for use"); System.out.println(car.toString()); }    //-2 使用spring容器创建和管理对象 public static void getBeanByApplictionContext(){
     //启动spring 容器并对其进行初始化 也要对bean进行实例化 ApplicationContext context
= new ClassPathXmlApplicationContext("com/smart/beanFactory/beans.xml"); System.out.println("get bean by applicationContext");
      //获取一个bean Car car
= context.getBean(Car.class); System.out.println(car.toString()); } public static void main(String[] args) throws IOException { getBean(); getBeanByApplictionContext(); } }
tips:spring中的Bean比较宽泛,任何一个java类只要能够被spring容器进行实例化和管理,就是Bean,在本文中,说获取bean意思是获取一个bean的实例,也就是获取某个类的实例对象
下面是上面的程序的输出,如果要使用这个demo你需要自己写一个简单car类,属性,getter setter toString方法,如果使用的IDEA,可以使用alt+insert弹出快速生成上述方法的
的菜单


resource url= file:/D:/masterSpring/code2/chapter4/target/classes/com/smart/beanFactory/beans.xml
六月 06, 2020 10:42:46 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/smart/beanFactory/beans.xml]
init BeanFactory
init car
car bean is ready for use
Car{brand='红旗CA72', color='黑色', maxSpeed=200}
六月 06, 2020 10:42:46 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4cb2c100: startup date [Sat Jun 06 10:42:46 CST 2020]; root of context hierarchy
六月 06, 2020 10:42:46 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/smart/beanFactory/beans.xml]
init car
get bean by applicationContext
Car{brand='红旗CA72', color='黑色', maxSpeed=200}

 仔细看看上面的代码,你就会焕然大悟,既然ioc容器是创建和管理bean的,那么谁提供了bean 谁就是ioc容器,使用第一种方法getBean的时候,可以点击方法进入看看,调用链条是这样的(其中调用的都是方法,为方便书写,采用的倍调用方法所在的类+ 方法名,省略参数):getBean--> AbstractBeanFactory.getBean-->AbstrctBeanFactory.doGetBean-->DefaultSingletonBeanRegistry.getSingleton-->DefaultSingletonBeanRegistry.getSingleton(这是个重载的方法),就在此处,如果不是第一次用到这个bean实例,那么DefaultSingletonBeanRegistry就会将bean实例缓存在一个叫做singletoneObjects的HashMap中,key 是bean的name,value 就是bean实例,如果是第一使用到这个bean那么spring会先创建bean实例,然后将这个bean实例存放到singletoneObjects中。源码是这样的 private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256)

综上,spring IoC容器就是一个类,这个类维护了一个线程安全的Map,在xml中定义的bean在经过spring的一顿操作之后,这些bean实例都会被放到singletoneObjects中,从容器中获取实例就是调用map的get方法。然而从调用者的角度来看,我们获取bena的时候是通过DefaultListableBeanFactory的对象来获取的,但是这个对象间接继承了很多类,实现了很多借口,如下图

为什么书中说一般称BeanFactoy是IoC容器,我的理解如下:我们是通过调用某一个类的getBean方法来获取实例对象的,getBean方法就是BeanFactroy接口中定义的方法,这个接口提供很多种获取实例对象的方法,除此之外,还有其一些必要的方法,因为BeanFactroy有很多子子孙孙,都要实现BeanFactory定义的规则,Beanfactory代表一般的,普遍IoC容器的功能,我们不应该说它的某个实现类是IoC容器,底层实现类是经常变换的,高度抽象的接口是比较稳定的,从普适性的角度触发,称BeanFactory 是IoC容器比较合适的


 

参考文献

[1]《精通Spring 4.x 企业应用开发实战》 陈华雄 林开雄 文建国 编著 74页

[2]《精通Spring 4.x 企业应用开发实战》 陈华雄 林开雄 文建国 编著 74页

[3] 《精通Spring 4.x 企业应用开发实战》 陈华雄 林开雄 文建国 编著 91页

posted @ 2020-06-06 12:26  指路为码  阅读(228)  评论(0编辑  收藏  举报