Annotation 的前世今生

从 Spring2.0 以后的版本中,Spring 也引入了基于注解(Annotation)方式的配置,注解(Annotation)是 JDK1.5 中引入的一个新特性,用于简化 Bean 的配置,可以取代 XML 配置文件。

开发人员对注解(Annotation)的态度也是萝卜青菜各有所爱,个人认为注解可以大大简化配置,提高开发速度,但也给后期维护增加了难度。目前来说 XML 方式发展的相对成熟,方便于统一管理。

随着 Spring Boot 的兴起,基于注解的开发甚至实现了零配置。但作为个人的习惯而言,还是倾向于 XML 配置文件和注解(Annotation)相互配合使用。

Spring IOC 容器对于类级别的注解和类内部的注解分以下两种处理策略:

1)、类级别的注解:如@Component、@Repository、@Controller、@Service 以及 JavaEE6 的 @ManagedBean 和@Named 注解,都是添加在类上面的类级别注解,Spring 容器根据注解的过滤规则扫描读取注解 Bean 定义类,并将其注册到 Spring IOC 容器中。

2)、类内部的注解:如@Autowire、@Value、@Resource 以及 EJB 和 WebService 相关的注解等,都是添加在类内部的字段或者方法上的类内部注解,SpringIOC 容器通过 Bean 后置注解处理器解析Bean 内部的注解。下面将根据这两种处理策略,分别分析 Spring 处理注解相关的源码。

定位 Bean 扫描路径

在 Spring 中 管 理 注 解 Bean 定 义 的 容 器 有 两 个 :

AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContex。

这两个类是专门处理 Spring 注解方式配置的容器,直接依赖于注解作为容器配置信息来源的 IOC 容器。

AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext 的 Web 版本,两者的用法以及对注解的处理方式几乎没有差别。现在我们以AnnotationConfigApplicationContext 为例看看它的源码:

通过上面的源码分析,我们可以看啊到 Spring 对注解的处理分为两种方式:

1)、直接将注解 Bean 注册到容器中

可以在初始化容器时注册;也可以在容器创建之后手动调用注册方法向容器注册,然后通过手动刷新容器,使得容器对注册的注解 Bean 进行处理。

2)、通过扫描指定的包及其子包下的所有类

在初始化注解容器时指定要自动扫描的路径,如果容器创建以后向给定路径动态添加了注解 Bean,则需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的 Bean 进行处理。

接下来,将会对两种处理方式详细分析其实现过程。

读取 Annotation 元数据

当创建注解处理容器时,如果传入的初始参数是具体的注解 Bean 定义类时,注解容器读取并注册。

1)、AnnotationConfigApplicationContext 通过调用注解 Bean 定义读取器

AnnotatedBeanDefinitionReader 的 register()方法向容器注册指定的注解 Bean,注解 Bean 定义读取器向容器注册注解 Bean 的源码如下:

从上面的源码我们可以看出,注册注解 Bean 定义类的基本步骤:

a、需要使用注解元数据解析器解析注解 Bean 中关于作用域的配置。

b、使用 AnnotationConfigUtils 的 processCommonDefinitionAnnotations()方法处理注解 Bean 定义类中通用的注解。

c、使用 AnnotationConfigUtils 的 applyScopedProxyMode()方法创建对于作用域的代理对象。

d、通过 BeanDefinitionReaderUtils 向容器注册 Bean。

下面我们继续分析这 4 步的具体实现过程

2)、AnnotationScopeMetadataResolver 解析作用域元数据

AnnotationScopeMetadataResolver 通过 resolveScopeMetadata()方法解析注解 Bean 定义类的作用域元信息,即判断注册的 Bean 是原生类型(prototype)还是单态(singleton)类型,其源码如下:

上述代码中的 annDef.getMetadata().getAnnotationAttributes()方法就是获取对象中指定类型的注解的值。

3)、AnnotationConfigUtils 处理注解 Bean 定义类中的通用注解

