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` 接口