# my-thinking-in-spring **Repository Path**: chen_qiang_dev/my-thinking-in-spring ## Basic Information - **Project Name**: my-thinking-in-spring - **Description**: Spring源码学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-07-15 - **Last Updated**: 2021-09-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README

My Thinking In Spring

作者:陈强 目前主流的 JAVA 开发框架均以 Spring 作为基础,笔者刚接触 JAVA 开始,就进入了 SpringBoot 的生态,没有经历过那个 Spring 一统天下的时代,故对 Spring 的技术掌握并不深刻,但无论是现在 SpringBoot 生态整合,还是面向微服务的 SpringCloud 生态,均是以 Spring 作为基础,所以掌握 Spring 显得尤为重要。 笔者学习 Spring 的主要途径是看了小马哥在极客时间的 Spring 编程思想,结合自己的一些认知,谈谈自己在学习完前辈的一些经验和总结后,自己对于 Spring 的一些认知。 正所谓学而不思则罔,如果在学习完前辈的经验和总结后,没有自己的思考和练习,只是照搬前人的总结死记硬背,终究是沙上建塔,一无所获。 为了尊重知识产权,本文的讨论范围只局限在笔者自己对 Spring 的思考和认知范围内,尽量做到不涉及课程的内容。但笔者对于 Spring 的认知毕竟建立在他人的知识体系上,难免设计到课程的内容会做些许模糊,若读者对于某些知识点有所迷茫,也可以到极客时间去看小马哥的原版内容。 最后,笔者认为大多 Spring 的技术点,在实际生产工作中并不会运用到,毕竟像笔者这种主要写业务代码的程序员不在少数,但知其然也要知其所以然,即使我们在工作中有许多的技术并不会应用到,但也不能放弃一颗求知若渴的心,相信在技术上的每一次深耕,都能成为走向更高台阶的一次积累。 # 第一章 Spring 的版本特性 在学习 Spring 前,首先应该注意到的就是 Spring 的各版本的差异,以及 Spring 对于 JDK 版本的支持。 Spring 框架对不同版本 JDK 的语言新特性都积极的做了支持,所以高版本的 Spring 框架自然需要更高版本的 JDK 的支持。 此处忽略 JavaEE 相关版本。 | Spring Framework | Java | JavaEE | | :--------------: | :----: | :----: | | 1.x | 1.3+ | - | | 2.x | 1.4.2+ | - | | 3.x | 5+ | - | | 4.x | 6+ | - | | 5.x | 8+ | - | Java 语言特性简要说明 - Java5 支持:枚举、泛型、注解、自动拆装箱 - Java6 支持:@Override - Java7 支持:Diamond 语法、多 catch、try-with-resource - Java8 支持:Lambda 语法、可重复注解、类型注解、时间 API 由于 Spring 积极的响应 Java 语言的新特性,在不同的版本中大量引进了 Java 的语言特性。所以相应的 Spring 版本也需要相应的 Java 版本进行支持。并且 Spring 在各个版本的实现可能有所区别,所以在学习时需要指定某个版本进行针对性的论述,而不要一概而论,笔者跟随课程学习的则是 `5.2.2.RELEASE` 版本,下面的所有示例也将基于这个版本展开。 # 第二章 Spring IOC 容器 由笔者自己的观点来看,要理解 Spring IOC 容器,首先要抛掉一些固有的观点,就是对容器的认识。通常情况下,我们会通过一些具体的现实中的拟物化的比喻来理解事务,比如:水杯就是一个容器,用来放水,IOC 容器也是一个容器,用来存储我们定义的 Bean。这种拟物化的理解虽然是正确的,但也是片面的。 在笔者学习小马哥的课程中,第二章讨论了关于 IOC 的定义,第三章讨论了关于 Spring 对于 IOC 的实现,并在两个章节中均给出了 Spring 原作者对于 IOC 的定义,以及 Spring 官网对于 IOC 的定义。此时笔者对于 IOC 的认识还是只停留在控制反转这种粗浅的层面,随着后续的学习及深入,再返回来重新学习这两个章节,便有了自己的理解。 ## 一、认识 IOC 前文中提到,我们大可不必将 IOC 容器拟物化为一个具体的实物进行理解,这样的理解是片面的。反而站在一个更加抽象的角度,能更好的理解什么是 IOC。其实笔者认为应更加的细化的理解,将 IOC 和容器这两个概念进行拆分,即应该先理解 IOC,再理解容器,而不是固化的认为 IOC 就是容器。 IOC 是一种编程思想的体现,既控制反转,也是好莱坞原则的体现。所谓的好莱坞原则就是不要打电话给我们,我们会打电话给你。在课程中小马哥提到,我们指的是应用程序所需的相关依赖和资源,而你指的就是应用程序本身。那么这里就是笔者第一次对 IOC 这个概念的理解,即应用程序所需的依赖和资源会主动的来找应用程序,而应用程序不需要去关心这些依赖和资源的来源。 那么既然理解了什么是 IOC 思想,那我们就可以猜想什么是 IOC 容器。笔者认为 IOC 容器,就是以容器的方式去实现 IOC 的思想。那么既然是容器,就必然承载了其中的物质。前文提到资源是主动的,其实也不尽然。更确切的说法应该是容器是主动的。 笔者在这里讨论主动与被动的概念,是想大家在理解控制反转时,不要狭隘的认为控制反转就是谁控制谁,谁又反转谁的概念。而是要站在一个更加宏观的角度,在理解某项技术时,也不要忘记了使用技术的根本目的,即技术是为应用程序服务的。 笔者认为在理解 IOC 思想时应该从三个方面共同理解,即应用程序,IOC 容器,资源和依赖。总结上面的论述,笔者第二次对 IOC 这个概念进行理解,即:应用程序需要的资源和依赖,由 IOC 容器主动的推送给应用程序,而应用程序不需要关注依赖和资源的来源,它们则由 IOC 容器进行管理。这里将 IOC 容器和应用程序独立开论述,并不意味着 IOC 容器和应用程序是相互独立的,只是对 IOC 容器的一种理解,而现实中我们的应用程序都是建立在 IOC 容器的基础上,所以它们并不是相互独立的。 笔者认为 IOC 容器是 IOC 思想的一种是实现方式,而根据笔者的认识,这种实现方式应该具备的能力有两点: 1. 能够管理应用程序需要的资源和依赖。 2. 能够在应用程序需要时,主动将这些资源和依赖推送给应用程序。 那么,一个最简单的 IOC 容器应该具备以上两点职责,而对于这种职责或能力,IOC 容器需要给出具体的实现,这就是我们常说的依赖查找和依赖注入。当然 IOC 容器具备的能力远不止这两点,还有生命周期的管理、配置元信息的管理等等,这里就不再赘述。 ## 二、Spring 的 IOC 实现 基于前文中我们所讨论的这种编程思想,IOC 容器的实现其实有非常多,这其中我们最熟悉的便是 Spring 的 IOC 容器,即 Spring 对于 IOC 思想的实现,一下就简称 Spring IOC 容器。 Spring IOC 容器的同样也实现了依赖注入和查找,但是官网只提及了依赖注入却未提及依赖查找,笔者猜想应该就和小马哥说的一样,依赖查找这种实现,早在别的框架中实现了,并且依赖查找必然要通过编码的方式实现,对我们业务逻辑多少有些侵入,所以官方还是比较推荐依赖注入这种实现方式。 笔者在学习的过程中,也跟着敲了一些 demo 和示例,这里可以参考工程中的代码样例,笔者在这里只做一些简单的说明。 ### 1. 依赖查找 依赖查找的方式在 Spring IOC 容器中有多种多样,大致可以分文一下这么四种: > - 通过 Bean 的名称进行查找 > - 通过 Bean 的类型进行查找 > - 通过 Bean 的名称和类型进行查找 > - 通过 Java 注解进行查找 而这其中,又可以对查找的方式进行近一步的细化: - 查找的方式可以分为:实时查找、延迟查找。 - 查找的结果可以分为:单个对象结果、集合类型结果。 参考代码模块:ioc-container-overview - 实体类的申明:huhu.io.thinking.in.spring.domain.User - Bean 的定义:dependency-lookup-context.xml - 依赖查找示例:huhu.io.thinking.in.spring.dependency.DependencyLookupDemo ### 2. 依赖注入 依赖注入的方式在 Spring IOC 容器中也有许多: > - 根据 Bean 的类型注入 > - 根据 Bean 的名称注入 > - 注入内建 Bean 对象 > - 注入非 Bean 依赖 参考代码模块:ioc-container-overview - 实体类生命:huhu.io.thinking.in.spring.repository.UserRepository - Bean 的定义:dependency-injection-context.xml - 依赖注入示例:huhu.io.thinking.in.spring.inject.DependencyInjectionDemo 在依赖注入部分,可以看到一些依赖注入,如下面这段: ```java public class UserRepository { // 忽略部分内容... private BeanFactory beanFactory; private ObjectFactory objectFactoryApplicationContext; // 忽略部分内容... } ``` 在依赖注入示例代码部分: ```java public class DependencyInjectionDemo { public static void main(String[] args) { // 忽略部分内容... // 获取自定义 Bean UserRepository userRepository = classPathXmlApplicationContext.getBean("userRepository", UserRepository.class); System.out.println(userRepository.getUsers()); // 获取依赖注入的 BeanFactory System.out.println(userRepository.getBeanFactory()); // 输出结果:org.springframework.beans.factory.support.DefaultListableBeanFactory@10bbd20a: defining beans [user,vip,objectFactory,userRepository]; root of factory hierarchy // 依赖查找 BeanFactory BeanFactory beanFactory = classPathXmlApplicationContext.getBean(BeanFactory.class); // 输出结果:Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.beans.factory.BeanFactory' available // 判断当前应用上下文是否等于依赖注入的 ApplicationContext System.out.println(classPathXmlApplicationContext == applicationContext); // 输出结果:true // 依赖查找非自定义的 Bean Environment environment = classPathXmlApplicationContext.getBean(Environment.class); System.out.println("获取 Environment 类型 Bean: " + environment); // 输出结果:获取 Environment 类型 Bean: StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]} // 忽略部分内容... } } ``` 在这里我们可以看到一些有趣的现象,这种有趣的现象也解释了依赖查找和依赖来源的区别,以及依赖的具体来源。 1. 依赖来源一:自定义 Bean 对象 > 在代码中通过 `classPathXmlApplicationContext.getBean("userRepository", UserRepository.class)` 的方式,可以获取到我们在 xml 文件中自定义的 Bean,这也是我们最常用的获取依赖的方式。 2. 依赖来源二:内建非 Bean 对象(内建依赖) > 在代码中通过 `userRepository.getBeanFactory()` 的方式确实获取了 BeanFactory 的实现类 DefaultListableBeanFactory,这个属性的来源是容器帮我们注入的,而非我们自定义的,这说明容器中存在 DefaultListableBeanFactory 这个对象。 > > 而在 `classPathXmlApplicationContext.getBean(BeanFactory.class)` 时,却抛出异常:NoSuchBeanDefinitionException,证明当前容器中并没有这个 Bean。 > > 这证明了:依赖注入的来源并一定是我们自定义的 Bean,也可以是来自于 IOC 容器的一些内建对象,并且这些内建的对象并不是 Bean,因为我们无法用 getBean() 的方式来获取它们。 2. 依赖来源三:内建 Bean 对象 > 在代码中通过 `classPathXmlApplicationContext.getBean(Environment.class)` 的方式获取到了 Environment 接口的实现类 StandardEnvironment 对象。 > > 这个对象我们并没有定义,也没有手动的进行创建,但依然可以通过 `getBean()` 的方式获取到,这证明了两点,首先它是一个 IOC 容器中的 Bean 对象,第二点它是由 IOC 容器定义的,而非用户定义的。 ### 3. 关于 ObjectFactory BeanFactory FactoryBean 在示例代码部分,可以看到 ObjectFactory 部分的相关代码。在上文中讨论依赖来源时进行了省略。因为笔者在学习时也不是特别理解它的作用,所以笔者单独做了一个示例来验证它们之间的区别: #### 3.1. 示例一 1. 定义实体类 TestObject ```java public class TestObject { // 通过类型注入 ObjectFactory private ObjectFactory userObjectFactory; // 通过类型注入 FactoryBean private FactoryBean userFactoryBean; public ObjectFactory getUserObjectFactory() { return userObjectFactory; } public void setUserObjectFactory(ObjectFactory userObjectFactory) { this.userObjectFactory = userObjectFactory; } public FactoryBean getUserFactoryBean() { return userFactoryBean; } public void setUserFactoryBean(FactoryBean userFactoryBean) { this.userFactoryBean = userFactoryBean; } @Override public String toString() { return "TestObject{" + "userObjectFactory=" + userObjectFactory + ", userFactoryBean=" + userFactoryBean + '}'; } } ``` 2. 定义 xml 配置文件 ```xml ``` 3. 编写测试类 ```java public class BeanFactoryAndObjectFactoryAndFactoryBean { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext beanFactory = new ClassPathXmlApplicationContext("/META-INF/my-test-object-context.xml"); // 依赖查找 User 对象 User user = beanFactory.getBean("user", User.class); System.out.println("依赖查找 User 对象: " + user); // 依赖查找 TestObject 对象 TestObject testObject = beanFactory.getBean("testObject", TestObject.class); System.out.println("依赖查找 TestObject 对象: " + testObject); // ObjectFactory 相关 ObjectFactory objectFactory = testObject.getUserObjectFactory(); System.out.println("依赖注入的 ObjectFactory: " + objectFactory); User user1 = objectFactory.getObject(); System.out.println("通过 ObjectFactory 获取 User1 对象: " + user1); System.out.println("通过 ObjectFactory 获取的 User 对象,和容器中自定义的 User 对象是否时同一个对象: " + (user == user1)); // FactoryBean 相关 FactoryBean factoryBean = testObject.getUserFactoryBean(); System.out.println("依赖注入的 FactoryBean: " + factoryBean); User user2 = factoryBean.getObject(); System.out.println("通过 FactoryBean 获取 User2 对象: " + user2); System.out.println(user == user2); System.out.println(user2 == factoryBean.getObject()); } } ``` 4. 输出结果 ``` 依赖查找 User 对象: User{id=18, name='陈强'} 依赖查找 TestObject 对象: TestObject{userObjectFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider@5a8e6209, userFactoryBean=null} 依赖注入的 ObjectFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider@5a8e6209 通过 ObjectFactory 获取 User1 对象: User{id=18, name='陈强'} 通过 ObjectFactory 获取的 User 对象,和容器中自定义的 User 对象是否时同一个对象: true 依赖注入的 FactoryBean: null Exception in thread "main" java.lang.NullPointerException at huhu.io.thinking.in.spring.tests.BeanFactoryAndObjectFactoryAndFactoryBean.main(BeanFactoryAndObjectFactoryAndFactoryBean.java:31) ``` #### 3.2. 结论一 - BeanFactory 是 Spring IOC 底层容器,可以通过 BeanFactory 容器进行依赖查找。 - ObjectFactory 可以用作依赖注入,再通过 getObject() 方法获取 Bean 对象。 - 当然我们也可以通过直接定义 User 字段,去直接获取到 User Bean,使用 ObjectFactory 的方式可以间接的获取到 User Bean,在课程中小马哥把这种方式称为延迟注入。同理也可以通过这种方式实现延迟查找。 - 通过 ObjectFactory 获取的 Bean 就是我们自定义的 Bean,ObjectFactory 只是改变了获取 Bean 的方式,而并不会产生新的 Bean。 - FactoryBean 此时为 null,我们将在下面做进一步的验证。 #### 3.3. 示例二 通过上面的示例我们可以看到,FactoryBean 的值为null,容器内当前并没有这个 Bean,需要配置相应类型的 Bean,才可以进行类型注入。故创建 实体类并进行注册。 1. 创建实体类 ```java public class UserFactoryBean extends AbstractFactoryBean { @Override public Class getObjectType() { return User.class; } @Override protected User createInstance() throws Exception { User user = new User(); user.setId(18L); user.setName("陈强"); return user; } } ``` 2. 修改 xml 配置文件 ```xml ``` 3. 修改测试相关代码 ```java public class BeanFactoryAndObjectFactoryAndFactoryBean { public static void main(String[] args) throws Exception { BeanFactory beanFactory = new ClassPathXmlApplicationContext("/META-INF/my-test-object-context.xml"); // 依赖查找 User 对象 User user = beanFactory.getBean("user", User.class); System.out.println("依赖查找 User 对象: " + user); // 依赖查找 TestObject 对象 TestObject testObject = beanFactory.getBean("testObject", TestObject.class); System.out.println("依赖查找 TestObject 对象: " + testObject); // ObjectFactory 相关 // ObjectFactory objectFactory = testObject.getUserObjectFactory(); // System.out.println("依赖注入的 ObjectFactory: " + objectFactory); // User user1 = objectFactory.getObject(); // System.out.println("通过 ObjectFactory 获取 User1 对象: " + user1); // System.out.println(user == user1); // FactoryBean 相关 FactoryBean factoryBean = testObject.getUserFactoryBean(); System.out.println("依赖注入的 FactoryBean: " + factoryBean); User user2 = factoryBean.getObject(); System.out.println("通过 FactoryBean 获取 User2 对象: " + user2); System.out.println(user == user2); System.out.println(user2 == factoryBean.getObject()); } } ``` 这里将 ObjectFactory 相关内容注释掉,是因为当前 Spring IOC 容器中存在两个相同类型的 Bean,通过 ObjectFactory 的 `getBean()` 方法获取 Bean,必然会抛出异常:NoUniqueBeanDefinitionException。 4. 输出结果 ``` 依赖查找 User 对象: User{id=18, name='陈强'} 依赖查找 TestObject 对象: TestObject{userObjectFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider@1f36e637, userFactoryBean=huhu.io.thinking.in.spring.tests.UserFactoryBean@578486a3} 依赖注入的 FactoryBean: huhu.io.thinking.in.spring.tests.UserFactoryBean@578486a3 通过 FactoryBean 获取 User2 对象: User{id=18, name='陈强'} false true ``` 5. 修改 xml 文件内容 ```xml ``` 6. 输出结果 ``` 依赖查找 User 对象: User{id=18, name='陈强'} 依赖查找 TestObject 对象: TestObject{userObjectFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider@1f36e637, userFactoryBean=huhu.io.thinking.in.spring.tests.UserFactoryBean@578486a3} 依赖注入的 FactoryBean: huhu.io.thinking.in.spring.tests.UserFactoryBean@578486a3 通过 FactoryBean 获取 User2 对象: User{id=18, name='陈强'} false false ``` 这里通过属性的修改,改变了 Bean 的作用域。 #### 3.4. 结论二 这里和课程中的结论是相同的。 - FactoryBean 是创建 Bean 的一种方式。 - 由于 FactoryBean 是通过 API 的方式创建 Bean,所以它可以实现更加复杂的初始化逻辑。 ### 4. 关于 BeanFactory 和 ApplicationContext 这两者的区别在网上能查到很多,所以这里不做过多说明和验证,只是简单的引用课程中给出的结论。 - BeanFactory 是最底层的 IOC 容器,它提供了查找 Bean 的最基本的方法。 - BeanFactory 有许多扩展实现,它们的命名方式均为 xxxBeanFactory。 - ApplicationContext 也继承自 BeanFactory,所以说它也是 BeanFactory 的一种实现。 - ApplicationContext 也有许多扩展实现,它们的名称方式均为 xxxApplicationContext。 - ApplicationContext 除了 IOC 容器的角色外,还提供了多种特性。 ### 5. 关于容器生命周期 关于容器的生命周期网上也有很多的文章,笔者不做过多说明,这里提供一个入口,可以从入口出发去研究 Spring IOC 容器的生命周期。 `org.springframework.context.support.AbstractApplicationContext#refresh()` # 第三章 Spring Bean Bean 作为 Spring 中的一个重要元素,Spring 官方甚至为它单独做了一个工程模块,即 `spring-beans`,读者可以在 `github` 上的 Spring 工程的官方仓库中看到,由此可见 Bean 的重要性。 既然提到 Bean 就不得不提几个关于 Bean 的几个重要接口: - `AliasRegistry` 接口,用于 Bean 别名的注册。 - `BeanNameGenerator` 接口,用于生成 Bean 的名称。 - `BeanDefinition` 接口,用于存储 Bean 的配置元信息。 - `BeanDefinitionRegistry` 接口,用于 `BeanDefinition` 的注册。 以及简单的 Bean 的生命周期相关内容,这部分可以参考代码示例: - Bean 的实例化 `huhu.io.thinking.in.spring.BeanInstantiationDemo` 代码示例。 - Bean 的初始化 `huhu.io.thinking.in.spring.BeanInitializationDemo` 代码示例。 最后笔者将谈及一些在课程中的疑问和相关的验证,并附上一些在网上查询的资料。 ## 一、Bean 名称和别名 在 Spring 框架中,我们可以大量的地方使用到 Bean 的名称。名称作为 Bean 在容器中的唯一标识符,它在依赖查找和依赖注入中都起到了至关重要重要的作用。 当然 Bean 不止可以有名称,还可以有别名,这让用户在不同的使用场景下可以给 Bean 赋予更加个性化的命名,让 Bean 的命名在不同的场景下都能够更加符合场景的语意。 ### 1. 关于 Bean 的命名 通常我们在定义 Bean 时,都会为 Bean 指定名称,这种情况下 Bean 的名称就是我们所指定的。在解析 xml 文件时,Spring 会将我们在 bean 标签中指定的 id 属性的作为 Bean 的名称,name 属性作为 Bean 的别名,其余属性作为 BeanDefinition,将三者共同封装为 `BeanDefinitionHolder` 以便后续注册 Bean 时使用。 参考源码部分节选,以 `ClassPathXmlApplicationContext` 为例,其中解析 xml 配置文件部分节选: ```java package org.springframework.beans.factory.xml; // import... public class BeanDefinitionParserDelegate { @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 获取 xml 文件中定义的 id 属性 String id = ele.getAttribute(ID_ATTRIBUTE); // 获取 xml 文件中定义的 name 属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 对 bean 别名的处理 List aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } // 对 bean 名称的处理 String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } // 校验 bean 名称的唯一性 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 对 BeanDefinition 的处理 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { // 忽略部分源码... } // 封装 BeanDefinitionHolder String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } } ``` 从这部分源码的解析中可以看出,在用户指定 Bean 的名称的情况下,Spring 对于 `beanName` 的相关处理。但还有许多的场景下,用户并未对 Bean 命名,这种情况下 Spring 会为我们的 Bean 生成一个名称。 关于 Spring 为 Bean 生成名字的部分源码,笔者认为没有再单独罗列的必要,因为这部分代码逻辑比较简单,并不复杂,读者可以自行进行验证。 笔者在这里给出源码的入口:`org.springframework.beans.factory.support.BeanNameGenerator` 接口,读者可以在接口的派生类中看到 Spring 对于 `beanName` 的生成规则。 ### 2. 关于 Bean 的别名 在上文中读者也许已经发现了,Bean 的别名在 xml 文件的解析过程中,也被一并封装在了 `BeanDefinitionHolder` 中,但这里只是对 `bean` 标签的 name 属性定义的别名进行了处理。 示例工程 BeanAliasDemo 中,在 xml 配置文件中单独定义了 `alias` 标签用于定义 Bean 的别名,笔者这对这里的别名处理做简单的源码展示。 ```java package org.springframework.core; // import... public class SimpleAliasRegistry implements AliasRegistry { // 别名和名称的映射关系 private final Map aliasMap = new ConcurrentHashMap<>(16); // 忽略部分源码... @Override public void registerAlias(String name, String alias) { // 忽略部分源码... synchronized (this.aliasMap) { // Bean 的别名和名称相同的情况下 if (alias.equals(name)) { this.aliasMap.remove(alias); // 忽略部分源码... } else { // 获取已注册的 Bean 的名称,第一次注册肯定是不存在的 String registeredName = this.aliasMap.get(alias); if (registeredName != null) { // 名称和别名已建立映射情况下,无需重复建立 if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } // 是否允许别名的覆盖,默认是允许的,所以这里取反 if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } // 忽略部分源码... } // 递归的方式检查别名的唯一性 checkForAliasCircle(name, alias); // 将别名和名称建立映射关系 this.aliasMap.put(alias, name); // 忽略部分源码... } } } // 忽略部分源码... } ``` 在这里可以看到,Spring 其实是将 Bean 的别名和名称建立了映射关系,在通过别名进行依赖查找时,实际上是先通过别名查找到 Bean 的名称,再通过名称查找到指定的 Bean。 这里还有一个小细节需要注意,如果是通过 `` 这种方式注册别名,我们可以定义多个别名,使用逗号或者分号进行分割,例如 `` 或者 `` 这两种方式。 如果是通过 `` 注册的别名,则不会有任何规则,如果有多个别名的需求,可以通过定义多个 `` 标签的方式进行实现,关于这部分读者可以自行进行验证。 ## 二、BeanDefinition 的注册 在完成 Bean 的元信息解析后,就需要将解析过后的 BeanDefinition 注册到我们当前的 Spring 应用上下文中,这个过程我们则称为 BeanDefinition 的注册。关于 Bean 的注册方式有许多,笔者这里直接列举课程中给出的方式。 - 基于 XML 配置的方式 - 基于注解的方式 - 基于 API 的方式 代码的实现读者可以参考示例工程 `spring-bean` 模块中: - 基于注解的实现 `huhu.io.thinking.in.spring.AnnotationBeanDefinitionDemo` - 基于 API 的实现 `huhu.io.thinking.in.spring.BeanDefinitionCreationDemo` 这里笔者不做过多阐述,只提出一些小的细节并加以佐证。 ### 1. 关于 @Component 和 @Configuration 在基于注解实现的注册中,笔者对两处产生了疑问,就是关于 `@Component` 注解和 `@Configuration` 注解,下面笔者给出相关的代码示例并结合网上查阅的资料进行自己的解读。 1. 定义实体类 ```java public class Car { private String brand; private String model; // 省略部分代码... } ``` ```java public class Driver { private String name; private Integer drivingAge; private Car car; // 忽略部分代码... } ``` 2. 定义配置类 ```java @Component public class MyConfig { @Bean public Car car() { Car car = new Car(); car.setBrand("奔驰"); car.setModel("E300L"); return car; } @Bean public Driver driver() { Driver driver = new Driver(); driver.setName("陈强"); driver.setDrivingAge(8); driver.setCar(car()); return driver; } } ``` 3. 测试代码 ```java public class MyApplicationContext { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 将自定义类注册到当前应用上下文中 applicationContext.register(MyConfig.class); applicationContext.refresh(); // 获取容器中的 Car Bean Map carMap = applicationContext.getBeansOfType(Car.class); System.out.println("当前应用上下文中 Car Bean 的数量为: " + carMap.size()); Car car = carMap.get("car"); System.out.println("Car Bean: " + car); // 获取容器中的 Driver Bean Map driverMap = applicationContext.getBeansOfType(Driver.class); System.out.println("当前应用上下文中 Driver Bean 的数量为: " + driverMap.size()); Driver driver = driverMap.get("driver"); System.out.println("Driver Bean: " + driver); System.out.println("--- 分割线 ---"); // Driver 中的 Car 和 容器中的 Car 是否是同一个 Car Car driverCar = driver.getCar(); System.out.println("driverCar: " + driverCar); System.out.println("car 和 driverCar 是否是同一个对象: " + (car == driverCar)); applicationContext.close(); } } ``` 5. 输出结果 ``` 当前应用上下文中 Car Bean 的数量为: 1 Car Bean: Car{brand='奔驰', model='E300L'} 当前应用上下文中 Driver Bean 的数量为: 1 Driver Bean: Driver{name='陈强', drivingAge=8, car=Car{brand='奔驰', model='E300L'}} --- 分割线 --- driverCar: Car{brand='奔驰', model='E300L'} car 和 driverCar 是否是同一个对象: false ``` 这里可以看到,当把 `MyConfig` 类作为 `@Component` 组件注册到我们的应用上下文中时,Driver 中所包含的 Car 和我们在 Spring 应用上下文中的 Car 并不是一个对象。 这里并没有什么,因为在 `MyConfig` 类中我们可以看到 `driver.setCar(car())` 显式的调用了 `car()` 方法,而该方法的实现是直接 `new Car()` 并返回,所以应用上下文中的 Car 和 Driver 中的 Car 并不是同一个 Car。 下面笔者对 `MyConfig` 类修改,进行再次注册。 6. 将 `@Component` 注解改为 `@Configuration` 注解。 ```java @Configuration public class MyConfig { // 忽略无改动部分代码... } ``` 7. 输出结果 ``` 当前应用上下文中 Car Bean 的数量为: 1 Car Bean: Car{brand='奔驰', model='E300L'} 当前应用上下文中 Driver Bean 的数量为: 1 Driver Bean: Driver{name='陈强', drivingAge=8, car=Car{brand='奔驰', model='E300L'}} --- 分割线 --- driverCar: Car{brand='奔驰', model='E300L'} car 和 driverCar 是否是同一个对象: true ``` 从上面的结果可以发现 `car` 和 `driverCar` 变成了同一个对象,这里笔者一开始也是非常疑惑的,冷静下来思考过后,虽然还无法看清变化的本质,但也有了一些个人的想法。 - `car()` 方法肯定没有被直接调用,不然应该 new 出一个新的 Car 对象 - `car()` 肯定被某个地方拦截住了 - `car()` 被拦截住以后肯定在应用上下文中进行了查找,把之前注册过的 Car Bean 的实例返回给我们 基于以上的猜想,笔者再次对代码进行改造,既然调用 `car()` 方法时时被拦截,那就将方法单独提取出来进行调用,再 debug 进行跟踪,问题就显得豁然开朗了。 ```java @Configuration public class MyConfig { // 忽略未修改的代码... @Bean public Driver driver() { Driver driver = new Driver(); driver.setName("陈强"); driver.setDrivingAge(8); // 在此进行断点并跟踪方法的执行 Car car = car(); driver.setCar(car); return driver; } } ``` 这里笔者就不进行细节性的说明了,读者可以自行的跟踪源码进行验证。笔者在这里直接给出结论。 - 被 `@Configuration` 标注的类,在注册到 Spring 应用上下文时会被 `CGLIB` 进行提升 - 由于 `MyConfig` 类被代理,所以其中的 `car()` 方法,在调用时被拦截。 关于这部分的具体实现,笔者并没有停止思索,首先 Spring 实在合适将 `@Configuration` 标注的类进行了动态代理,在拦截了 `car()` 方法后,Spring 又是怎样去做的呢,带着这些思考笔者翻阅了网上的资料,相信这部分资料读者也能轻松的找到。那么这里就给出部分的源码,进行简单的展示。 ```java package org.springframework.context.annotation; // import... public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { // 忽略无关代码... /** * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}. * Candidate status is determined by BeanDefinition attribute metadata. * @see ConfigurationClassEnhancer */ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { // 所有包含 @Configuration 的 BeanDefiniton 将被暂存在这个 map 中 Map configBeanDefs = new LinkedHashMap<>(); // 循环遍历 BeanFactory 中的所有 Bean 的名称 for (String beanName : beanFactory.getBeanDefinitionNames()) { // 通过 Bean 的名称获取 Bean 对应的 BeanDefinition BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); // 此处判断是否是一个 @Configuration 标注的类 Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE); MethodMetadata methodMetadata = null; // 忽略部分源码... if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) { if (!(beanDef instanceof AbstractBeanDefinition)) { // 忽略部分源码... } else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) { // 忽略部分源码... } // 将 Bean 的名称和 BeanDefinition 放入之前定义好的 map 中以便后续使用 configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } // 忽略部分源码... // 创建一个配置类的增强器 ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); // 便利 map 这里就是对 @Configuration 标注类进行代理的主要部分 for (Map.Entry entry : configBeanDefs.entrySet()) { // 忽略部分源码... // 获取 BeanDefinition 的 beanClass 属性 Class configClass = beanDef.getBeanClass(); // 生成一个被 CGLIB 提升过的代理对象 Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); if (configClass != enhancedClass) { if (logger.isTraceEnabled()) { // 忽略部分源码... } // 将 BeanDefinition 中的 beanClass 属性设置为被 CGLIB 提升后的新 Class 对象 beanDef.setBeanClass(enhancedClass); } } } // 忽略无关代码... } ``` 这里从节选的源码的分析,和 `enhanceConfigurationClasses` 方法的 javadoc 中,可以到看到被 `@Configuration` 标注的类时如何被提升的。抛开一些细枝末节的逻辑,不难发现其实现并不复杂。 当 `MyConfig` 类被代理后,我们在调用其中的 `car()` 方法时,实际被拦截到了另外一个方法中,这里笔者也节选部分的代码进行简单的展示和分析。 ```java package org.springframework.context.annotation; // import... class ConfigurationClassEnhancer { // 忽略无关源码... /** * Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper * handling of bean semantics such as scoping and AOP proxying. * @see Bean * @see ConfigurationClassEnhancer */ private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback { /** * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the * existence of this bean object. * @throws Throwable as a catch-all for any exception that may be thrown when invoking the * super implementation of the proxied method i.e., the actual {@code @Bean} method */ @Override @Nullable public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable { // 获取当前的 BeanFactory ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance); // 通过被拦截的方法获取 Bean 的名称 String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod); // Determine whether this bean is a scoped-proxy if (BeanAnnotationHelper.isScopedProxy(beanMethod)) { // 忽略部分源码... } // To handle the case of an inter-bean method reference, we must explicitly check the // container for already cached instances. // First, check to see if the requested bean is a FactoryBean. If so, create a subclass // proxy that intercepts calls to getObject() and returns any cached bean instance. // This ensures that the semantics of calling a FactoryBean from within @Bean methods // is the same as that of referring to a FactoryBean within XML. See SPR-6602. // 处理 FactoryBean 相关 if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) && factoryContainsBean(beanFactory, beanName)) { // 忽略部分源码... } // 处理工厂方法相关 if (isCurrentlyInvokedFactoryMethod(beanMethod)) { // The factory is calling the bean method in order to instantiate and register the bean // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually // create the bean instance. if (logger.isInfoEnabled() && BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) { // 忽略部分源码... } return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs); } // 因为 MyConfig 配置类并没有采用以上 FactoryBean 和 FactoryMethod 的方式进行注册 // 所以会执行下面的方法 return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); } private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, ConfigurableBeanFactory beanFactory, String beanName) { // The user (i.e. not the factory) is requesting this bean through a call to // the bean method, direct or indirect. The bean may have already been marked // as 'in creation' in certain autowiring scenarios; if so, temporarily set // the in-creation status to false in order to avoid an exception. // 根据 beanName 判断当前 Bean 是否在创建中,这里的 beanName 是面方法中根据 beanMethod 解析出来的 boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName); try { if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); } // 判断方法的执行是否需要参数 car() boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs); if (useArgs && beanFactory.isSingleton(beanName)) { // Stubbed null arguments just for reference purposes, // expecting them to be autowired for regular singleton references? // A safe assumption since @Bean singleton arguments cannot be optional... for (Object arg : beanMethodArgs) { // 忽略部分源码... } } // 这里通过 beanName 从 BeanFactory 中获取对应的 Bean Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) { // 忽略部分源码... } Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod(); if (currentlyInvoked != null) { // 找到当前执行方法的外层的 Bean 的名称 // 当前方法 car() // 外层 Bean: driver 它的名称也是 driver String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked); // 这里为找到的 Bean 和外层 Bean 依赖关系 // 即为 car 和 dirver 建立依赖关系 beanFactory.registerDependentBean(beanName, outerBeanName); } // 最后将这个找的 Bean 的返回 return beanInstance; } finally { // 忽略部分源码... } } } } ``` 通过上面的源码分析已经可以完全的证明笔者的猜想是正确的。这里在节选源码段落的时候,故意留下了作者写的注释,其实在注释中作者已经给出了明确的解释,在帮助笔者的理解上起到了很大的作用,希望读者们也能够留意这些注释。 关于这部分笔者最后想说,其实在 `@Configuration` 注解中也有关于代理 Bean 方法的相关描述,这里做个简单的展示。 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { // 忽略无关源码... /** * Specify whether {@code @Bean} methods should get proxied in order to enforce * bean lifecycle behavior, e.g. to return shared singleton bean instances even * in case of direct {@code @Bean} method calls in user code. This feature * requires method interception, implemented through a runtime-generated CGLIB * subclass which comes with limitations such as the configuration class and * its methods not being allowed to declare {@code final}. *