AnnotationConfigUtils 类的 processCommonDefinitionAnnotations()在向容器注册 Bean 之前,首先对注解 Bean 定义类中的通用 Spring 注解进行处理,源码如下:

4)、AnnotationConfigUtils 根据注解Bean定义类中配置的作用域为其应用相应的代理策略

AnnotationConfigUtils 类的 applyScopedProxyMode()方法根据注解 Bean 定义类中配置的作用域@Scope 注解的值,为 Bean 定义应用相应的代理模式,主要是在 Spring 面向切面编程(AOP)中使用。

源码如下:

这段为 Bean 引用创建相应模式的代理,这里不做深入的分析。

5)、BeanDefinitionReaderUtils 向容器注册 Bean

BeanDefinitionReaderUtils 主要是校验 BeanDefinition 信息,然后将 Bean 添加到容器中一个管理BeanDefinition 的 HashMap 中。


扫描指定包并解析为 BeanDefinition

当创建注解处理容器时,如果传入的初始参数是注解 Bean 定义类所在的包时,注解容器将扫描给定的包及其子包,将扫描到的注解 Bean 定义载入并注册。

1)、ClassPathBeanDefinitionScanner 扫描给定的包及其子包

AnnotationConfigApplicationContext 通 过 调 用 类 路 径 Bean 定 义 扫 描 器 ClassPathBeanDefinitionScanner 扫描给定包及其子包下的所有类,主要源码如下:

类路径 Bean 定义扫描器 ClassPathBeanDefinitionScanner 主要通过findCandidateComponents()

方法调用其父类 ClassPathScanningCandidateComponentProvider 类来扫描获取给定包及其子包下的类。

2)、ClassPathScanningCandidateComponentProvider 扫描给定包及其子包的类

ClassPathScanningCandidateComponentProvider 类的 findCandidateComponents()方法具体实现扫描给定类路径包的功能,主要源码如下:

注册注解 BeanDefinition

AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 Web 版, 它们对于注解 Bean 的注册和扫描是基本相同的,但是AnnotationConfigWebApplicationContext 对注解 Bean 定义的载入稍有不同,AnnotationConfigWebApplicationContext 注入注解 Bean 定义源码如下:

以上就是解析和注入注解配置资源的全过程分析。

IOC 容器小结

现在通过上面的代码,总结一下 IOC 容器初始化的基本步骤:

1、初始化的入口在容器实现中的 refresh()调用来完成。

2、对 Bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition(),

其中的大致过程如下:通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统,URL 等方式来定为资源位置。

如果是 XmlBeanFactory 作为 IOC 容器,那么需要为它指定 Bean 定义的资源,也 就 是 说 Bean 定 义 文 件 时 通 过 抽 象 成 Resource 来 被 IOC 容 器 处 理 的 , 容器通过BeanDefinitionReader 来 完 成 定 义 信 息 的 解 析 和 Bean 信 息 的 注 册 , 往 往 使 用 的 是 XmlBeanDefinitionReader 来 解 析 Bean 的 XML 定 义 文 件 - 实 际 的 处 理 过 程 是 委 托 给 BeanDefinitionParserDelegate 来完成的。

从而得到 bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition对象来表示-这个名字可以让我们想到loadBeanDefinition(),registerBeanDefinition()这些相关方法。

它们都是为处理 BeanDefinitin 服务的,容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程。

这个 HashMap 是 IOC 容器持有Bean 信息的场所,以后对 Bean 的操作都是围绕这个 HashMap 来实现的。

然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IOC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。

基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。

Spring本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架实现。

以下是容器初始化全过程的时序图

