依赖注入

依赖注入是Spring框架最核心的能力,Spring框架提供的AOP,WebMVC等其它功能都是以依赖注入容器作为基础构建的,Spring依赖注入容器类似于一个用于组装对象的框架内核,任何应用或者基于Spring的框架都能从中受益

核心概念

理解一个项目,我习惯从它的领域模型开始,领域模型即项目中的核心概念,Spring的依赖注入容器中有哪些核心概念?

· bean是spring中的核心概念之一,是spring操作的实体,对于bean,spring中有BeanDefinition接口与之对应,BeanDefinition接口表示bean的定义,它有多个实现类,比如GenericBeanDefinition、AnnotatedGenericBeanDefinition等,每个不同的实现类针对特定的配置方式

· BeanFactory是spring的服务域,用于管理bean,BeanFactory也有多个实现类和抽象类,用于实现依赖注入容器的不同的使用方式

· ApplicationContext是用于对BeanFactory做增强,提供了BeanFactory的生命周期管理,状态变更广播,注解配置等功能

核心类之间的关系

在介绍Spring的框架设计原则之前,我们先通过一个类图来看一下Spring依赖注入容器的核心类之间的关系:

这里是Spring ApplicationContext的继承结构中的几个有代表性的类,由于相差类较多,这里省略了继承关系中间的抽象类,有兴趣的读者可以自己翻阅Spring的源代码,看看其实现细节。ApplicationContext的继承体系使用了模板方法模式,Spring中不同的抽象类为不同类型的ApplicationContext提供了基本的支持,比如AbstractXmlApplicationContext是通过xml配置使用的ApplicationContext的抽象实现。抽象类中实现了ApplicationContext应该具备的共同特性和逻辑相同的方法,具体类中再实现具体的不同点的逻辑,当然,子类中可以覆盖那些抽象类中的方法,以便对ApplicationContext做扩展。

我们先简单的看一下ClassPathXmlApplicationContext类,很多初学者最初就是从ClassPathXmlApplicationContext类开始接触Spring的ApplicationContext对象的,这类是通过加载classpath下的xml配置来初始化Spring的依赖注入容器,并完成bean的装载和依赖对象的注入过程,此类的使用步骤如下:

1. 添加Spring的配置文件,比如/META-INF/service.xml

2. 在service.xml中配置需要的bean

3. 通过service.xml创建ClassPathApplicationContext对象:

前面讲述了常用的ClassPathXmlApplicationContext,现在这里再看一下一个不常用,却更能说明ApplicationContext的结构的类GenericApplicationContext,从上面的继承关系图中可以看出此类实现了BeanDefinitionRegistry接口,BeanDefinitionRegistry接口有什么作用呢?我们在使用Spring的依赖注入容器前,都需要通过XML或者注解的方式向Spring中配置bean,Spring在解析xml或者注解的同时,会将每一个配置解析成一个BeanDefinition对象注册到spring容器中,BeanDefinitionRegistry接口就是用来做bean的注册的,可以先看一下BeanDefinitionRegistry接口的定义:

可以看到,BeanDefinitionRegistry接口提供了Bean的注册相关的方法,可以用此接口向Spring中注册BeanDefinition对象,移除BeanDefinition对象,获取已注册的BeanDefinition对象等,GenericApplicationContext类没有配置加载功能,无法通过xml配置完成初始化,如果想要使用GenericApplicationContext类,则需要以编码的方式向Spring中注册Bean,例如:

我们再回到前文讲到的ClassPathXmlApplicationContext,ClassPathXmlApplicationContext是GenericApplicationContext的子类,只不过ClassPathXmlApplicationContext通过解析xml的方式来完成对BeanDefinition的注册前面讲到的示例中,在xml中配的配置就类似于以繁琐的编码的方式使用GenericApplicationContext时通过代码创建的BeanDefinition对象,如果我们打开ClassPathXmlApplicationContext及其父类AbstractXmlApplicationContext类,会发现它们有如下结构 (为了简单起见,这里同样省略了中间类,XmlBeanDefinitionReader实际上在AbstractXmlApplicationContext中):