The default is {@code true}, allowing for 'inter-bean references' within * the configuration class as well as for external calls to this configuration's * {@code @Bean} methods, e.g. from another configuration class. If this is not * needed since each of this particular configuration's {@code @Bean} methods * is self-contained and designed as a plain factory method for container use, * switch this flag to {@code false} in order to avoid CGLIB subclass processing. *

Turning off bean method interception effectively processes {@code @Bean} * methods individually like when declared on non-{@code @Configuration} classes, * a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore * behaviorally equivalent to removing the {@code @Configuration} stereotype. * @since 5.2 */ boolean proxyBeanMethods() default true; } ``` 这部分内容是在前面的分析后无意间看到的,找问题要从问题的根本出发,若一开始笔者从这里出发去分析问题,相信会少很多迷惑。也希望读者在学习的过程中不要犯和笔者同样的错误。 ### 2. 关于 Bean 的实例化、初始化、销毁 这部分内容并不复杂,具体细节将在生命周期章节进行详述,笔者在这里只是简单的给出实例化和储是话的一些方式,和示例工程中参考的代码部分。 Bean 实例化的方式,参考示例工程代码 1. `huhu.io.thinking.in.spring.BeanInstantiationDemo` 代码示例 - 通过构造器 - 通过静态工厂方法 - 通过 Bean 工厂实例方法 - 基于 FactoryBean 2. `huhu.io.thinking.in.spring.ServiceLoaderBeanInstantiationDemo` 代码示例 - 基于 `ServiceLoaderFactoryBean` 的方式 - 基于 `AutowireCapableBeanFactory` 的方式 Bean 初始化的方式,参考示例工程代码 1. `huhu.io.thinking.in.spring.BeanInitializationDemo` 代码示例 - 基于 `@PostContruct` 注解 - 基于 `InitializingBean` 接口 - 通过自定义初始化方法 Bean 的延迟初始化方式,参考示例工程代码 1. `huhu.io.thinking.in.spring.factory.DefaultUserFactory` 代码示例 - 基于 `@Lazy` 注解 - 基于 xml 文件配置 Bean 的销毁,参考示例工程代码 1. `huhu.io.thinking.in.spring.factory.DefaultUserFactory` 代码示例 - 基于 `@PreDestroy` 注解 - 基于 `DisposableBean` 接口 - 通过自定义销毁方法 关于以上内容,笔者感觉实际应用中感知并不强,尤其是在 SpringBoot 的大环境下。读者在运行示例工程代码时,无需关心具体的实现,只要关注集中不同的初始化和销毁方式执行的顺序,以及延迟初始化和实时初始化的不同就可以了。具体的底层实现原理网上又许多资料可供翻阅,笔者在这里就不多加赘述了。 # 第四章 依赖查找 在 `Spring IOC 容器` 章节笔者曾经提到过,依赖查找是 IOC 容器的基本功能,但 Spring 官网并未对依赖查找功能做详细的介绍,甚至在 IOC 的介绍中对其进行了忽略,可见依赖查找功能并不是 Spring 所关注的,尤其在当下 `SpringBoot` 这个大环境中,也基本不可能遇需要直接通过 `getBean()` 的方式去查找相应的资源或依赖,所以这里笔者简单的罗列一些课程中介绍的依赖查找的方式,配合部分源码的展示进行一个简单的介绍。 ## 1. 依赖查找的几种方式 Spring 的依赖查找可以简单的分为四种,在 Spring 中分别由不同的接口进行定义,笔者在这里罗列其中的一些,由于这部的源码的实现并不复杂,感兴趣的读者可以自行翻阅学习。 - 单一类型的依赖查找 参考接口 `org.springframework.beans.factory.BeanFactory` 参考方法 `getBean(String)` 及其重载方法 - 集合类型的依赖查找 参考接口 `org.springframework.beans.factory.ListableBeanFactory` 参考方法 `getBeansOfType(Class)` 及其重载方法 - 层次性的依赖查找 参考接口 `org.springframework.beans.factory.HierarchicalBeanFactory` - 延时查找 参考接口 `org.springframework.beans.factory.ObjectFactory` ## 2. 关于 ObjectProvider 在 Spring 4.3 之前,非延迟初始化的 Bean 若要实现延时查找,可以通过 `ObjectFactory` 接口进行实现,而在 Spring 4.3 时,提供了 `ObjectFactory` 的派生接口 `ObjectProvider` 接口,它是一种更加安全的实现方式,并在 5.1 版本又进一步提升了对 Java 8 `Stream` 的支持。 Spring 5.1 在 `BeanFactory` 接口中提供了 `getBeanProvider(Class)` 方法,调用此方法时,并不会直接返回指定类型的 Bean,而实返回一个 `ObjectProvider` 接口,而此接口的是一个泛型接口,它的泛型就是我们指定的 Bean 的类型。这种不直接返回指定 Bean 的方式可以认为时一种延时,下面我们对 Bean 是否被延时初始化进行验证。 ```java package huhu.io.thinking.in.spring.tests; // import... public class MyObjectProviderDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(MyObjectProviderDemo.class); applicationContext.refresh(); // 通过 getBeanProvider 的方式进行延时查找 ObjectProvider objectProvider = applicationContext.getBeanProvider(User.class); System.out.println("延时查找: " + objectProvider); // 通过 getBean 的方式进行实时查找 User user = applicationContext.getBean(User.class); System.out.println("实时查找: " + user); applicationContext.close(); } } ``` 输出结果展示 ``` 延时查找: org.springframework.beans.factory.support.DefaultListableBeanFactory$1@67b92f0a Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'huhu.io.thinking.in.spring.domain.User' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1126) at huhu.io.thinking.in.spring.tests.MyObjectProviderDemo.main(MyObjectProviderDemo.java:31) ``` 在上面的示例中我们看到,当前 Spring 应用上下文中,并没有注册任何 `User` 类型的 Bean。`getBeanProvider()` 却能正常的打印输出,而使用传统 `getBean()` 的方式却抛出了异常。 抛出异常是符合我们预期的,因为我们知道当前应用上下文中并没有指定类型的 Bean 的定义,所以无法实例化这个 Bean,但使用 `getBeanProvider()` 方法进行依赖查找时,并没有抛出异常,从侧面证明了,此时的 Bean 还没有被实例化,所以这里并不会抛出异常。 下面我们将代码稍作调整,从 ObjectProvider 中获取我们指定的 Bean。 ```java package huhu.io.thinking.in.spring.tests; // import... public class MyObjectProviderDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(MyObjectProviderDemo.class); applicationContext.refresh(); // 通过 getBeanProvider 的方式进行延时查找 ObjectProvider objectProvider = applicationContext.getBeanProvider(User.class); System.out.println("延时查找: " + objectProvider); // 通过 ObjectProvider 获取指定类型的 Bean User user = objectProvider.getObject(); System.out.println("获取指定类型的Bean: " + user); applicationContext.close(); } } ``` 输出结果展示 ``` 延时查找: org.springframework.beans.factory.support.DefaultListableBeanFactory$1@67b92f0a Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'huhu.io.thinking.in.spring.domain.User' available at org.springframework.beans.factory.support.DefaultListableBeanFactory$1.getObject(DefaultListableBeanFactory.java:370) at huhu.io.thinking.in.spring.tests.MyObjectProviderDemo.main(MyObjectProviderDemo.java:31) ``` 可以看到,第二条打印语句并没有执行,而在 `objectProvider.getObject()` 就抛出了异常,并且异常的类型和上面是相同的,则又进一步证明了 `ObjectProvider` 的延时特性,在我们获取 `ObjectProvider` 时 Bean 并没有被实例化,而当我们从 `ObjectProvider` 中 `getBean()` 获取指定的 Bean 时,这个 Bean 才会被实例化。 这里笔者又去翻阅了 `ObjectProvider` 和 `ObjectFactory` 的 Javadoc,像了解其中的究竟,看到了一些有趣的描述,并引发了新的思考。 以下摘自 `ObjectFactory` 接口的 Javadoc 部分,我们前面已经知道 `ObjectProvider` 接口是 `ObjectFactory` 接口的子接口,既然是继承关系,那么它也必定实现了 `ObjectFactory` 接口的特性。 ```java package org.springframework.beans.factory; // import... /** * Defines a factory which can return an Object instance * (possibly shared or independent) when invoked. * *

