diff --git a/source/_posts/2023-12-07-use-and-analysis-of-PropertySource-annotation-in-Spring.md b/source/_posts/2023-12-07-use-and-analysis-of-PropertySource-annotation-in-Spring.md index 739bc5c8..036fd870 100644 --- a/source/_posts/2023-12-07-use-and-analysis-of-PropertySource-annotation-in-Spring.md +++ b/source/_posts/2023-12-07-use-and-analysis-of-PropertySource-annotation-in-Spring.md @@ -227,4 +227,8 @@ private void addPropertySource(PropertySource propertySource) { 可以适当地将添加属性源和使用属性分开看待,`Environment` 是它们产生联系的枢纽,`@PropertySource` 注解的处理过程是 `@Configuration` 注解的处理过程的一部分,在文件中的配置转换成为 `Environment` 中的 `PropertySource` 后,如何使用它们是独立的一件事情。 -
{% asset_img "Snipaste_2023-12-07_22-49-22.png" Environment 中的 MutablePropertySources %}
\ No newline at end of file +
{% asset_img "Snipaste_2023-12-07_22-49-22.png" Environment 中的 MutablePropertySources %}
+ +关于搭配使用的 `@Value` 注解是如何工作的,可以参考文章: + +- - {% post_link 'source-code-analysis-of-AutowiredAnnotationBeanPostProcessor-in-Spring' 'Spring AutowiredAnnotationBeanPostProcessor 的源码分析' %} \ No newline at end of file diff --git a/source/_posts/2023-12-08-source-code-analysis-of-AutowiredAnnotationBeanPostProcessor-in-Spring.md b/source/_posts/2023-12-08-source-code-analysis-of-AutowiredAnnotationBeanPostProcessor-in-Spring.md index cd4f649d..3e4491a5 100644 --- a/source/_posts/2023-12-08-source-code-analysis-of-AutowiredAnnotationBeanPostProcessor-in-Spring.md +++ b/source/_posts/2023-12-08-source-code-analysis-of-AutowiredAnnotationBeanPostProcessor-in-Spring.md @@ -1,16 +1,39 @@ --- -title: Spring AutowiredAnnotationBeanPostProcessor 的源码分析 +title: Spring AutowiredAnnotationBeanPostProcessor 的源码分析 date: 2023-12-08 12:00:30 tags: [java, spring] --- -在 `Spring` 中,`AutowiredAnnotationBeanPostProcessor` 是一个非常重要的后处理器,它可以自动装配标注注解的字段和方法,默认使用 `@Autowired` 和 `@Value` 注解,可以支持 `JSR-330` 的 `@Inject` 注解。本文通过分析源码介绍它的工作原理。 +在 `Spring` 中,`AutowiredAnnotationBeanPostProcessor` 是一个非常重要的后处理器,它可以自动装配标注注解的字段和方法,默认使用 `@Autowired` 和 `@Value` 注解,可以支持 `JSR-330` 的 `@Inject` 注解。本文通过分析源码介绍它的调用时机和工作原理。 +### 介绍 + +`AutowiredAnnotationBeanPostProcessor` 顾名思义,是自动装配注解的 `BeanPostProcessor`,但是它处理的不仅仅是 `@Autowired` 这一个注解。个人认为 `Autowired Annotation` 的意思更接近“用于标注**目标**是**被自动装配**的**注解**”。使用“目标”是为了表达注解标注的目标不仅仅限于字段,更是包括构造函数、方法、方法参数以及注解;使用“被自动装配”是为了表达注解描述的是目标的特征或者被处理的结果,体现出被动的语义更准确;使用“注解”是为了表达注解的种类不仅仅限于 `@Autowired`,还包括 `@Value` 和 `@Inject`,它们都指示目标需要被自动装配处理。 + +通过 `AutowiredAnnotationBeanPostProcessor` 的构造函数可以看到 `@Inject` 注解的特别之处,为了使用它,需要在 `Maven` 配置中额外引入 `javax.inject` 依赖。 + +```java +public AutowiredAnnotationBeanPostProcessor() { + this.autowiredAnnotationTypes.add(Autowired.class); + this.autowiredAnnotationTypes.add(Value.class); + try { + this.autowiredAnnotationTypes.add((Class) + ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); + logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); + } + catch (ClassNotFoundException ex) { + // JSR-330 API not available - simply skip. + } +} +``` + ### 入口:`populateBean` 方法 -我们在{% post_link Spring-application-context-refresh-process 'Spring 应用 context 刷新流程' %}中介绍过属性注入发生在 `populateBean` 方法中。 +我们在{% post_link how-does-Spring-load-beans 'Spring Bean 加载过程' %}中介绍过**为 `bean` 填充属性值**发生在 `populateBean` 方法中。我们也将直接从这里开始跟踪代码的处理过程。 + +> 个人认为宽松地讲,“填充属性”等于“注入属性”等于“自动装配”,前两者更侧重处理的结果,后者更侧重过程的特征,但请注意在具体的代码上下文中应辨析区别。例如为 `bean` 填充属性是 `Spring` 的重要目标之一,基于 `Autowired Annotation` 进行自动装配某一个后处理器的功能,是 `Spring` 实现目标的其中一个具体方式。 ```java protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { @@ -18,6 +41,7 @@ protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper // 如果存在 InstantiationAwareBeanPostProcessor 或者需要检查依赖 if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); + // 如果存在 InstantiationAwareBeanPostProcessor if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { @@ -39,15 +63,33 @@ protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper } ``` +> 有时候在 `Spring` 中看到 `BeanPostProcessor` 并不能代表将目光转向该接口的方法实现。不同 `BeanPostProcessor` 的子接口存在不同的调用时机。`AutowiredAnnotationBeanPostProcessor` 间接实现了 `InstantiationAwareBeanPostProcessor` 并直接实现了 `MergedBeanDefinitionPostProcessor`,这是我们今天要关注的两个重点接口。 + +**`AutowiredAnnotationBeanPostProcessor` 是什么时候注册的呢?** + +以 `AnnotationConfigApplicationContext` 为例,它在构造函数中创建了 `AnnotatedBeanDefinitionReader`,`AnnotatedBeanDefinitionReader` 又在构造函数中注册了基于注解配置的处理器: + +```java +AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); +``` + +其中就包括 `AutowiredAnnotationBeanPostProcessor`。 + ### 后处理 PropertyValues -`AutowiredAnnotationBeanPostProcessor` 实现了 `InstantiationAwareBeanPostProcessor` 接口,在 `postProcessPropertyValues` 方法中,它做了两件事情: +`AutowiredAnnotationBeanPostProcessor` 实现了 `InstantiationAwareBeanPostProcessor` 接口,该接口关注 `bean` 的实例化: -- 查找需要自动装配的元数据 -- 注入 +- `postProcessBeforeInstantiation`(实例化前) +- `postProcessAfterInstantiation`(实例化后) +- `postProcessPropertyValues`(实例化后) > `postProcessPropertyValues` 方法在工厂将给定属性值应用到给定 `bean` 之前对给定属性值进行后处理。允许检查是否满足所有依赖关系,例如基于 `bean` 属性 `setters` 上的 `@Required` 注解进行检查。还允许替换要应用的属性值,通常是通过基于原始 `PropertyValues` 创建新的 `MutablePropertyValues` 实例,并添加或删除特定值。 +`postProcessPropertyValues` 方法做了两件事情: + +- 查找需要自动装配的元数据 +- 注入 + ```java @Override public PropertyValues postProcessPropertyValues( @@ -70,16 +112,20 @@ public PropertyValues postProcessPropertyValues( ### 查找自动装配元数据 -`InjectionMetadata` 是用于管理注入元数据的内部类,不适合直接在应用程序中使用。它和 `Class` 是一对一的关系,封装了需要注入的元素 `InjectedElement`。一个 `InjectedElement` 对应着一个字段(`Field`)或一个方法(`Method`),分别对应着两个实现类 `AutowiredFieldElement` 和 `AutowiredMethodElement`。 +> 这部分代码体现了注入(Injection)和自动装配(Autowiring)的等价性。`InjectionMetadata` 和 `AutowiringMetadata` 的含义是用于注入(自动装配)的元数据。 + +`InjectionMetadata` 是用于管理注入元数据的内部类,不适合直接在应用程序中使用。它和 `Class` 是一对一的关系,封装了需要**被注入**的元素 `InjectedElement`。一个 `InjectedElement` 对应着一个字段(`Field`)或一个方法(`Method`),分别对应着两个实现类 `AutowiredFieldElement` 和 `AutowiredMethodElement`。这里再次体现了**被注入、被自动装配**的语义。 查找自动装配元数据的过程如下: - 先从缓存中获取,如果存在且不需要刷新,则直接返回结果 - 否则构建自动装配元数据并放入缓存 +> 注意:在 `postProcessPropertyValues` 第一次调用 `findAutowiringMetadata` 缓存中就已经有结果了。什么时候构建并存入缓存的呢? + ```java private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz, PropertyValues pvs) { - // 缓存 key + // 缓存 key,如果没有指定退化为使用全限定类名 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // 双重检查 // 先从缓存中获取 @@ -95,6 +141,7 @@ private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz try { // 构建自动装配元数据 metadata = buildAutowiringMetadata(clazz); + // 放入缓存 this.injectionMetadataCache.put(cacheKey, metadata); } catch (NoClassDefFoundError err) { @@ -129,10 +176,11 @@ private InjectionMetadata buildAutowiringMetadata(final Class clazz) { ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { - // 查找代表自动装配的注解:@Autowired、@Value、@Inject(可选) + // 查找表示需被自动装配的注解:@Autowired、@Value、@Inject(可选) AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { + // 不支持静态字段 if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static fields: " + field); } @@ -140,7 +188,7 @@ private InjectionMetadata buildAutowiringMetadata(final Class clazz) { } // 确定 required boolean required = determineRequiredStatus(ann); - // 创建 AutowiredFieldElement 并添加 + // 根据 field 和 required 创建 AutowiredFieldElement 并添加 currElements.add(new AutowiredFieldElement(field, required)); } } @@ -153,16 +201,18 @@ private InjectionMetadata buildAutowiringMetadata(final Class clazz) { if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } - // 查找代表自动装配的注解:@Autowired、@Value、@Inject(可选) + // 查找表示需被自动装配的注解:@Autowired、@Value、@Inject(可选) AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { + // 不支持静态方法 if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterTypes().length == 0) { + // 不支持无参数的方法 if (logger.isWarnEnabled()) { logger.warn("Autowired annotation should only be used on methods with parameters: " + method); @@ -182,14 +232,16 @@ private InjectionMetadata buildAutowiringMetadata(final Class clazz) { targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); - // 封装为 InjectionMetadata + // 封装为 InjectionMetadata 返回 return new InjectionMetadata(clazz, elements); } ``` ### 注入 -`InjectionMetadata` 的 `inject` 方法比较简单,内部会遍历并调用 `InjectedElement` 的 `inject` 方法,`AutowiredFieldElement` 和 `AutowiredMethodElement` 各自实现了 `inject` 方法。对于字段来说,注入意味着将一个解析得到的 `value` 通过反射设置到字段中;对于方法来说,注入意味着解析得到方法的参数的 `value`,然后通过反射调用方法。 +对于字段来说,注入意味着将一个解析得到的 `value` 通过反射设置到字段中;对于方法来说,注入意味着解析得到方法参数的 `value`,然后通过反射调用方法。 + +`InjectionMetadata` 的 `inject` 方法比较简单,内部会遍历并调用 `InjectedElement` 的 `inject` 方法,`AutowiredFieldElement` 和 `AutowiredMethodElement` 各自实现了 `inject` 方法。 ```java public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable { @@ -213,11 +265,9 @@ public void inject(Object target, String beanName, PropertyValues pvs) throws Th - 都使用 `DependencyDescriptor` 描述即将被注入的特定依赖项,`DependencyDescriptor` 包装了构造函数参数、方法参数或者字段,允许以统一的方式访问它们的元数据 - 都会缓存 `DependencyDescriptor` -- 都会记录自动装配的 `bean`,用于判断是否使用 `DependencyDescriptor` 的变体优化缓存 +- 都会记录自动装配的 `bean`,用于判断是否使用 `DependencyDescriptor` 的变体 `ShortcutDependencyDescriptor` 优化缓存 - 都通过 `beanFactory.resolveDependency` 解析依赖 -> 不理解缓存 `DependencyDescriptor` 代码上的注释 - #### 字段注入 ```java @@ -251,7 +301,7 @@ private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { Set autowiredBeanNames = new LinkedHashSet(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { - // beanFactory 解析依赖 + // 通过 beanFactory 解析依赖得到 value value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { @@ -261,13 +311,14 @@ private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { // 如果未缓存,则缓存 if (!this.cached) { if (value != null || this.required) { - // 缓存 desc + // 缓存 DependencyDescriptor this.cachedFieldValue = desc; // 注册依赖关系,用于控制销毁顺序 registerDependentBeans(beanName, autowiredBeanNames); // 如果自动装配的 bean 刚好只有一个 if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); + // 检测工厂里存在 bean if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { // 替换为具有预先解析的目标 bean 名称的 DependencyDescriptor 变体 @@ -295,6 +346,8 @@ private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { #### 方法注入 +> 不理解缓存 `DependencyDescriptor` 代码上的注释:Shortcut for avoiding synchronization... + ```java private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { // 是否必须 @@ -330,14 +383,15 @@ private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; Set autowiredBeans = new LinkedHashSet(paramTypes.length); TypeConverter typeConverter = beanFactory.getTypeConverter(); + // 遍历 for (int i = 0; i < arguments.length; i++) { MethodParameter methodParam = new MethodParameter(method, i); - // 创建依赖描述符 + // 为每个方法参数创建依赖描述符 DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required); currDesc.setContainingClass(bean.getClass()); descriptors[i] = currDesc; try { - // beanFactory 解析依赖 + // 通过 beanFactory 解析依赖得到 value Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter); if (arg == null && !this.required) { arguments = null; @@ -355,6 +409,7 @@ private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { if (!this.cached) { if (arguments != null) { this.cachedMethodArguments = new Object[paramTypes.length]; + // 缓存 DependencyDescriptor for (int i = 0; i < arguments.length; i++) { this.cachedMethodArguments[i] = descriptors[i]; } @@ -363,8 +418,10 @@ private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { // 如果自动装配的 bean 数量等于参数的数量 if (autowiredBeans.size() == paramTypes.length) { Iterator it = autowiredBeans.iterator(); + // 遍历 for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); + // 检测工厂里存在 bean if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { // 替换为具有预先解析的目标 bean 名称的 DependencyDescriptor 变体 @@ -411,7 +468,7 @@ private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { #### 解析已缓存的方法参数或字段 -> 为什么在这里 `beanFactory.resolveDependency` 需要的参数和未缓存时不一样啊? +> 为什么在这里 `beanFactory.resolveDependency` 需要的参数和未缓存时不一样啊?虽然内部会通过相同的方式获得 `typeConverter`,但是很奇怪啊。 ```java private Object resolvedCachedArgument(String beanName, Object cachedArgument) { @@ -427,6 +484,8 @@ private Object resolvedCachedArgument(String beanName, Object cachedArgument) { ### 解析依赖 +解析依赖的过程暂不深入。 + ```java public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { @@ -531,4 +590,76 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } } -``` \ No newline at end of file +``` + +### 构建自动装配元数据的时机 + +你在 `Debug` 的时候也许会注意到,在第一次进入 `postProcessPropertyValues` 方法,查找自动装配元数据时,就已经是从缓存中获取的了。那么究竟是**什么时候构建自动装配元数据并放入缓存的**呢?这就需要我们目前一直没有讲到的 `MergedBeanDefinitionPostProcessor` 派上用场了。在 `postProcessMergedBeanDefinition` 方法中,也调用了 `findAutowiringMetadata` 方法,这才是真正的第一次查找自动装配元数据。 + +```java +public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { + if (beanType != null) { + // 查找自动装配元数据 + InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); + // 检查配置成员 + metadata.checkConfigMembers(beanDefinition); + } +} +``` + +那么 **`MergedBeanDefinitionPostProcessor` 又是什么时候被调用的**呢?在 `doCreateBean` 方法中,创建实例后,填充属性前。 + +```java +protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) + throws BeanCreationException { + // 创建实例 + // 允许 post-processors 修改合并过的 bean definition + synchronized (mbd.postProcessingLock) { + // 如果尚未被 MergedBeanDefinitionPostProcessor 应用过 + if (!mbd.postProcessed) { + try { + // 应用 + applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + } + catch (Throwable ex) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Post-processing of merged bean definition failed", ex); + } + // 修改为被应用过 + mbd.postProcessed = true; + } + } + // 为 bean 填充属性值 +} +``` + +#### 检测配置成员 + +> `configMember` 这个命名不太理解,指的是通过配置实现注入的 `Member` (`Filed` 和 `Method` 的父类)吗? + +检测配置成员,如果不是外部管理的配置成员,则注册为外部管理的配置成员。在合并后的 `bean` 定义中,`externallyManagedConfigMembers` 保存了外部管理的配置成员,用于标记一个配置成员是外部管理的。例如当一个字段同时标注了 `@Resource` 和 `@Autowired` 注解,当 `@Resouce` 注解被处理后,该字段已经被标记,当 `@Autowired` 注解被处理时,就会跳过该字段,避免重复注入造成冲突。 + +> 这里的外部管理感觉有点指向依赖注入的控制反转思想。 + +```java +public void checkConfigMembers(RootBeanDefinition beanDefinition) { + Set checkedElements = new LinkedHashSet(this.injectedElements.size()); + // 遍历需要被注入的元素 + for (InjectedElement element : this.injectedElements) { + Member member = element.getMember(); + // 如果不是外部管理的配置成员 + if (!beanDefinition.isExternallyManagedConfigMember(member)) { + // 注册为外部管理的配置成员 + beanDefinition.registerExternallyManagedConfigMember(member); + checkedElements.add(element); + if (logger.isDebugEnabled()) { + logger.debug("Registered injected element on class [" + this.targetClass.getName() + "]: " + element); + } + } + } + // 在 `InjectionMetadata#inject` 方法中,迭代的集合将会是它 + this.checkedElements = checkedElements; +} +``` + +> 如果不了解具体的场景,可能会比较难想象这个标记的用处是什么。 \ No newline at end of file