使用ClassPathXmlApplicationContext后,注册Bean的过程由GenericApplicationContext的手动编码变成了的通过xml配置,注册Bean的过程由XmlBeanDefinitionReader负责,配置只是编码方式的一种简化,它并不是框架的核心XmlBeanDefinitionReader的执行过程后文会有详细讲解

框架的初始化

上一节讲过的GenericApplicationContext的使用中,调用了一次refresh方法完成了初始化,同样,ClassPathXmlApplicationContext也需要通过refresh方法完成初始化,只不过默认是自动调用了refresh方法,如果打开ClassPathXmlApplicationContext的源码,我们可以看到多个构造器,其中有一个构造器可以指定是否自动刷新依赖注入容器完成依赖注入窗口的初始化,如果想要手动初始化,我们可以换使用如下方式创建ClassPathXmlApplicationContext对象:

通过IDE的帮助,我们很容易能找到refresh方法的声明,它声明在ApplicationContext的一个子接口ConfigurabeApplicationContext中,我们暂且先不解析此接口的作用,我们可以先看一看refresh方法的实现。

打开AbstractApplicationContext类的refresh方法,这是ApplicationContext的初始化入口,这里先介绍refresh方法的结构:

· 首先被调用的是prepareRefresh方法,此方法是AbstractApplicationContext的模板方法模式的扩展方法,子类可覆盖此方法,用于在初始化前做前置准备

· 其次再是获取ConfigurableListableBeanFactory对象,ApplicationContext并不是从头开始做依赖注入容器,它是建立在BeanFactory之上,BeanFactory是Spring实现依赖注入的最核心的接口

· 第三个步骤是调用prepareBeanFactory,此方法用于向Spring中注册默认的单例bean以及默认的BeanPostProcessor等

· 第四步是调用postProcessBeanFactory,此方法用于执行准备好了BeanFactory的环境后的后置逻辑,子类可覆盖它实现特定逻辑

· 第五步是invokeBeanFactoryPostProcessors,从方法名上可以看出,这一步是在调用容器提供的BeanFactoryPostProcessor接口,此接口是Spring提供的扩展接口之一,我们后文再详细讨论

· 第六步调用registerBeanPostProcessors,此方法用于注册BeanPostProcessor,用于对bean的创建做拦截处理,这一步会创建PostProcessor的对象,BeanPostProcessor接口也是Spring提供的一个扩展接口,后面再详细讨论

· 第七步是initMessageSource,用于初始化MesasgeSource

· 第八步是initApplicationEventMulticaster,用于初始化事件广播器,Spring在初始化的前后可以广播ApplicationEvent,用户可自定义ApplicationListener来响应这些事件

· 第九步调用onRefresh,此方法也是用于扩展,子类中可用它实现其它特殊bean的装配和定制

· 第十步调用registerListeners,这一步是识别出Spring中配置的ApplicationListener对象

· 第十一步调用finishBeanFactoryInitialization,创建所有非lazy的单例对象

· 第十二步finishRefresh,完成初始化,此方法中会触发Lifecycle接口,以及广播事件,触发ApplicationListener

Spring初始化的整个过程非常清晰,整个流程见如下refresh方法的代码:

从整个初始化的大方法中,不难发现,ApplicationContext提供了以下BeanFactory没有的能力:

· BeanFactoryPostProcessor,对BeanFactory做拦截

· MessageSource,可用于实现消息的转换,国际化等功能

· 事件广播机制,可对依赖注入民容器的状态做监听

· 更加可扩展,可定制化

看完了初始化的大方法后,我们再来看看obtainFreshBeanFactory方法,此方法用于获取BeanFactory对象,其实现如下:

这个方法实现非常简洁,其中重要的一步便是refreshBeanFactory方法的调用,此方法是抽象方法,子类可实现以各种不同的方式创建BeanFactory,比如AbstractXmlApplicationContext中通过读取xml配置来创建BeanFactory,而GenericApplicationContext实现类中中则只是创建BeanFactory,没有注册BeanDefinition对象,需要手动编码注册。

RefreshBeanFactory方法的实现之一AbstractRefreshableApplicationContext中的实现是对xml的加载相关的特定逻辑,如下代码:

loadBeanDefinitions方法在AbstractRefreshableApplicationContext中是一个抽象方法,用于从xml中加载Bean,AbstractRefreshableApplicationContext中不关注如何加载,其子类的实现类中处理如何加载bean,AbstractXmlApplicationContext中实现了loadBeanDefinitions方法,其实现中通过XmlBeanDefinitionReader加载,而ClassPathXmlApplicationContext类中则实现从类路径下加载xml配置同时FileSystemXmlApplicationContext类则实现了从文件系统中的文件路径下加载xml配置。AbstractXmlApplicationContext中的loadBeanDefinitions方法中使用了XmlBeanDefinitionReader对象,使用方式如下 :

从上图中可以看到,XmlBeanDefinitionReader中使用到了ResourceLoader接口,此接口定义如下:

ResourceLoader接口用于加载资源,Spring的AbstractXmlApplicationContext实现了此接口,用于加载配置。AbstractXmlApplicationContext中的getResource方法子类也可覆盖,比如其中一个子类FileSystemXmlApplicationContext则覆盖了此方法,用于加载文件系统中的xml文件。

Spring模板方法模式的扩展

说了这么多Spring中使用的模板方法模式,这里举个例子,通过freemarker处理spring的xml配置,即在Spring的xml配置中可使用velocity语法,这里通过扩展Spring中的常用类ClassPathXmlApplicationContext类,并覆盖其getResource方法,在覆盖的方法中对Spring加载的xml配置做freemarker的处理,先看看代码如何实现:

实现代码后,再来看看如何使用,我们先准备好一个属性文件,用于创建velocity的Context对象并供velocity做渲染,我这里的路径为META-INF/config/app.properties 我这里只是一个示例,创建一个简单的属性文件即可:

这里只有一个key为default_username的属性,下面再来写一个使用了velocity语法的xml配置文件,作为示例,只写一个简单的xml配置文件,其中只有一个bean并且只使用velocity的if语法,其中使用了前面创建的属性文件中的default_username属性,判断如果default_username的值为test,则创建bean,否则什么都不做。因为前面的属性文件中的default_username的值确实为test,所以if判断会满足,我们可以从ApplicationContext中获取到配置的bean。我这里的xml路径为META-INF/freemarkerApp.xml.ftl,其内容如下 :

配置好了bean之后,最后再来创建ApplicationContext对象,过程如下图所示:

整个示例到此结束