This interface is typically used to encapsulate a generic factory which * returns a new instance (prototype) of some target object on each invocation. * * 忽略无关说明... */ @FunctionalInterface public interface ObjectFactory { // 忽略部分源码... } ``` 在阅读完笔者摘要的这部分说明后,可能也产生了和笔者同样的疑惑,什么叫做每次返回的都是目标对象的一个新实例,还特别标注为 `prototype` 模式,也就是我们常说的原型对象,而不是单例对象。难道就算我将 Bean 定义为单例的,它也返回给我一个新的实例吗?为了明白原作者的语意,下面我修改代码对其进行验证。 ```java package huhu.io.thinking.in.spring.tests; // import... public class MyObjectProviderDemo { public static void main(String[] args) { // 忽略无关代码... // 通过 getBeanProvider 的方式进行延时查找 ObjectProvider objectProvider = applicationContext.getBeanProvider(User.class); // 通过 ObjectProvider 获取指定类型的 Bean User userByObjectProvider1 = objectProvider.getObject(); User userByObjectProvider2 = objectProvider.getObject(); System.out.print("多次从 ObjectProvider 中进行查找获取的是同一个 Bean 吗? "); System.out.println(userByObjectProvider1 == userByObjectProvider2); // 实时查找应用上下文中的 Bean User userByBeanFactory = applicationContext.getBean(User.class); System.out.print("ObjectProvider 中查找的 Bean 是应用上下文中的 Bean 吗? "); System.out.println(userByBeanFactory == objectProvider.getObject()); // 忽略无关代码... } /** * 在应用上下文中定义一个单例的 Bean */ @Bean public User user() { User user = new User(); user.setId(2L); user.setName("呼呼"); return user; } } ``` 输出结果展示 ``` 多次从 ObjectProvider 中进行查找获取的是同一个 Bean 吗? true ObjectProvider 中查找的 Bean 是应用上下文中的 Bean 吗? true ``` 从实际的验证结果看,这里可见并非原型对象,而是单例对象。那么框架作者的意思难道并非是指目标对象是原型的,而是指承载了目标对象的 `ObjectProvider` 对象是原型的吗,再次修改代码。 ```java package huhu.io.thinking.in.spring.tests; // import... public class MyObjectProviderDemo { public static void main(String[] args) { // 忽略无关代码... // 进行两次查找 ObjectProvider objectProvider1 = applicationContext.getBeanProvider(User.class); ObjectProvider objectProvider2 = applicationContext.getBeanProvider(User.class); // 验证两次查找的结果 System.out.println("两次查找的 ObjectProvider 对象是否是同一个对象? "); System.out.println(objectProvider1 == objectProvider2); // 忽略无关代码... } // 忽略 Bean 的定义... } ``` 输出结果展示 ``` 两次查找的 ObjectProvider 对象是否是同一个对象? false ``` 从上面的输出结果可以看出在使用 `getBeanProvider()` 进行依赖查找时,都会生成一个新的对象,而从 `ObjectProvider` 中获取目标 Bean 则还是我们注册的单例对象,这个现象的原理本质,在笔者翻阅源码的实现后,发现了其本质的实现原理并没有非常的复杂。 在源码展示前,笔者不禁想说,语言及文化的差异在很大一个程度上导致了我们中国程序员对框架底层实现的理解困难,有时看国外人写的文章或者看官网的文档,即使进行翻译,看起来也是非常的迷惑,像笔者这种非计算机科班出生,并且开发年限不长,又只是普通人并没有什么计算机天赋的程序员,应该不在少数,对于这种情况,只有努力的学习和钻研,才是唯一出路,一声长叹。 言归正传,展示源码部分。在前面的章节中,我们曾经说过 IOC 容器的底层实现是 `BeanFactory` 接口,它提供了依赖查找的基本功能。而在 5.1 版本中,其提供了 `getBeanProvider()` 方法,而此方法在 `BeanFactory` 接口的实现类中,无论是哪种实现,都是采用了内部类的方式。笔者此例使用 `AnnotationConfigApplicationContext` 这个上下文实际底层使用的就是 `DefaultListableBeanFactory` 的实现,读者可以自行 `Debug` 进行验证。 ```java package org.springframework.beans.factory.support; // import... public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { // 忽略无关源码... @SuppressWarnings("unchecked") @Override public ObjectProvider getBeanProvider(ResolvableType requiredType) { return new BeanObjectProvider() { @Override public T getObject() throws BeansException { // 从 BeanFactory 容器中获取 Bean T resolved = resolveBean(requiredType, null, false); if (resolved == null) { throw new NoSuchBeanDefinitionException(requiredType); } return resolved; } // 忽略无关源码... }; } } ``` 从笔者截取的这部分源码中可以看出。每次返回的 `ObjectProvider` 对象都是 `new` 的一个内部类,所以 Spring 的作者使用 `prototype` 来形容它。而 `resolveBean()` 方法会从容器中去查找目标 Bean,若查找不到会显式的抛出异常,这也符合我们前面的验证。至于 `resolveBean()` 方法具体式怎么做的,我们会在后续的章节中进行分析。 至于为什么说 `ObjectProvider` 在依赖查找时更加安全,读者可以参考示例工程中的 `TypeSafetyDependencyLookupDemo` 以及笔者所标注的 `Javadoc` 说明,进行阅读及自己的相关验证。 # 第五章 依赖注入 依赖注入可以说是 Spring IOC 容器的核心功能之一,也是最能体现 IOC 思想的地方。笔者在学习这一部分章节时,尤其是关于依赖注入的原理及底层实现时,其实是非常迷茫的。主要原因还是对整个 Spring 框架对依赖的处理过程的比较迷茫,它其中又涉及了 Spring 的整个生命周期相关的过程。所以这里笔者将依照自己的想法对源码进行梳理,并对困惑的点进行验证。 ## 一、依赖注入的方式 Spring 为依赖注入提供了多种多样的方式,而笔者日常生活中使用的最多的是基于注解的方式进行依赖注入。这里就不得不提笔者与 SpringBoot 的不解之缘,相信大家在日常的开发工作中使用最多的也是 `@Autowired` 注解,这个注解其实是 Spring 原生提供的,而它也仅是 Spring 提供的依赖注入的众多方式之一。 笔者本章将基于 `@Autowired` 注解进行验证,至于其他的实现方式,基本也是大同小异,笔者就不一一阐述了,读者可以自行翻阅相关资料,进行源码的阅读。 ## 二、关于 @Autowired 注解 在 `@Autowired` 注解的 `Javadoc` 部分已经详细介绍了此注解的使用方式,这里简单的罗列下 - Autowired Constructors - Autowired Fields - Autowired Methods - Autowired Parameters - Multiple Arguments and 'required' Semantics - Autowiring Arrays, Collections, and Maps 可以看出其实 `@Autowired` 注解的使用方式还是非常多的,而笔者最常用的还是基于 `Fields` 的字段注入,相信这也是大部分人使用的方式。而在 `Javadoc` 的其它部分还提供了一些相关类: - org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - java.util.Optional - java.util.Collection - java.util.Map - org.springframework.core.Ordered - org.springframework.beans.factory.config.BeanPostProcessor - org.springframework.beans.factory.config.BeanFactoryPostProcessor - org.springframework.beans.factory.annotation.Qualifier - org.springframework.core.annotation.Order - org.springframework.beans.factory.annotation.Value 关于这部分内容,笔者将结合演示代码进行说明,其中 `AutowiredAnnotationBeanPostProcessor` 是处理 `@Autowired` 注解的回调,关于这个类会单独提出做源码分析。 ### 1. 字段注入 - 通过 `UserConfig` 配置类,定义两个 `User` 类型的 Bean,其中 `VipUser` 是 `User` 的子类。 ```java package huhu.io.thinking.in.spring.config; // import... @Configuration public class UserConfig { @Bean public User user() { User user = new User(); user.setId(1L); user.setName("陈强"); return user; } @Bean public User vipUser() { VipUser vipUser = new VipUser(); vipUser.setId(2L); vipUser.setName("呼呼"); vipUser.setAddress("南京"); return vipUser; } } ``` - 定义 `UserClub` 类承接所有 `User` 类型的 Bean ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "UserClub{" + "user=" + user + '}'; } } ``` - 定义测试基类 ```java package huhu.io.thinking.in.spring; // import... public class AutowiredAnnotationTests { @Test public void test01() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(UserConfig.class); applicationContext.register(UserClub.class); applicationContext.refresh(); // 依赖查找 UserClub bean = applicationContext.getBean(UserClub.class); User user = bean.getUser(); // 输出结果 if (user != null) System.out.println(user); else System.out.println("依赖注入失败"); applicationContext.close(); } } ``` 从测试类中我们可以看到,如果 `UserClub` 中的 `User` 依赖注入成功,那就直接打印以来的内容,否则打印 `依赖注入失败`。 读者可以看到,这是我们常用的一种依赖注入的方式,即字段注入,这种依赖注入的方式相信不用笔者多说,直接放出打印结果。 ``` User{id=1, name='陈强', city=null, resource=null, workCities=null, lifeCities=null} ``` 但是如此简单的字段注入中,也隐藏了一些细节值得我们细细推敲,笔者在这里一些小的实验,并针对每个实验给出个人的一些猜想。 问题一:当前 Spring 引用上下文中存在两个 `User` 类型的 Bean,为什么 Spring 选择了 `User` 而非 `VipUser`,依赖注入是否和字段的名称有关? #### 1.1. 字段注入的猜想 1. 修改字段名为 `vipUser` ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired private User vipUser; // 忽略无关代码... } ``` 得到输出结果 ``` VipUser{address='南京'} User{id=2, name='呼呼', city=null, resource=null, workCities=null, lifeCities=null} ``` 可以发现输出结果变成了 `VipUser`,基本确认了依赖注入字段和字段名是有关的。 2. 对字段的名称进行随意修改 ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired private User abc; // 忽略无关代码... } ``` 得到输出结果 ``` org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userClub': Unsatisfied dependency expressed through field 'abc'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'huhu.io.thinking.in.spring.domain.User' available: expected single matching bean but found 2: user,vipUser ``` Spring 在应用上下文中找到了多个 `User` 类型的 Bean,无法确定需要注入的是哪个 Bean。 3. 字段名称保持 `abc` 不变,将 `VipUser` 定义为首要 Bean。 ```java package huhu.io.thinking.in.spring.config; // import... @Configuration public class UserConfig { // 忽略无关代码... @Bean @Primary public User vipUser() { // 忽略无关代码... } } ``` 得到输出结果 ``` VipUser{address='南京'} User{id=2, name='呼呼', city=null, resource=null, workCities=null, lifeCities=null} ``` 可以看出当 Spring 引用上下文存在多个同类型的 Bean 的情况下,会优先考虑标注了 `@Primary` 注解的 Bean。 4. 将字段名称变为 `user` 而 `@Primary` 注解依然标注在 `VipUser` 的申明上。 ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired private User user; // 忽略无关代码... } ``` 得到输出结果 ``` VipUser{address='南京'} User{id=2, name='呼呼', city=null, resource=null, workCities=null, lifeCities=null} ``` 可以看到输出依然是 `VipUser`,并没有因为字段名而发生改变。 #### 1.2. 字段注入的结论 在进行了上述例子后,相信读者也发现了其中的一些细节,笔者这里进行一个简单总结。 - 当应用上下文相同类型的 Bean 只存在一个时,Spring 通过类型就能直接判断出我们所需的依赖。 - 当应用上下文中存在多个同类型的 Bean 时,优先考虑 `Primary Bean`,当然 `primray` 的定义可以时基于 xml 文件的,也可以是基于 `@Primary` 注解的。 - 若应用上下文中并没有定义 `Primary Bean` 时,会根据字段名称判断我们需要的依赖。 - 若以上条件均未成立,Spring 则无法确定我们需要的依赖,抛出 `UnsatisfiedDependencyException` 异常。 那么既然有了验证结果,我们不难发现,Spring 在进行依赖解析时,首先会依据依赖的类型,在应用上下文中查找指定类型的 Bean,若只存在一个,则直接将这个 Bean 进行注入。若存在多个 Bean,那么会先判断哪个 Bean 是 `Primary` 的,若找到了 `Primary` 的,则将找的 Bean 进行注入。若不存在 `Primary` 的 Bean,则判断字段名称与 Bean 名称相同的 Bean 进行注入,若还是没有找到,则抛出异常。 这里只是通过验证反推了一个可能的实现方案,也许它不是正确的。相信大家在学习了一些 Spring 的知识后,也能写出上面的验证过程并不复杂。笔者在这里花费时间进行验证,是想向读者展示一个思考的过程,这个过程远比 Spring 底层到底是如何实现的更加重要。 有了上面的认识,读者可以自行去验证构造器注入、setter 方法注入、集合类型注入、Map 类型注入、以及 Optional 注入。它们基本都是大同小异,也可以参考笔者的工程示例 `E-dependency-injection` 模块的代码。 ### 2. 关于 @Qualifier 注解 其实上面的验证是符合正常人逻辑的,但是并不是正确的,比方说基于 `@Qualifier` 注解限定的情况,笔者并没有考虑进去,相信这个注解大家并不陌生。当应用上下文中存在多个 Bean 的情况下,我们可以使用 `@Qualifier` 注解去指定需要的 Bean,这是一种更加细粒度的注入。 #### 2.1. 基础使用 在应用上下文中,定义多个相同类型的 Bean ```java package huhu.io.thinking.in.spring.config; // import... @Configuration public class UserConfig { @Bean public User user1() { User user = new User(); user.setId(1L); user.setName("陈强1"); return user; } @Bean public User user2() { User user = new User(); user.setId(2L); user.setName("陈强2"); return user; } @Bean public User user3() { User user = new User(); user.setId(3L); user.setName("陈强3"); return user; } @Bean public User user4() { User user = new User(); user.setId(4L); user.setName("陈强4"); return user; } @Bean @Primary public User vipUser() { VipUser vipUser = new VipUser(); vipUser.setId(5L); vipUser.setName("呼呼"); vipUser.setAddress("南京"); return vipUser; } } ``` 在 `UserClub` 类中使用 `@Qualifier` 注解进行条件限定。 ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired @Qualifier("user3") private User abc; // ... } ``` 测试类代码忽略,直接给出输出结果 ``` User{id=3, name='陈强3', city=null, resource=null, workCities=null, lifeCities=null} ``` 可以看到,此时的字段名是随便定义的 `abc`,而标注了 `@Primary` 注解的是 `VipUser`,但是输出的却是 `user3`,这说明了 `@Qualifier` 注解的优先级比 `@Primary` 注解更加高。 但 `@Qualifier` 注解的用法用法远不止此,在课程中小马哥提到了 `@Qualifier` 注解的分组功能,笔者针对这个分组功能,将进行进一步的验证。 #### 2.2. 分组特性 定义多个 `User` 类型的 Bean,并将其进行分组,单数的分入 `group1`,双数的分入 `group2`。 ```java package huhu.io.thinking.in.spring.config; // import... @Configuration public class UserConfig { @Bean @Qualifier("group1") public User user1() { User user = new User(); user.setId(1L); user.setName("陈强1"); return user; } @Bean @Qualifier("group2") public User user2() { User user = new User(); user.setId(2L); user.setName("陈强2"); return user; } @Bean @Qualifier("group1") public User user3() { User user = new User(); user.setId(3L); user.setName("陈强3"); return user; } @Bean @Qualifier("group2") public User user4() { User user = new User(); user.setId(4L); user.setName("陈强4"); return user; } @Bean @Qualifier("group1") public User vipUser() { VipUser vipUser = new VipUser(); vipUser.setId(5L); vipUser.setName("呼呼"); vipUser.setAddress("南京"); return vipUser; } } ``` 在 `UserClub` 中进行分组注入。 ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired @Qualifier("group1") private List usersSingle; @Autowired @Qualifier("group2") private List usersDouble; @Autowired private List usersAll; // ... } ``` 在测试类中分别打印对应分组的内容,这里直接给出输出内容。 ``` usersSingle User{id=1, name='陈强1', city=null, resource=null, workCities=null, lifeCities=null} User{id=3, name='陈强3', city=null, resource=null, workCities=null, lifeCities=null} VipUser{address='南京'} User{id=5, name='呼呼', city=null, resource=null, workCities=null, lifeCities=null} usersDouble User{id=2, name='陈强2', city=null, resource=null, workCities=null, lifeCities=null} User{id=4, name='陈强4', city=null, resource=null, workCities=null, lifeCities=null} usersAll User{id=1, name='陈强1', city=null, resource=null, workCities=null, lifeCities=null} User{id=2, name='陈强2', city=null, resource=null, workCities=null, lifeCities=null} User{id=3, name='陈强3', city=null, resource=null, workCities=null, lifeCities=null} User{id=4, name='陈强4', city=null, resource=null, workCities=null, lifeCities=null} VipUser{address='南京'} User{id=5, name='呼呼', city=null, resource=null, workCities=null, lifeCities=null} ``` 这个特性可以说非常实用,当一个接口有许多的实现时,并要在不同的业务场景下使用不同的实现,这会是一个非常便利的实现方案。当然,基于 `@Qualifier` 注解还有许多功能,比方说课程中提到的基于 `@Qualifier` 注解的自定义派生注解,那么它的可变性将更加丰富。 ### 3. 关于 @Order 注解 这个注解在 Spring 中通常担任的优先级排序的角色,而它在依赖注入的场景中也不列外,笔者这里就给出一个简单的示例进行验证。 ```java package huhu.io.thinking.in.spring.config; // import... @Configuration public class UserConfig { @Bean public User user1() { User user = new User(); user.setId(1L); user.setName("陈强1"); return user; } @Bean public User user2() { User user = new User(); user.setId(2L); user.setName("陈强2"); return user; } } ``` 默认情况下,Spring 对 Bean 的处理是按照定义的顺序进行的。这里我们看到,在这个类中我们先定义了 `user1`,再定义了 `user2`,那么它们的输出顺序就应该是我们定义的顺序。这里忽略测试代码,直接给出结果。 ``` User{id=1, name='陈强1', city=null, resource=null, workCities=null, lifeCities=null} User{id=2, name='陈强2', city=null, resource=null, workCities=null, lifeCities=null} ``` 为了保证严谨,读者可以自行将这两个 Bean 的定义顺序进行对调,以验证默认的规则,这里笔者就不进行展示了。这里我们将 `user2` 上添加 `@Order` 直接,并为其定义优先级。 ```java package huhu.io.thinking.in.spring.config; // import... @Configuration public class UserConfig { @Bean public User user1() { // ... } @Bean @Order(Ordered.LOWEST_PRECEDENCE - 1) public User user2() { // ... } } ``` `@Order` 注解的默认优先级是最低优先级,这里将优先级跳到倒数第二,让 `user2` 的优先级高于 `user1` 的优先级,在进行输出。 ``` User{id=2, name='陈强2', city=null, resource=null, workCities=null, lifeCities=null} User{id=1, name='陈强1', city=null, resource=null, workCities=null, lifeCities=null} ``` 可以看到依赖注入的顺序发生了改变,实际上 `@Order` 注解在许多对优先级有要求的场景下都能起到作用,则例笔者就不详述了,读者可以自行翻阅资料进行验证。 ### 4. 依赖注入解析与依赖注入处理 在经过上文中的一些简单代码示例后,相信读者对依赖注入应该有了一个基本的了解,对其用法的多样性也有了一定的认识。那么 Spring 的底层是如何对依赖进行处理的呢,在学习小马哥的课程中,笔者第一遍看时,还是比较迷茫的,笔者觉得主要有两点原因,第一是对 Spring 框架的陌生,第二是笔者学习时间基本就是上班时间摸鱼,这种学习方式的效率是极低的,所以如果有时间进行系统性的学习,千万不要以这种方式学习。 言归正传,Spring 底层是如何处理依赖的。这主要分为两个部分,第一部分是依赖的处理、第二部分是依赖的解析。这两个概念可以简单的理解为,依赖处理就是将我们的依赖进行过滤和封装,而依赖解析则是对封装好的数据进行判断和处理。 #### 4.1. 依赖处理 在第二节 `关于 @Autowired 注解` 时,我们提到过 `AutowiredAnnotationBeanPostProcessor` 这个回调接口,Spring 对于 `@Autowired` 注解的主要处理过程,就封装在这里类中。 从 `AutowiredAnnotationBeanPostProcessor` 的命名就能看出,它是处理自动绑定注解的 `BeanPostProcessor`,在 `Javadoc` 部分描述了它可以处理哪些注,像我们最常用的 `@Autowired` 注解和 `@Value` 注解,处理这两个注解外,它还能处理 `@Lookup` 注解和 JSR-330 的 `@Inject` 注解。 笔者本例中以 `@Autowired` 注解为例,由简入繁的对依赖处理的过程进行解析。 依赖注入类的定义: ```java package huhu.io.thinking.in.spring.config; // import... @Component public class UserClub { @Autowired private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } } ``` 测试类: ```java package huhu.io.thinking.in.spring; // import... public class AutowiredAnnotationTests { @Test public void test01() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(UserConfig.class); applicationContext.register(UserClub.class); applicationContext.refresh(); UserClub bean = applicationContext.getBean(UserClub.class); User user = bean.getUser(); System.out.println(user); } } ``` 可以看到在这个测试案例中 `UserClub` 中通过 `@Autowired` 注解,依赖注入了一个 `User` 类型的 Bean,这就像在工作中,我们通常会在 `Controller` 层中依赖注入 `Service` 一样。Spring 在处理依赖时通常会经理一些特定的步骤,笔者在这里直接给出 `AutowiredAnnotationBeanPostProcessor` 中的方法进行说明。 ```java package org.springframework.beans.factory.annotation; // import... public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { private final Map injectionMetadataCache = new ConcurrentHashMap<>(256); @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // ... } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // ... } private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { // ... } } ``` 这里笔者忽略了具体的实现,先对这几个重点关注的入口方法和字段做个简单的介绍。 - 属性 `injectionMetadataCache` 用于缓存目标 Bean 的名字和注入元信息。以本例来说,目标 Bean 就是我们的 `UserClub`,注入元信息封装了我们需要注入的依赖 `User` 的相关信息。 - 方法 `postProcessMergedBeanDefinition` 用于创建 `InjectionMetadata` 注入元信息,并将其缓存到 `injectionMetadataCache` 中。 - 方法 `postProcessProperties` 用于获取注解元信息,执行注入方法。 - 内部类 `AutowiredFieldElement` 封装了注入字段的相关信息,依赖注入方法的实现,调用依赖解析获取相关依赖,进行注入。 下面笔者将针对每个方法和其中调用的一些私有方法进行分段说明,并在重点关注的代码段上进行注释说明。 方法 `postProcessMergedBeanDefinition` 源码分析。 ```java package org.springframework.beans.factory.annotation; // import... public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { // 获取 InjectionMetadata 依赖注入元信息 // 判断哪些成员需要进行外部依赖 // 并将依赖注入元信息进行缓存 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); // 初始化 BeanDefinition 中需要进行外部依赖的成员 // 即,在 BeanDefinition 中单独定义了哪些成员是需要进行外部依赖的 // 并封装了 InjectionMetadata 中的 checkedElements // 内部实现比较简单,不进行单独说明 metadata.checkConfigMembers(beanDefinition); } private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz, @Nullable PropertyValues pvs) { // 获取 Bean 的名称 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 根据 Bean 的名称在缓存中查看是否已经定义了依赖注入元信息 InjectionMetadata InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 判断是否需要刷新依赖注入元信息,内部判断逻辑比较简单 if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } // 以上这里进行加锁,再获取,再判断。主要是为了保证线程的安全,无关注入的逻辑,无需太过纠结。 // 构建依赖注入元信息 // 在这里判断哪些字段需要进行依赖注入并将它们封装到 InjectionMetadata metadata = buildAutowiringMetadata(clazz); // 将封装好的 InjectionMetadata 加入到缓存中 this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; } private InjectionMetadata buildAutowiringMetadata(final Class clazz) { // 判断是否需要进行依赖注入 // 若是 Java 的原生类,则不需要依赖注入 if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; } // 封装所有需要注入的元素 List elements = new ArrayList<>(); Class targetClass = clazz; // 因为目标类可能存在继承关系 // 所以递归到父类中去寻找是否有需要依赖注入的字段或者方法 // 直到找到 Object 这个顶级父类为止 do { // 封装当前层级需要依赖注入的元素集合 final List currElements = new ArrayList<>(); // 通过反射判断当前层级哪些字段需要进行注入 // 此方法本身也是进行循环查找,所以它会处理完本层级中的所有字段 ReflectionUtils.doWithLocalFields(targetClass, field -> { // 找到层级的类中,标注了指定注解的字段 // @Autowired @Value @Inject MergedAnnotation ann = findAutowiredAnnotation(field); if (ann != null) { // 判断被注解的字段是否是静态的,如果是静态的则做一个警告 // 跳过这个静态字段去处理下一个字段 if (Modifier.isStatic(field.getModifiers())) { // ... return; } // 判断依赖是否是必须的,默认情况下都是必须的 boolean required = determineRequiredStatus(ann); // 将当前字段封装并添加的之前定义的集合中 currElements.add(new AutowiredFieldElement(field, required)); } }); // 通过反射判断当前层级哪些方法需要进行注入 ReflectionUtils.doWithLocalMethods(targetClass, method -> { // 和上面处理字段的方式差不太多... }); // 将本层级类中所有需要依赖注入的元素添加到之前定义好的集合中 elements.addAll(0, currElements); // 将目标类重新赋值为父类 // 跳出循环的条件就是不存在父类或者已经查到了 Object 这个顶级父类 targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); // 封装 InjectionMetadata 返回 // 内部逻辑比较简单 // 若 elements 为空则封装一个空的实现返回 // 否则调用构造方法创建一个 InjectionMetadata 返回 return InjectionMetadata.forElements(elements, clazz); } } ``` 经历过以上步骤后,我们需要的依赖注入元信息 `InjectionMetadata` 就已经封装好了,进入 `postProcessProperties` 方法,循环的将每一个需要注入的依赖进行解析并注入。 方法 `postProcessProperties` 源码解析。 ```java package org.springframework.beans.factory.annotation; // import... public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 此方法在上面已经分析过了 // 这里再次调用时,直接从缓存中获取到 InjectionMetadata // 所以不会再执行一遍构建的逻辑 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 进行注入 metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; } public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // checkedElements 和 injectedElements 在上一段源码分析时已经封装好 Collection checkedElements = this.checkedElements; Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { // 循环遍历需要依赖注入的元素 for (InjectedElement element : elementsToIterate) { if (logger.isTraceEnabled()) { // ... } // 对每个元素进行注入 element.inject(target, beanName, pvs); } } } private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取需要进行依赖注入的字段 Field field = (Field) this.member; Object value; // 以存在缓存的情况 if (this.cached) { // 如果缓存了,直接获取对应的依赖 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } // 没有进行缓存的情况 else { // 根据字段的信息创建依赖描述符 // 依赖描述符顾名思义就是封装了对依赖的描述信息 DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set autowiredBeanNames = new LinkedHashSet<>(1); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { // 进行依赖的解析,将在下一届依赖解析部分进行讲解 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { // ... } synchronized (this) { if (!this.cached) { // 判断是否需要创建快捷方式 ShortcutDependencyDescriptor if (value != null || this.required) { this.cachedFieldValue = desc; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { // 创建快捷方式 ShortcutDependencyDescriptor this.cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType()); } } } else { this.cachedFieldValue = null; } // 将当前依赖注入的元素改为已被缓存的 this.cached = true; } } } if (value != null) { // 因为我们的字段通常是 private 修饰的,将依赖注入的字段的访问权限开放 ReflectionUtils.makeAccessible(field); // 通过反射将解析出的 Bean 设置到 当前的字段上 // 这里依赖的注入就完成了 field.set(bean, value); } } } } ``` 在上面的源码解析中,基于 `@Autowired` 注解实现的依赖注入的过程基本就已经清晰了,其中实际 Bean 的解析和获取部分进行了忽略,这使得整个依赖处理的过程看起来比较连贯,也更加容易理解。 实际上,最终设置到字段上的 Bean 肯定是从容器中依赖查找得来的。而提供了依赖查找能力的最底层 IOC 容器就是 `BeanFactory` 接口,在 `BeanFactory` 接口的派生接口中,有一个 `AutowireCapableBeanFactory` 接口在提供了依赖查找的同时也提供了依赖解析的能力。 #### 4.2. 依赖解析 依赖解析主要是负责对需要进行注入的字段,进行解析,包括了需要注入的字段是什么类型,从容器中查找指定的类型的 Bean。当然这其中的诸多细节,还是通过看源码的方式会显得更加直观,这里就不再展示依赖解析代码的入口了,因为在 `BeanFactory` 接口的实现类中,仅有 `DefaultListableBeanFactory` 类对依赖解析方法 `resolveDependency(DependencyDescriptor, String, Set, TypeConverter)` 做了具体的实现。 ```java package org.springframework.beans.factory.support; // import... @SuppressWarnings("serial") public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); // 判断需要依赖注入的字段的类型是否为 Optional if (Optional.class == descriptor.getDependencyType()) { return createOptionalDependency(descriptor, requestingBeanName); } // 判断需要依赖注入的字段的类型是否为 ObjectFactory 或者 ObjectProvider else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { return new DependencyObjectProvider(descriptor, requestingBeanName); } // 判断需要依赖注入的字段是否为 JSR-330 的 Provider else if (javaxInjectProviderClass == descriptor.getDependencyType()) { return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } // 不属于以上三种情况 else { // 处理延迟依赖注入 Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { // 非延迟的依赖注入 result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } } @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { // 对快捷方式的处理,之前提到的 ShortcutDependencyDescriptor Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } // 获取需要进行注入的依赖的类型 Class type = descriptor.getDependencyType(); // 主要处理基于 @Value 注解的原生类型注入 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { // ... } // 处理数组类型、集合类型、Map 类型的多 Bean 依赖 Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } // 处理非集合型的依赖注入 // 包括了对 @Qualifier 注解的处理 // 当上下文中存在多个同类型的 Bean 时,且没有 @Qualifier 注解进行限定 // 也将返回多个 Bean // Map 中的 key 是 Bean 的名称,value 是 Bean 的类型,而非 Bean 的实例 Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); // 未找到符合的情况 if (matchingBeans.isEmpty()) { // 判断这个依赖是否是必须的 if (isRequired(descriptor)) { // 若是必须的则抛出异常 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; // 处理找到多个符合的 Bean 的情况 if (matchingBeans.size() > 1) { // 这里首先处理 Primary Bean // 若不存在 Primary Bean 的情况,会根据字段的名称进行匹配 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { // 源码忽略... // 没有匹配到 Bean 的名称话,判断是否是必须的 // 若是必须的就抛出异常 // 若不是必须的则返回 null } // 根据匹配到的 Bean 的名称获取 Bean 的类型 instanceCandidate = matchingBeans.get(autowiredBeanName); } // 处理仅有一个符合的 Bean 的情况 else { // 直接获取 Bean 的名称和 Bean 的类型 Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } // 将依赖的 Bean 的名称加入到集合中 if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } // 若当前 instanceCandidate 是一个 Class 对象 if (instanceCandidate instanceof Class) { // 根据 Bean 的名称和字段的类型进行依赖查找 // 查找完后 instanceCandidate 从 Class 对象变为 Bean 的实例对象 instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; // 处理 NullBean 的情况 if (result instanceof NullBean) { // ... } // 检查字段的类型和查找到的 Bean 实例的类型是否相同 if (!ClassUtils.isAssignableValue(type, result)) { throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass()); } // 将 Bean 的实例返回 return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } } } ``` 从上面的源码分析可以看到在依赖解析过程中的大致脉络,笔者这里并未对分支流程做出解析,主要原因是分支流程的数量过于庞大,且有些分支的逻辑较为复杂,笔者在分支的入口处都做了注释,读者可以根据需要的条件修改示例代码,进入分支进行自行验证。 ### 5. 依赖注入总结 依赖注入作为 Spring IOC 的核心功能之一,其实现原理相对来说还是比较复杂的,很多的细节值得推敲。笔者虽然在上面进行了部分的源码分析,但绝不是这么点源码就能说的明白的,其中大量的细节主动的忽略掉,并且依赖注入仅仅是嵌套在 Bean 的生命周期中的一环,要想透彻的理解,不仅需要认真的学习,还要反复的练习和多角度的思考,相信在学习完后续的课程后,笔者再回顾此章节会有新的认识。 # 第六章 依赖的来源 在 spring 中,依赖的来源有很多,不仅仅是开发人员通过 xml 文件或者注解配置的 bean,spring 中还有许多内建的依赖和资源,它们伴随着容器的启动被加载,通常并不为我们所感知到。在 spring 的核心功能依赖查找和依赖注入两个场景中,依赖的来源也不尽相同。 ## 一、依赖注入和依赖查找 相信大部分读者和笔者一样,在日常的开发工作中大部分时间使用 spring 的依赖注入功能,却很少关心 spring 的依赖查找功能,毕竟依赖查找功能需要手动的基于 API 显式的调用,这多少侵入了我们的业务逻辑。这同样使得笔者并不清楚它们之间的区别,大部开发时间笔者都知识对自己封装的 bean 进行操作,很少会使用到 spring 内建的一些依赖,下面笔者将以 `BeanFactory` 为例给出一个简单的测试。 定义 `BeanFactoryHolder` 配置类 ```java package huhu.io.thinking.in.spring; // import... @Configuration public class BeanFactoryHolder { } ``` ### 1. 依赖查找 ```java package huhu.io.thinking.in.spring; // import... public class MyThinkingTests { @Test public void test() { // 创建 spring 应用上下文 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 将 BeanFactoryHolder 作为配置类注册到应用上下文 applicationContext.register(BeanFactoryHolder.class); // 启动 spring 应用上下文 applicationContext.refresh(); // 依赖查找 BeanFactory BeanFactory beanFactory = applicationContext.getBean(BeanFactory.class); System.out.println("依赖查找 BeanFactory : " + beanFactory); // 关闭 spring 应用上下文 applicationContext.close(); } } ``` 输出结果 ``` org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.beans.factory.BeanFactory' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1126) at huhu.io.thinking.in.spring.MyThinkingTests.test(MyThinkingTests.java:22) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) ``` 输出结果抛出异常 `NoSuchBeanDefinitionException` 也非常好理解,毕竟我们的配置类中并没有定义任何 bean 的配置信息,也没有在 spring 应用上下文中配置 `BeanFactory` 类型的 bean,所以依赖查找的结果并不能找到指定类型的 bean。 ### 2. 依赖注入 此时的 spring 应用上下文中很明显不存在 `BeanFactory` 类型的 bean,下面我们使用依赖注入的方式注入这个 bean,且依然不配置 `BeanFactory` 类型的 bean。 在配置类 `BeanFactoryHolder` 中进行依赖注入 ```java package huhu.io.thinking.in.spring; // import... @Configuration public class BeanFactoryHolder { @Autowired private BeanFactory beanFactory; // 提供 get 方法让测试类进行获取 public BeanFactory getBeanFactory() { return beanFactory; } } ``` 此时还是没有配置 `BeanFactory` 类型的 bean 在应用上下文中,在依赖解析时应该会抛出 `UnsatisfiedDependencyException` 异常,提示创建 bean 失败。 修改测试类 ```java package huhu.io.thinking.in.spring; // import... public class MyThinkingTests { @Test public void test() { // ... // 依赖查找 BeanFactoryHolder Bean 再通过 get 方法获取 BeanFactory BeanFactoryHolder beanFactoryHolder = applicationContext.getBean(BeanFactoryHolder.class); System.out.println("依赖注入 BeanFactory : " + beanFactoryHolder.getBeanFactory()); // ... } } ``` 输出结果 ``` 依赖注入 BeanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@42d8062c: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,beanFactoryHolder]; root of factory hierarchy ``` 这里读者可能已经发现,BeanFactory 已经被成功的注入了,从这点来看,依赖查找和依赖注入还是有些微妙的区别,这个区别主要就是它们二者依赖的来源是不同的,我们由此得出结论,spring 中存在一些对象,它可以被依赖注入,但无法被依赖查找。 ### 3. 内建依赖 从上面的分析中得知,spring 中存在一些内建的对象,可以被依赖注入,但无法被依赖查找。这里内建的依赖其实在第一章节提到过这个名词,但当时对 spring 的一些原理尚且模糊,并未完全理解。现在想想又觉得自己十分可笑,无论是通过配置文件还是通过其他方式,最终都会来到我们的 API 调用,只要知道了哪些接口是进行注册 bean 的,再通过 IDE 工具进行查找接口在哪里被调用了,就很简单的找到了 spring 在启动的过程中创建了哪些内建依赖。 这里笔者就不给出源码进行分析了,只提供接口名供读者自行翻阅源码。 - org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition - org.springframework.beans.factory.support.DefaultListableBeanFactory#registerSingleton - org.springframework.beans.factory.support.DefaultListableBeanFactory#registerResolvableDependency 这里需要注意的就是 `egisterResolvableDependency` 注册的依赖的,这些依赖比较特殊,它们不是 bean,也不是单例对象,也不被 spring 所管理,在课程中被称为 `非spring管理对象`,而笔者上文中提到的 beanFactory 对象就属于其中之一。 # 第七章 Bean 的作用域 在 spring 中 bean 的作用域最常见的就是 `singleton` 和 `prototype` 两种,相信这两种也是读者最常用的。关于这两种作用域,笔者就不多做介绍了,直接给出结论。 - singleton 单例模式,每次创建的都是同一个 bean,经过 bean 的完整生命周期 - prototype 原型模式,每次创建的都是一个新的 bean,只会经过初始化的生命周期,并不会经过销毁的生命周期。 其实这里很好理解,首先不管是哪种模式,在需要依赖时,依赖均由 spring 提供,所以自然也由 spring 来创建相关的 bea,然而单例模式因为整个上下文中只存在一个实例,所以它的销毁过程也由 spring 管理,而原型模式下,由于存在多个实例,所以 spring 只负责创建,销毁工作就交由 gc 负责了,相当于 spring 帮我们 new 了一个对象。 关于 web 相关的作用域,笔者认为并不是太重要,只要作为了解就好了。 - RequestScope - SessionScope - ApplicationScope 相关的说明可以自行查阅,其实网上说的很多,简单了解就好。 # 第八章 Bean 的生命周期 在学习一门框架时,有两个重要的元素,第一个是上下文,而另外一个就是生命周期。 了解 bean 的生命周期有助于我们了解 bean 的ji加载机制,以及实现一些更加安全且高效的自定义机制。 bean 的生命周期大体上分为下面这么几个阶段: 1. BeanDefinition 合并阶段 2. ClassLoad 阶段 3. 实例化阶段 4. 属性赋值阶段 5. Aware 回调阶段 6. 初始化阶段 7. 销毁阶段 这其中 BeanDefinition 合并阶段和 ClassLoad 阶段笔者也是第一次接触。其中 ClassLoader 阶段属于 java 标准类加载,笔者认为这一个阶段是可以忽略的。 以传统的 BeanFactory 为例,当我们需要获取一个 Bean 的时候,首先会进行 `getBean()`,相信这个方法读者应该非常熟悉了,当向 BeanFactory 容器中注册 bean 时,如果不主动的进行 `getBean()` 那么该 bean 只是以 BeanDefinition 的方式存放在容器中,并没有进行生命周期的运作,所以 `getBean()` 方法就成为了学习 bean 生命周期的入口方法,而无论已何种形式进行 `getBean()` 最终都会调用到 `AbstractBeanFactory` 中的 `doGetBean()` 方法,这里就成为学习 Bean 的生命周期的入口方法。 笔者也将从入口处出发,挑寻代码中的重点知识进行分析,以求巩固知识,温故知新。 ## 一、BeanDefinition 的合并 在 spring 中 BeanDefinition 通常有两种,一种是通用的 `GenericBeanDefinition`,基本上所有我们定义的 bean 在注册阶段都是该类型。而第二种则是 `RootBeanDefinition`,spring 会将所有用户定义的 `GenericBeanDefinition` 转变为 `RootBeanDefinition` 随后参与到 bean 的实例化。 合并 `BeanDefinition` 的入口发生在 `AbstractBeanFactory` 的 `getMergedBeanDefinition(String, BeanDefinition, BeanDefinition)` 方法,下面笔者就挑一个比较常见的场景。 ### 1. 定义 bean ```xml ``` 此处定义了两个 bean 并且这两个 bean 存在继承关系。 ### 2. 测试类 ```java package huhu.io.thinking.in.spring; import ... public class MyBeanLifecycleDemo { private static final String[] configLocations = {"META-INF/my-lifecycle-demo-context.xml"}; public static void main(String[] args) { fromBeanFactory(); } private static void fromBeanFactory() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); int count = beanDefinitionReader.loadBeanDefinitions(configLocations); System.out.println("当前已加载 bean 的数量 -> " + count); getBean(beanFactory, "user", User.class); getBean(beanFactory, "vip", VipUser.class); } private static void getBean(BeanFactory beanFactory, String beanName, Class beanClass) { Object bean = beanFactory.getBean(beanName, beanClass); System.out.println("获取 bean -> " + JSON.toJSONString(bean)); } } ``` 将上面定义的 `xml` 配置文件加载到 `BeanFactory` 容器中,并进行 `getBean()` 操作。 ### 3. 源码展示 此处笔者直接将断点打到 `AbstractBeanFactory` 的 `getMergedBeanDefinition()` 方法,进行简单说明。 ```java protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { synchronized (this.mergedBeanDefinitions) { // mbd 是 mergedBeanDefinition 的缩写,代表合并后的 beanDefinition // mbd 是用于将该方法入参的 bd 最终合并为 mbd RootBeanDefinition mbd = null; // previous 是用于缓存之前 mergedBeanDefinitions 中的 bean 的 RootBeanDefinition // 这里可以暂时忽略该定义 RootBeanDefinition previous = null; // containingBd 是当存在嵌套式的 bean 的情况下存在的 // 即一个 bean 中依赖另外一个 bean // containingBd 这里一般情况下都是 null if (containingBd == null) { // 先从 map 中获取 // 当 bean 第一次被依赖查找时,map 中并不存在 mbd // 所以返回值必然为 null mbd = this.mergedBeanDefinitions.get(beanName); } // 第一次进行以来查找时,mbd == null 为 true if (mbd == null || mbd.stale) { previous = mbd; // 情况一 // 当 user 进来时,user 并没有 parent,可以看前面 xml 的定义 // 所以 user bean 必然进入 if if (bd.getParentName() == null) { // 这里无论是 if 还是 else 都是 new 了一个新的 RootBeanDefinition // 也可以理解为深拷贝 // 目的是不改变原始的 BeanDefinition 的定义 if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } // 情况二 // 当 vip 进来时,vip 是存在 parent bean 的 // 所以 vip bean 必然进入 else else { // 定义 parent beanDefinition BeanDefinition pbd; // 下面是一个比较巧妙的操作 try { // 1.获取 parent bean 的名称 String parentBeanName = transformedBeanName(bd.getParentName()); // 2.判断 parent bean 的名称是否和当前的 bean 相同 if (!beanName.equals(parentBeanName)) { // 不相等的情况 // 递归的往 parent bean 中去找 beanDefinition pbd = getMergedBeanDefinition(parentBeanName); } // 相等的情况下 // 一个 beanFactory 容器中不可能存在两个同名的 bean // 且 beanFactory 容器是存在层次性的 // 所以会到当前 beanFactory 容器的上一级容器中去查找 else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { // 再次递归 pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // 以当前 bean 的 parent bean 为基础创建的 RootBeanDefinition mbd = new RootBeanDefinition(pbd); // 将当前 bean 的属性复写到 parent bean 中 mbd.overrideFrom(bd); } // 为 bean 设置默认的作用域 singleton if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(SCOPE_SINGLETON); } // 忽略 if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { // ... } // 将合并完成的 beanDefinition 缓存 if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } // 忽略 if (previous != null) { // ... } // 将合并完成的 beanDefinition 返回 return mbd; } } ``` 可以看出 `BeanDefinition` 的合并过程并不复杂,但需要注意的是这两个递归,简单的来说,就是如果当前正在进行合并的 bean 如果存在 parent 的情况,会先将 parent 中的 `BeanDefinition` 进行合并,再将当前 bean 的一些属性重写到合并后的 `BeanDefinition` 中。 在这个例子中 vip 的 parent 是 user,如果 user 还存在 parent 的话,会一层一层的向上递归。 ## 二、Bean 的实例化 实例化在 java 中其实就是创建对象的过程,和我们平时在写代码时 `new` 对象并没有什么区别,spring 对一个 bean 的实例化也是这样。本质上说都是将 `Class` 对象实例化到 具体的对象。 spring 在将一个对象实例化时,为用户提供了多个接口,让开发者可以干预到实例化的过程中。并且将这个实例化的过程进行进一步的细化,主要有: - 实例化前阶段 - 实例化阶段 - 实例化后阶段 在以上三个阶段中,spring 均为开发者提供了可以进行干预的手段,笔者将根据课程所学,对三个阶段进行分别的介绍,并给出自己的一些见解。 先来看下 bean 的实例化入口方法,该方法位于 `AbstractAutowireCapableBeanFactory` 中。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 忽略无关代码... // 在 xml 文件中我们定义了 bean 的 class=“...” // 这里的 class 属性是 String 类型的字符串 // 通过 resolveBeanClass 方法将 String 类型解析到 Class 类型 // 使用的就是 java 传统的类加载方式 Class resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { ... } // 忽略无关代码... try { // 1.实例化前阶段 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { ... } try { // 2.其他生命周期相关阶段 Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { ... } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { ... } } ``` 这里删除了大量无关的代码,读者需要重点关注的就是 `1` 和 `2` 两个重点方法,下面笔者将重点讲解这两个点,这里也是实例化的核心。 ### 1. bean 的实例化前阶段 bean 的实例化前阶段是一个比较特殊的阶段。它的特殊点在于如果对该阶段进行干预,有可能会导致后续的生命周期相关阶段被完全跳过。这点体现在上面的 `1` 阶段,当 `resolveBeforeInstantiation` 方法的返回值不为 null 时,则直接 return bean,而不会再继续执行后去的代码。 笔者这里给出 `resolveBeforeInstantiation` 方法的相关源码。 ```java @Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; // 判断 bean 是否在实例化前已被解析了 // 第一次解析时肯定未被解析过,所以这里 mbd.beforeInstantiationResolved = null // 所以条件必然成立进入 if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // 这里判断 bean 的 class 是否已被解析过,并且当前容器中是否含有 BeanPostProcessor if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // 获取 bean 的类型 Class targetType = determineTargetType(beanName, mbd); // 前面 bean 的类型已经被解析过,所以条件必然成立 if (targetType != null) { // 对 bean 的进行实例化 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); // 如果 bean 被实例化,则条件成立 if (bean != null) { // 如果 bean 这里被实例化了,则对 bean 进行初始化后置处理 // 初始化的相关操作将在初始化阶段进行说明 bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } // 修改 mbd.beforeInstantiationResolved 的值 mbd.beforeInstantiationResolved = (bean != null); } return bean; } ``` bean 的实例化前阶段就是执行了 `applyBeanPostProcessorsBeforeInstantiation` 方法,这个方法内部提供了回调的接口让开发者对 bean 进行干预。 ```java @Nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) { // 将容器中的所有 BeanPostProcessor 全部遍历 for (BeanPostProcessor bp : getBeanPostProcessors()) { // 找出 InstantiationAwareBeanPostProcessor 的实例 if (bp instanceof InstantiationAwareBeanPostProcessor) { // 强转 InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 执行 bean 实例化前阶段回调 Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; } ``` 到这里相信读者已经清晰了,如果读者想干预 bean 的实例化前阶段,只需要实现 `InstantiationAwareBeanPostProcessor` 接口,重写其中的 `postProcessBeforeInstantiation` 方法,并将该实现类注册到当前 BeanFactory 容器中就行了。 前面读者提到过干预实例化前阶段将导致 bean 的生命周期的终结,使 bean 的生命周期在该阶段就完成,这会导致 bean 的实例化和初始化过程不完整。那为何 spring 为我们提供这种方式呢?这里可以看下 spring 对于这个方法的说明。 ```java /** * Apply this BeanPostProcessor before the target bean gets instantiated. * The returned bean object may be a proxy to use instead of the target bean, * effectively suppressing default instantiation of the target bean. *

If a non-null object is returned by this method, the bean creation process * will be short-circuited. The only further processing applied is the * {@link #postProcessAfterInitialization} callback from the configured * {@link BeanPostProcessor BeanPostProcessors}. *

This callback will be applied to bean definitions with their bean class, * as well as to factory-method definitions in which case the returned bean type * will be passed in here. *

Post-processors may implement the extended * {@link SmartInstantiationAwareBeanPostProcessor} interface in order * to predict the type of the bean object that they are going to return here. *

The default implementation returns {@code null}. * @param beanClass the class of the bean to be instantiated * @param beanName the name of the bean * @return the bean object to expose instead of a default instance of the target bean, * or {@code null} to proceed with default instantiation * @throws org.springframework.beans.BeansException in case of errors * @see #postProcessAfterInstantiation * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getBeanClass() * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName() */ @Nullable default Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { return null; } ``` 该注释中有两句话导出了关键: - The returned bean object may be a proxy to use instead of the target bean, effectively suppressing default instantiation of the target bean. ↑ 返回的 bean 可以是一个代理,代替目标 bean,从而有效的抑制了目标 bean 的默认实例化。 - If a non-null object is returned by this method, the bean creation process will be short-circuited. ↑ 如果返回一个非空对象,bean 的创建过程将被打断。 从上面的注释可以看出,该方法主要是为目标 bean 产生代理对象使用的。这种需要为自定的 bean 生成代理对象的情况,一般很少,毕竟在面向业务开发的过程中,开发者定义的 bean,都是含有明确的业务目标的,它们可以是一个 `controller`,可以是一个 `service`,而在定义这些 bean 时就已经赋予了其具体的实现,使它们各司其职,并没有任何产生代理的必要,所以该接口回调在大部分场景下并不适用。 ### 2. bean 的实例化阶段 在 java 中,需要将一个类实例化。只能通过调用其构造方法来实现,这里 spring 对 bean 的实例化也同样如此。但是我们知道一个对象可能存在多个构造器,构造器也可能存在有参或无参的,并且如果开发者存在偏好的构造器又如何处理。这也注定了 spring 对 bean 的实例化过程是一个复杂的过程。 spring 在将 bean 实例化的过程中,提供了 `InstantiationStrategy` 接口来帮助 bean 完成实例化。下面笔者将给出两种实例化的方式。 首先 bean 的实例化入口在 `AbstractAutowireCapableBeanFactory` 类的 `createBeanInstance` 方法,笔者这里给出该方法的简略介绍。 ```java protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // 获取 bean 的类型 Class beanClass = resolveBeanClass(mbd, beanName); // 忽略... if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { ... } // 忽略... Supplier instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { ... } // 通过工厂方法实例化,本例中未指定工厂方法,忽略... if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // 这里为了提高性能,防止重复解析构造器,做了一个缓存 // 第一次解析不会进入,忽略... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { ... } if (resolved) { ... } // 获取指定的构造器,一般情况下并不会指定候选的构造器 // 可以通过实现 SmartInstantiationAwareBeanPostProcessor 接口 // 重写 determineCandidateConstructors 方法来指定使用哪个构造器 Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); // 1. 本例中第一种情况:通过构造器自动绑定的方式进行实例化 if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 忽略... ctors = mbd.getPreferredConstructors(); if (ctors != null) { ... } // 2. 本例中第二种情况:通过默认构造器进行实例化 return instantiateBean(beanName, mbd); } ``` 通过上面的源码说明可以看出,一般情况下 spring 会根据开发者的配置选择两种不同的实例化策略,第一种是通过构造器自动绑定的方式,这种方式不是默认行为,需要手动指定。第二种则是通过默认后早期的方式,这也是 spring 的默认行为,下面笔者将着重介绍这两种实例化策略。 #### 2.1. 通过默认无参构造器实例化 bean 在阅读完前文的源码的,基本的思路已经清晰了。spring 一定是通过反射调用构造器的 `newInstance` 方法完成的实例化。那么区别就在于使用哪一个构造器了。 - 实例化入口 ```java protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { ... } else { // 通过 InstantiationStrategy 进行实例化 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { ... } } ``` - 实例化策略入口 ```java @Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // 没有被 CGLIB 代理 if (!bd.hasMethodOverrides()) { Constructor constructorToUse; synchronized (bd.constructorArgumentLock) { // 第一次实例化,resolvedConstructorOrFactoryMethod 必然为 null constructorToUse = (Constructor) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { // 获取 bean 的类型 final Class clazz = bd.getBeanClass(); // 如果 bean 是一个接口,忽略... if (clazz.isInterface()) { ... } try { // java 安全相关 if (System.getSecurityManager() != null) { // 忽略... } else { constructorToUse = clazz.getDeclaredConstructor(); } // 解析完构造器,为 resolvedConstructorOrFactoryMethod 赋值 // 下次就不需要重复解析了 bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { // 忽略... } } } // 调用构造器方法 // 工具类方法比较简单,忽略代码的展示 return BeanUtils.instantiateClass(constructorToUse); } // 被 CGLIB 代理,忽略... else { return instantiateWithMethodInjection(bd, beanName, owner); } } ``` #### 2.2. 通过构造器自动绑定实例化 bean 上面展示了简单的通过无参构造器进行实例化的方式,可以说觉得部分的 bean 都是通过以上方式进行实例化的,下面进行一种特殊方式的实例化。 - 修改 bean 的配置文件。 ```xml ``` 可以看到 userWrapper 这个 bean 是通过构造器进行自动绑定的,在代码中对其进行 getBean 早操将出发该 bean 的实例化。实例工程这里进行忽略,直接从源码部分着手。 - 通过构造器自动绑定的方式进行实例化。 ```java public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor[] chosenCtors, @Nullable Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor constructorToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; // 判断是否有构造器参数,这里并没有在 xml 文件中配置构造器参数,所以肯定为 null if (explicitArgs != null) { argsToUse = explicitArgs; } else { Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { // 因为我们是第一次创建 bean, 所以此时还没有已经解析过的构造器 // 并且在配置文件中也没有配置工厂方法,所以 constructorToUse 还是为 null // 所以下面的两个 if 条件均不成立 constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // 忽略... } } if (argsToResolve != null) { // 忽略... } } // constructorToUse 和 argsToUse 初始化都是 null // 并且在上面的两个 if 中都没有被重新初始化, 所以此条件必然进入 if (constructorToUse == null || argsToUse == null) { // 这里因为前面并没有重新 SmartInstantiationAwareBeanPostProcessor 的 // determineCandidateConstructors 方法, 所以入参的 chosenCtors 必然也为 null // if 条件成立 Constructor[] candidates = chosenCtors; if (candidates == null) { // 获取 bean 的类型 Class beanClass = mbd.getBeanClass(); try { // 获取当前 bean 所有已声明的构造器 // isNonPublicAccessAllowed() 默认情况下为 true candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors()); } catch (Throwable ex) { // 忽略... } } // 判断是否仅有一个构造器: 这里笔者只定义了一个构造器, 所以条件成立 // 判断 explicitArgs 是否为空, 这里 explicitArgs 是通过 getBean 的方式传递的过来的: 这里笔者在 getBean 的时候并未指定构造器参数, 所以条件成立 // 判断 mbd 是否定义了构造器参数: 在 xml 文件中并未定义构造器参数, 所以条件成立 if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { // 获取构造器 Constructor uniqueCandidate = candidates[0]; // 判断构造器参数的数量是否为0, 也就是无参构造器 // 这里显然不成立 if (uniqueCandidate.getParameterCount() == 0) { // 忽略... } } // 判断是否是构造器绑定模式 // chosenCtors 前面已经说过为 null, 主要看后面一个判断 // 在 xml 文中我们已经明确的知道了当前的 userWrapper 是自动绑定构造器的模式了 // 所以 autowiring 必然为 true boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs; if (explicitArgs != null) { // 忽略... } // 对 resolvedValues 和 minNrOfArgs 进行初始化 else { ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // 对构造器进行排序 AutowireUtils.sortConstructors(candidates); // 进行一些参数的初始化 int minTypeDiffWeight = Integer.MAX_VALUE; Set> ambiguousConstructors = null; LinkedList causes = null; for (Constructor candidate : candidates) { // 获取当前构造器有几个参数 int parameterCount = candidate.getParameterCount(); // constructorToUse 和 argsToUse 还是为 null, 条件不成立 if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) { break; } // 当前构造器的参数数量 1 < 最小构造器的参数数量 0 不成立 if (parameterCount < minNrOfArgs) { continue; } ArgumentsHolder argsHolder; // 获取参数的类型 Class[] paramTypes = candidate.getParameterTypes(); if (resolvedValues != null) { try { // 获取构造器参数的名称, 该处是通过 ConstructorProperties 注解进行获取的 // 此处并没有申明该注解, 所以必然为 null String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount); if (paramNames == null) { // 获取参数名称发现器 ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { // 这里获取到构造器参数的名称, 即 user paramNames = pnd.getParameterNames(candidate); } } // 重点: // 到这里已经获取到了构造器参数的类型和名称 // 在 beanFactory 中进行以来解析, 获取到了 User 类型的 bean // 需要思考到底是通过类型还是通过名称获取的 bean argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1); } catch (UnsatisfiedDependencyException ex) { // 忽略... } } else { // 忽略... } int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // 这个 if 主要是针对下面的一些判断条件 // 用于判断构造器和构造器参数是否已经正常的解析出来 if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { // 忽略... } } if (constructorToUse == null) { // 忽略... } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { // 忽略... } // 将解析出来的构造器进行缓存 if (explicitArgs == null && argsHolderToUse != null) { argsHolderToUse.storeCache(mbd, constructorToUse); } } Assert.state(argsToUse != null, "Unresolved constructor arguments"); // 调用构造器对 bean 进行实例化, 和上一节的方式大同小异, 这里不再单独解析 bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); return bw; } ``` 从源码的角度看,这个方法非常长并且过程有些复杂,所以笔者对和很多代码进行了忽略和删除,可以看出这个方法的大部分过程都是在解析构造器和构造器参数。并且笔者标注了一个重点,这个重点笔者就不做源码分析了,有兴趣的读者可以自行 debug 跟进源码进行分析。 这里笔者直接给出结论,其核心就是调用了 beanFactory 的 resolveDependency 方法进行了一次依赖解析,关于依赖解析笔者已经在 `4.2` 章节进行了详细的论述。 ## 三、bean 的实例化后阶段和及属性填充阶段 之所以将这个两个阶段进行合并,是因为 bean 的实例化后阶段可以直接影响 bean 是否要进行属性填充。 ### 1. bean 的实例化后阶段 ```java public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { /** * Perform operations after the bean has been instantiated, via a constructor or factory method, * but before Spring property population (from explicit properties or autowiring) occurs. *

This is the ideal callback for performing custom field injection on the given bean * instance, right before Spring's autowiring kicks in. *

The default implementation returns {@code true}. * @param bean the bean instance created, with properties not having been set yet * @param beanName the name of the bean * @return {@code true} if properties should be set on the bean; {@code false} * if property population should be skipped. Normal implementations should return {@code true}. * Returning {@code false} will also prevent any subsequent InstantiationAwareBeanPostProcessor * instances being invoked on this bean instance. * @throws org.springframework.beans.BeansException in case of errors * @see #postProcessBeforeInstantiation */ default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } } ``` 这里从 `javadoc` 的 `@return` 解释可以清楚看到,如果实例化后阶段的回调返回 `true` 的话,将会为 bean 进行属性填充。而如果返回 `false` 的话将跳过 bean 的属性填充。 ### 2. bean 的属性填充 ```java @SuppressWarnings("deprecation") // for postProcessPropertyValues protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // 在当前的案例中 BeanWrapper 肯定不为 null if (bw == null) { // 忽略... } // 重点一 // 实例化后阶段,从整个 if 判断的内容可以看出 // 会从容器中获取 InstantiationAwareBeanPostProcessor 并调用其 postProcessAfterInstantiation 方法 // 如果返回 false 则直接 return 不再执行后续代码 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } // 获取在 xml 文件中定义的所有属性值 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); // 获取当前 bean 的自动绑定模式 // 判断是否为 [通过名称自动绑定] 或 [通过类型自动绑定] // 此例中并非这两种模式,所以忽略 int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { // 忽略... } // 是否存在 bean 实例化的后置处理器,也就是 InstantiationAwareBeanPostProcessor // 此例中为 true,因为笔者在测试代码中进行了配置。 boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); // 是否需要依赖检查,这里为 false boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; // 条件成立 if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } // 此处的 for 循环时进行属性赋值的 for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 重点二 // 调用 InstantiationAwareBeanPostProcessor 的 postProcessProperties 方法对属性进行处理 PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); // 当属性后置处理的回调返回为 null 时,默认情况下就是返回 null // 同时兼容 postProcessPropertyValues 方法回调 if (pvsToUse == null) { if (filteredPds == null) { // 从 BeanWrapper 中获取依赖描述符 // 一般情况下这个变量并没有什么用处,因为大部分情况下 // bean 不需要依赖检查,所以这个属性并不会进行相关操作 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } // postProcessPropertyValues 已被标记为 Deprecated // 这里做一个兼容 // 该方法默认情况下返回的入参 pvs // 如果返回 null 则会像实例化后阶段一样终止 bean 的属性填充过程 pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } // 如果 postProcessProperties 方法回调不为 null 则将 pvs 重新赋值 // 一般情况下在回调方法中可以对属性进行修改,再将其返回,跳过上述的操作 pvs = pvsToUse; } } } // 默认情况下不需要依赖检查,所以条件不成立 if (needsDepCheck) { // 忽略... } // 重点三 // 进行属性填充 if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } } ``` 在上述的源码分析中,笔者列出了三个重点关注的地方 - bean 的实例化后阶段 - bean 的属性填充前阶段 - bean 的属性填充阶段 其中实例化前阶段和属性填充前阶段,是开发人员可以进行干预的,而属性填充阶段是无法进行干预的。这里就不对属性填充阶段进行源码分析了,其过程也并不复杂,如果读者感兴趣可以自行进行源码的跟踪。 ## 四、Aware 接口回调阶段和 bean 的初始化阶段 这个章节涉及的生命周期比较多,笔者这里对其进行细分一下: - Aware 接口回调 - bean 的初始化前阶段 - bean 的初始化阶段 - bean 的初始化后阶段 上述的所有阶段,开发者基本都可以进行干预,也基本上都是平时开发中经常使用的一些功能,下面笔者对源码进行一个简单的分析。 ```java protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { // SecurityManager 相关操作,忽略 if (System.getSecurityManager() != null) { // 忽略... } else { // Aware 接口回调 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // bean 的初始化前阶段 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // bean 的初始化阶段 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { // 忽略... } if (mbd == null || !mbd.isSynthetic()) { // bean 的初始化后阶段 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } ``` 这里笔者并未对源码进行过分的深究,主要是经过前面多个阶段的学习,这里显得不那么复杂了。尤其像 `BeanPostProcessor` 相关实现,相信读者已经是驾轻就熟。 但是这里笔者还是要指出其中的几点以防读者在后续的使用中存在迷茫。 ### 1. Aware 接口回调 在 `BeanFactory` 场景中只能对 `BeanNameAware`、`BeanClassLoaderAware`、`BeanFactoryAware` 三个接口进行回调,参考 `AbstractAutowireCapableBeanFactory#invokeAwareMethods` 方法。 在 `ApplicationContext` 场景中不仅可以对上述的三个接口进行回调,还能对 - `EnvironmentAware` - `EmbeddedValueResolverAware` - `ResourceLoaderAware` - `ApplicationEventPublisherAware` - `MessageSourceAware` - `ApplicationContextAware` 这些 `Aware` 接口的实现进行回调,参考 `ApplicationContextAwareProcessor#invokeAwareInterfaces` 方法。 ### 2. bean 的初始化前阶段 会先调用 `InitializingBean#afterPropertiesSet` 方法进行初始化 再调用自定义的初始化方法进行初始化。 若存在 `CommonAnnotationBeanPostProcessor` 场景,则会对 `@PreConstruct` 注解标注的初始化方法进行优先调用,参考 `InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization` 方法,再对上述两个初始化方法进行调用。 ### 3. bean 的初始化完成阶段 该阶段比较特殊,需要手动调用 `DefaultListableBeanFactory#preInstantiateSingletons` 方法才能触发。 而它的回调方法则是 `SmartInitializingSingleton#afterSingletonsInstantiated`。 一般情况下用不到,它的实际用处是参与到 `ApplicationContext` 场景的生命周期中。 参考 `AbstractApplicationContext#finishBeanFactoryInitialization`。 ## 五、bean 的销毁阶段和GC bean 的销毁阶段主要是通过 `DisposableBeanAdapter` 进行相关回调。其源码十分简单,笔者这里进行一些简单的分析。 ```java public void destroy() { // 重点一:bean 的销毁前阶段 // 和初始化前阶段一样也有 CommonAnnotationBeanPostProcessor 存在的情况 // 对 @PreDestroy 注解的执行 if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } if (this.invokeDisposableBean) { if (logger.isTraceEnabled()) { // 忽略... } try { if (System.getSecurityManager() != null) { // 忽略... } else { // 重点二 // 实现 DisposableBean 接口,对 destroy 方法回调 ((DisposableBean) this.bean).destroy(); } } catch (Throwable ex) { // 忽略... } } // 重点三:自定义销毁方法 if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } else if (this.destroyMethodName != null) { Method methodToInvoke = determineDestroyMethod(this.destroyMethodName); if (methodToInvoke != null) { invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke)); } } } ``` 从源码分析上可以看出,其基本执行思路和 bean 的初始化阶段非常相似,可以当作是初始化的翻版来看。笔者这里仅指出三个需要关注的点,其余的源码进行了忽略。 并且在 bean 的销毁阶段中,这三个点都是可以由用户进行干预的。 最后讨论下 bean 的垃圾回收阶段,bean 的销毁阶段执行完成后,以为这 bean 在 IOC 容器中被销毁了,并不代表着 bean 被垃圾回收了。可以通过重写 `finalize()` 方法来证明这一点。 bean 的垃圾回收依然遵从 java 的默认机制。 # 第九章 Spring 的配置元信息 spring 的配置元信息就是对 spring 可以提供的相关配置的自定义,主要涵盖了两个部分: - spring 容器的相关配置 - spring bean 的相关配置 以及不同的配置方式涉及的相关底层 `API` 的调用,笔者认为 bean 的相关配置和使用在日常工作中比较常见,而 spring 容器相关的配置并不常用,这是因为大部分场景下,开发者并不需要修改 spring 容器的默认行为,所以笔者在本章节也着重介绍 spring bean 的相关配置元信息,以及其对应的 `API` 底层实现。 ## 一、bean 的配置元信息 bean 的配置元信息主要是封装在了 BeanDefinition 中,无论是通过 XML 的方式,还是通过 properties 的方式,亦或是通过注解的方式,其底层都是通过 API 的方式对配置进行封装。 这里笔者就列举出三种不同形式的 bean 的配置方式,并对其调用的源码进行分析。 ### 1. XML - 定义 bean ```xml ``` - 测试代码 ```java package huhu.io.thinking.in.spring.test; import huhu.io.thinking.in.spring.domain.User; import org.junit.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; /** * 测试工程一 * *

通过 xml 配置文件的方式配置 bean 的元信息 * * @author huhu * @see XmlBeanDefinitionReader * @since 2020-10-06 */ public class XmlBeanDefinitionDemo { @Test public void test() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); String xmlFilePath = "classpath:META-INF/bean-definition-configuration.xml"; XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 加载 xml 配置文件 int beanDefinitionsCount = beanDefinitionReader.loadBeanDefinitions(xmlFilePath); System.out.printf("当前已加载 beanDefinition 数量 %d 个%n", beanDefinitionsCount); User user = beanFactory.getBean("user", User.class); System.out.println(user); } } ``` 加载 xml 资源使用的 API 为 `XmlBeanDefinitionReader` 接口,该接口底层基于以下几个核心 API 来操作 xml 资源。 - 对资源的封装使用 `Resource` 接口 - 对 `xml` 资源文件的解析使用 `DOM level 3 API` - 对 BeanDefinition 的解析使用 `BeanDefinitionParserDelegate` 类 - 对 BeanDefinition 的注册使用 `BeanDefinitionRegistry` 接口