从源码深处体验Spring核心技术--基于注解的IOC初始化相关推荐

  1. 从源码深处体验Spring核心技术--基于Xml的IOC容器的初始化

    IOC 容器的初始化包括 BeanDefinition 的 Resource 定位.加载和注册这三个基本的过程. 我们以ApplicationContext 为例讲解,ApplicationConte ...

  2. 从源码深处体验Spring核心技术--面试中IOC那些鲜为人知的细节

    通过前面章节中对 Spring IOC 容器的源码分析,我们已经基本上了解了 Spring IOC 容器对 Bean 定义资源的定位.载入和注册过程,同时也清楚了当用户通过 getBean()方法向 ...

  3. 从源码深处体验Spring核心技术--IOC容器初体验

    开局经验之谈:可能从这一篇文章开始,小伙伴们都会有点晕车的感觉了,但是这个系列并不是只是介绍下spring表面的一些肤浅的东西,本系列的目的是为了让大家从源码层次深入理解Spring,这也是大家在未来 ...

  4. (附源码)APP+spring boot基于Android智能手机的微课程学习系统设计与实现 毕业设计100909

    摘  要 随着现在网络的快速发展,网络的应用在各行各业当中它很快融入到了许多学校的眼球之中,他们利用网络来做这个微课程学习系统的网站,随之就产生了"智能手机的微课程学习系统 ",这 ...

  5. 【Spring】基于注解的IOC案例

    代码结构: bean.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&quo ...

  6. 【Spring】IOC:基于注解的IOC容器初始化源码分析

    从 Spring2.0 以后的版本中,Spring 也引入了基于注解(Annotation)方式的配置,注解(Annotation)是 JDK1.5 中引入的一个新特性,用于简化 Bean 的配置,可 ...

  7. 专治不会看源码的毛病--spring源码解析AOP篇(2017版)

    昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些.原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们,不 ...

  8. spring源码分析第六天------spring经典面试问题

    spring源码分析第六天------spring经典面试问题 1.Spring5 新特性及应用举例 2.Spring 经典的面试问题 a.什么是 Spring 框架?Spring 框架有哪些主要模块 ...

  9. 框架源码专题:Spring是如何集成Mybatis的?Spring怎么管理Mapper接口的动态代理

    文章目录 1. Spring集成Mybatis代码示例 2. Spring 如何解析Mybatis配置文件 3. Spring是怎么管理Mapper接口的动态代理的 4. Spring整合Mybati ...

最新文章

  1. 看闯关东原来知道古代已经十六进制了
  2. Struts+2权威指南--基于WebWork核心的MVC开发源码下载
  3. 用一维数组统计五个人的成绩中的最大值最小值平均值_昨天为了整理这份教程,我放弃了参加一个几亿人的大项目。...
  4. C++ 模板:template
  5. hdu 5045 费用流
  6. RocketMQ中的死信队列
  7. 43岁被裁员,200万年薪泡汤:这4件事你要尽早明白
  8. PHP实时生成并下载超大数据量的EXCEL文件 1
  9. 什么?都2021年了还不会ajax嘛,来这里让您快速学会Ajax
  10. 158. class, static, self, parent
  11. 20190929每日一句
  12. 计算机应用基础试题答案截图,《计算机应用基础》试题二
  13. 北达科他州立大学计算机科学专业,2019上海软科世界一流学科排名计算机科学与工程专业排名北达科他州立大学排名第201-300...
  14. 前端自学驿站:【建议收藏】css晦涩难懂的点都在这啦
  15. 转:使用Python写一个m3u8多线程下载器
  16. arcgis里dem填洼_ArcGIS水文分析实战教程(4)地形预处理
  17. Zblog的控制面板代码
  18. Oracle用OEM和命令行方式创建用户及表空间
  19. 深入理解常见的二十三种设计模式
  20. 1Mbps的ECS服务器能干嘛?

热门文章

  1. Appium移动自动化配置-ios安卓
  2. 一加3 CM13 12306 不能用
  3. spring-cloud 学习三 服务提供者
  4. SQLSERVER2005的安装目录结构(下)
  5. 2014-2015-1学期使用的教材
  6. 在进行数据插入数据库操作时,对于id的处理
  7. 浅谈 举家搬迁静态文件到CDN
  8. WinSock服务程序
  9. 使用python读取mysql数据库并进行数据的操作
  10. JS学习--Date对象