spring依赖注入_Spring源码阅读:Spring依赖注入容器相关推荐

  1. java spring ioc 实例_Spring 源码阅读(IOC容器)-bean的实例化以及注入

    3.Bean的实例化以及注入过程分析 Bean的实例以及注入是在getBean时触发的,由于外部容器是与外部调用交互的桥梁,我们首先从外部容器入手,AbstractApplicationContext ...

  2. spring源码阅读(3)-- 容器启动之BeanFactoryPostProcessor

    接着上文<spring源码阅读(2)-- 容器启动之加载BeanDefinition>,当spring加载完所有BeanDefinition时,并不会马上去创建bean,而是先配置bean ...

  3. Spring Boot 2.0系列文章(四):Spring Boot 2.0 源码阅读环境搭建

    前提 前几天面试的时候,被问过 Spring Boot 的自动配置源码怎么实现的,没看过源码的我只能投降��了. 这不,赶紧来补补了,所以才有了这篇文章的出现,Spring Boot 2. 0 源码阅 ...

  4. java观察者模式在spring中的应用_Spring源码之spring中的观察者模式和监听器的使用...

    声明:本文根据鲁班学院子路老师spring中观察者模式课程整理得来 观察者模式特点: 被观察者持有监听的观察者的引用. 被观察者支持增加和删除的观察者. 被观察者状态改变通知观察者. JDK中观察者i ...

  5. batch spring 重复执行_Spring源码高级笔记之——Spring AOP应用

    Spring AOP应用 AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码.日志代码.事务控制代码.性能监控代码. 第1节AOP相关术语 1.1业务主线 在讲解AO ...

  6. Spring Boot Transactional注解源码阅读笔记(二)

      在源码笔记(一)中,我们留下了几个问题: Spring Boot是怎么扫描到我们的bean里面有 Transactional 这个注解,并且把 InfrastructureAdvisorAutoP ...

  7. java spring源码_spring源码分析-spring中的bean

    接触过spring的人都知道,在spring中我们称java对象为bean,我们在spring的debug日志或者报错日志也能看到各种bean的描述.其实,spring的bean和java的对象之间是 ...

  8. vc+ mfc 方法怎么被调用_Spring源码阅读(二)我的方法是怎么被自动调用的

    曾几何时,我们在写Java代码时,写好一个类,然后去创建这个类的对象,再调用它的方法,项目可能变得有些冗余,但却其乐融融,因为我知道程序每一步干了什么, 但现在,时代变了,你发现我往往只需要实现一个接 ...

  9. 【Spring Boot实战】源码解析Spring Boot自动配置原理

    一.简介 Spring致力于让Java开发更简单,SpringBoot致力于让使用Spring进行Java开发更简单,SpringCloud致力于基于SpringBoot构建微服务生态圈,让微服务开发 ...

最新文章

  1. 腾讯!阿里!大二男生斩获4家头部科技公司实习offer!完整经验总结!
  2. PYTHON1.day01
  3. python opencv生成 html5 支持的mp4
  4. PyQt5初级教程--PyQt5中的部件II[9/13]
  5. MySQL设置表的字段值自动增加
  6. 简单介绍日志的发展历史
  7. 程序员都怎么过端午节?
  8. [剑指offer]面试题第[52]题[Leedcode][第160题][JAVA][相交链表][双指针]
  9. java 数据流 中文_【Java I/O流】File、字符集、字节流、字符流、缓冲流、数据流、对象流、序列化、try-wi...
  10. 提高效率:17款超赞的谷歌chrome浏览器插件、扩展程序
  11. Thread 类创建线程的五种基本写法
  12. require.js官方使用教程
  13. 你缺的不是一个“大牛” 而是一个透视宝
  14. 肥猫学习日记---数据结构与算法(三)-----链表
  15. 有3n个花盆,红色、蓝色和黄色的各n个。开始时排列的顺序是混乱的,如黄、红、蓝、黄、黄、蓝、黄、红、红……
  16. linux查询socket资源,TCP的socket资源被耗尽的问题
  17. int 和 Integer 有什么区别
  18. redirect-重定向
  19. TCP/IP协议数据链路层
  20. java计算机毕业设计干洗店订单管理系统设计与实现源码+mysql数据库+系统+lw文档+部署

热门文章

  1. NPOI 1.2教程(目录)
  2. c#子线程和主线程创建窗体时顶层显示的区别
  3. MFC原理 消息传递
  4. MFC中 给按钮添加图片的方法
  5. 微信小程序实现数字为四位一组间隔(仿银行卡卡号)
  6. IOS Swift5.5的通知写法
  7. 关于Visual Studio 2017安装需要注意的细节
  8. icp光谱仪的工作原理_ICP2060T ICP光谱仪
  9. java反射机制_java反射机制的讲解
  10. Fragment向ChildFragment传值