Spring源码分析衍生篇五:AutowiredAnnotationBeanPostProcessor

2023-11-13

一、前言

本文是 Spring源码分析:Spring源码分析四:bean的属性注入 - populateBean 的衍生文章。主要是因为本人菜鸡,在分析源码的过程中还有一些其他的内容不理解,故开设衍生篇来完善内容以学习。


Spring源码分析衍生篇四:后处理器 BeanPostProcessor 中我们介绍了 BeanPostProcessor 后处理器的基本使用。本篇我们来介绍其中的 一个相当重要的实现类 :AutowiredAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor 完成了 Spring基本的属性注入功能(@Autowired、@Value 、@Inject 注解功能)。下面我们来详细介绍。

二、AutowiredAnnotationBeanPostProcessor

下面是 AutowiredAnnotationBeanPostProcessor 的结构图图。
在这里插入图片描述
可以看到 AutowiredAnnotationBeanPostProcessor 继承InstantiationAwareBeanPostProcessorAdapter类、实现 MergedBeanDefinitionPostProcessorPriorityOrderedBeanFactoryAware 接口。

个人理解:

  • 继承InstantiationAwareBeanPostProcessorAdapter、实现 MergedBeanDefinitionPostProcessor 是为了根据后处理器的调用时机来完成一些功能。
  • 实现 PriorityOrdered 接口是为了标注自身优先注入。
  • 实现 BeanFactoryAware 是为了拿到 BeanFactory。

在进行下面讲解之前,先了解一下AutowiredAnnotationBeanPostProcessor 中的一个Set集合 autowiredAnnotationTypes 。 autowiredAnnotationTypes 集合中保存了该类会处理的注解。

	private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);

	...
	public AutowiredAnnotationBeanPostProcessor() {
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

可以看到的是 在 AutowiredAnnotationBeanPostProcessor初始化的时, autowiredAnnotationTypes 中添加了三个注解@Autowired@Value、 和通过反射得到的 javax.inject.Inject。这三个注解也就是本类负责解析的三个注解了(@Inject 并不一定能加载到,看用户是否引入相应的包)。

三、determineCandidateConstructors

determineCandidateConstructors 方法是 InstantiationAwareBeanPostProcessorAdapter 接口中的方法。其作用是从 注入bean的所有构造函数中过滤出可以作为构造注入的构造函数列表。

@Override
	@Nullable
	public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
			throws BeanCreationException {

		// Let's check for lookup methods here...
		// 在这里首先处理了@Lookup注解
		// 判断是否已经解析过 。lookupMethodsChecked 作为一个缓存集合,保存已经处理过的bean
		if (!this.lookupMethodsChecked.contains(beanName)) {
			if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
				try {
					Class<?> targetClass = beanClass;
					do {
						// 遍历bean中的每一个方法
						ReflectionUtils.doWithLocalMethods(targetClass, method -> {
							// 判断 方法是否被 @Lookup 修饰
							Lookup lookup = method.getAnnotation(Lookup.class);
							if (lookup != null) {
								Assert.state(this.beanFactory != null, "No BeanFactory available");
								// 如果被@Lookup 修饰,则封装后保存到RootBeanDefinition 的methodOverrides 属性中,在 SimpleInstantiationStrategy#instantiate(RootBeanDefinition, String, BeanFactory) 进行了cglib的动态代理。
								LookupOverride override = new LookupOverride(method, lookup.value());
								try {
									RootBeanDefinition mbd = (RootBeanDefinition)
											this.beanFactory.getMergedBeanDefinition(beanName);
									mbd.getMethodOverrides().addOverride(override);
								}
								catch (NoSuchBeanDefinitionException ex) {
									throw new BeanCreationException(beanName,
											"Cannot apply @Lookup to beans without corresponding bean definition");
								}
							}
						});
						targetClass = targetClass.getSuperclass();
					}
					while (targetClass != null && targetClass != Object.class);

				}
				catch (IllegalStateException ex) {
					throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
				}
			}
			// 将已经解析好的beanName 添加到缓存中
			this.lookupMethodsChecked.add(beanName);
		}

		// Quick check on the concurrent map first, with minimal locking.
		// 这里开始处理构造函数
		// 获取bean的所有候选构造函数
		Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
		if (candidateConstructors == null) {
			// Fully synchronized resolution now...
			synchronized (this.candidateConstructorsCache) {
				candidateConstructors = this.candidateConstructorsCache.get(beanClass);
				// 如果构造函数为null,则通过反射获取
				if (candidateConstructors == null) {
					Constructor<?>[] rawCandidates;
					try {
						rawCandidates = beanClass.getDeclaredConstructors();
					}
					catch (Throwable ex) {
						throw new BeanCreationException(beanName,
								"Resolution of declared constructors on bean Class [" + beanClass.getName() +
								"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
					}
					List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
					// @Autowired(required = true) 的构造函数,有且只能有一个
					Constructor<?> requiredConstructor = null;
					// 默认的无参构造函数
					Constructor<?> defaultConstructor = null;
					// 针对 Kotlin 语言的构造函数,不太明白,一般为null
					Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
					int nonSyntheticConstructors = 0;
					for (Constructor<?> candidate : rawCandidates) {
						// 构造函数是否是非合成,一般我们自己创建的都是非合成的。Java在编译过程中可能会出现一些合成的构造函数
						if (!candidate.isSynthetic()) {
							nonSyntheticConstructors++;
						}
						else if (primaryConstructor != null) {
							continue;
						}
						// 遍历autowiredAnnotationTypes 集合,判断当前构造函数是否被 autowiredAnnotationTypes集合中的注解修饰,若未被修饰,则返回null
						// autowiredAnnotationTypes 集合中的注解在一开始就说了是 @Autowired、@Value 和 @Inject 三个。 
						MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
						if (ann == null) {
							// 如果未被修饰,这里判断是否是 Cglib 的代理类,如果是则获取原始类,否则直接返回beanClass
							Class<?> userClass = ClassUtils.getUserClass(beanClass);
							// 如果这里不相等,肯定是通过 cglib的代理类,这里的userClass 就是原始类,再次判断构造函数是否包含指定注解
							if (userClass != beanClass) {
								try {
									Constructor<?> superCtor =
											userClass.getDeclaredConstructor(candidate.getParameterTypes());
									ann = findAutowiredAnnotation(superCtor);
								}
								catch (NoSuchMethodException ex) {
									// Simply proceed, no equivalent superclass constructor found...
								}
							}
						}
						
						if (ann != null) {
							// 如果已经找到了必须装配的构造函数(requiredConstructor  != null),那么当前这个就是多余的,则抛出异常
							if (requiredConstructor != null) {
								throw new BeanCreationException(beanName,
										"Invalid autowire-marked constructor: " + candidate +
										". Found constructor with 'required' Autowired annotation already: " +
										requiredConstructor);
							}
							// 确定是否是必须的,@Autowired 和@Inject 默认为true。@Autowired 可以通过 required 修改
							boolean required = determineRequiredStatus(ann);
							if (required) {
								// 如果当前构造函数为必须注入,但是候选列表不为空,则说明已经有构造函数适配,则抛出异常。就是只要有required = true的构造函数就不允许存在其他可注入的构造函数
								if (!candidates.isEmpty()) {
									throw new BeanCreationException(beanName,
											"Invalid autowire-marked constructors: " + candidates +
											". Found constructor with 'required' Autowired annotation: " +
											candidate);
								}
								// 到这一步,说明当前构造函数是必须的,且目前没有其他构造函数候选
								// 直接将当前构造函数作为必须构造函数
								requiredConstructor = candidate;
							}
							// 添加到候选列表
							candidates.add(candidate);
						}
						// 如果 构造函数参数数量为0,则是默认构造函数,使用默认构造函数
						else if (candidate.getParameterCount() == 0) {
							defaultConstructor = candidate;
						}
					}
					// 如果候选构造函数不为空
					if (!candidates.isEmpty()) {
						// Add default constructor to list of optional constructors, as fallback.
						if (requiredConstructor == null) {
							if (defaultConstructor != null) {
								candidates.add(defaultConstructor);
							}
							else if (candidates.size() == 1 && logger.isInfoEnabled()) {
								logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
										"': single autowire-marked constructor flagged as optional - " +
										"this constructor is effectively required since there is no " +
										"default constructor to fall back to: " + candidates.get(0));
							}
						}
						candidateConstructors = candidates.toArray(new Constructor<?>[0]);
					}
					// 如果 当前bean只有一个有参构造函数,那么将此构造函数作为候选列表返回(这就代表,如果bean中只有一个有参构造函数并不需要使用特殊注解,也会作为构造函数进行注入)
					else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
						candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
					}
					//下面这一段判断不是太理解
					else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
							defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
						candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
					}
					else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
						candidateConstructors = new Constructor<?>[] {primaryConstructor};
					}
					else {
						candidateConstructors = new Constructor<?>[0];
					}
					this.candidateConstructorsCache.put(beanClass, candidateConstructors);
				}
			}
		}
		return (candidateConstructors.length > 0 ? candidateConstructors : null);
	}

这里的构造函数选取规则大致如下:

  1. 解析@Lookup 注解的方法,保存到 RootBeanDefinition
  2. 从缓存中获取筛选好的构造函数列表,若有直接返回,没有则进行下一步
  3. 通过反射获取bean 的所有构造函数,并进行构造函数遍历。筛选每个构造函数是否被 @Autowired、@Inject 注解修饰。
  4. 当前构造函数没有被修饰,则判断当前bean是否 是 Cglib动态代理类,如果是,则获取原始类的构造函数,再判断 构造函数是否被 @Autowired、@Inject 注解修饰。
  5. 如果第三步或者第四步成功,则根据如下规则筛选
    • 如果有一个必须注入的构造函数(@Autowired(required =true) 或者 @Inject ),则不允许有其他候选构造函数出现。有且只能筛选出一个必须注入的构造函数
    • 如果不存在必须注入的构造含函数 (@Autowired(required =false) 或者 @Inject) ,则允许多个候选注入构造函数出现(@Autowired(required = false) 修饰的构造函数)。并且将这个几个候选构造函数返回
    • 如果bean有且只有一个构造函数,即使没有被注解修饰,也会调用该构造函数作为bean创建的构造函使用
    • 上面三种情况都不满足,就按照指定的规则来进行判断返回候选列表(其实是我没看懂else if 后面的逻辑 )

四、postProcessProperties & postProcessPropertyValues

在这两个方法中完成了@Autowired、@Inject、 @Value 注解的解析。

在调用 postProcessProperties 时正是完成Bean 属性注入的时候,详情请看 Spring源码分析四:bean的属性注入 - populateBean。这里不多讲。

在postProcessProperties 方法中完成了Bean 中@Autowired、@Inject、 @Value 注解的解析并注入的功能。

具体代码如下:

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		// 筛选出需要注入的属性类型
		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;
	}

	@Deprecated
	@Override
	// 过时方法
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {

		return postProcessProperties(pvs, bean, beanName);
	}

1. findAutowiringMetadata

上面可以看到,findAutowiringMetadata完成了对需要注入属性的筛选工作,将筛选通过的bean信息缓存到injectionMetadataCache 中,表示当前加载的bean需要注入的bean属性,下面来分析如何完成。
注: 这个方法第一次调用是在 postProcessMergedBeanDefinition 方法中,后续的调用都是从缓存中获取了

	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		// Fall back to class name as cache key, for backwards compatibility with custom callers.
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		// Quick check on the concurrent map first, with minimal locking.
		// 从缓存中获取metadata
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		// 如果需要刷新  : metadata == null || metadata.needsRefresh(clazz)
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						metadata.clear(pvs);
					}
					// 创建 metadata 
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}
	// 核心的筛选功能方法
	private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		// 确定给定的类是否适合携带指定的注释,比如一些注释只能用在方法或者类上
		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}

		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			// 遍历类中的每个属性,判断属性是否包含指定的属性(通过 findAutowiredAnnotation 方法)
			// 如果存在则保存,这里注意,属性保存的类型是 AutowiredFieldElement
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});
			// 遍历类中的每个方法,判断属性是否包含指定的属性(通过 findAutowiredAnnotation 方法)
			// 如果存在则保存,这里注意,方法保存的类型是 AutowiredMethodElement
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
						}
					}
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});

			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
		// 将结果返回
		return InjectionMetadata.forElements(elements, clazz);
	}

总计 : 遍历当前bean中的所有属性和方法,如果包含指定属性,则保存起来。属性保存的类型是AutowiredFieldElement, 方法保存的类型是 AutowiredMethodElement

注: AutowiredFieldElementAutowiredMethodElementAutowiredAnnotationBeanPostProcessor 的内部类

2. InjectionMetadata#inject

筛选出需要注入的元素,则开始进行注入,这里可以看到,inject 的实现很简单,遍历所有元素,调用元素的 inject 方法。不过这里就知道上面针对属性和方法,保存的类型分别是 AutowiredFieldElementAutowiredMethodElement。所以属性调用的是AutowiredFieldElement.inject, 方法调用的是 AutowiredMethodElement.inject

	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			for (InjectedElement element : elementsToIterate) {
				if (logger.isTraceEnabled()) {
					logger.trace("Processing injected element of bean '" + beanName + "': " + element);
				}
				element.inject(target, beanName, pvs);
			}
		}
	}

这里我们需要注意:
findAutowiringMetadata 中添加的注入元素的顺序先添加属性元素,再添加方法元素。
那么在 InjectionMetadata#inject 的遍历中也是先遍历属性元素,再遍历方法元素。那么就可以知道,方法注入的优先级要高于属性注入,因为方法注入在属性注入后,会将属性注入的结果覆盖掉。

比如 :

@RestController
public class DemoController {
    @Autowired
    private DemoService demoService;

    @Autowired
    public void setDemoService(DemoService demoService) {
        this.demoService = new DemoServiceImpl("setter");
    }

    public DemoController(DemoService demoService) {
        this.demoService = new DemoServiceImpl("构造");
    }

最终注入的 DemoService 是 设值注入的 结果。

在这里插入图片描述


跑题了,下面来看看属性注入和方法注入的具体实现

3. AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

AutowiredFieldElement#inject 针对属性的注入实现。具体实现如下:

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			// 如果缓存,从缓存中获取
			if (this.cached) {
				// 判断 如果  cachedFieldValue instanceof DependencyDescriptor。则调用 resolveDependency 方法重新加载。
				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
			}
			else {
				// 否则调用了 resolveDependency 方法。这个在前篇讲过,在 populateBean 方法中按照类型注入的时候就是通过此方法,也就是说明了 @Autowired 和 @Inject默认是 按照类型注入的
				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
				desc.setContainingClass(bean.getClass());
				Set<String> 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) {
					throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
				}
				synchronized (this) {
					// 如果没有缓存,则开始缓存
					if (!this.cached) {
						if (value != null || this.required) {
							// 这里先缓存一下 desc,如果下面 utowiredBeanNames.size() > 1。则在上面从缓存中获取的时候会重新获取。
							this.cachedFieldValue = desc;
							// 注册依赖bean
							registerDependentBeans(beanName, autowiredBeanNames);
							// 如果按照类型只查到一个bean(因为可能存在多个类型相同,name不同的bean),则缓存
							if (autowiredBeanNames.size() == 1) {
								String autowiredBeanName = autowiredBeanNames.iterator().next();
								if (beanFactory.containsBean(autowiredBeanName) &&
										beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
									this.cachedFieldValue = new ShortcutDependencyDescriptor(
											desc, autowiredBeanName, field.getType());
								}
							}
						}
						else {
							this.cachedFieldValue = null;
						}
						this.cached = true;
					}
				}
			}
			if (value != null) {
				// 通过反射,给属性赋值
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

这里需要注意的是:在没有缓存的时候,调用的是 beanFactory.resolveDependency 方法。这个方法在 Spring源码分析四:bean的属性注入 - populateBean 中有过详细解读。简单来说,Spring 通过类型来查找bean就是通过该方法实现的,所以这里说明了 @Autowired@Inject 默认都是按照类型注入的。

4. AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject

AutowiredFieldElement#inject 针对方法的注入实现。具体实现如下:

	protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			// 检测是否可以跳过
			if (checkPropertySkipping(pvs)) {
				return;
			}
			// 获取方法
			Method method = (Method) this.member;
			Object[] arguments;
			// 如果缓存从缓存中虎丘
			if (this.cached) {
				// Shortcut for avoiding synchronization...
				arguments = resolveCachedArguments(beanName);
			}
			else {
				// 获取方法的参数,从Spring 容器中获取(缓存中没有则尝试创建)
				int argumentCount = method.getParameterCount();
				arguments = new Object[argumentCount];
				DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
				Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
				Assert.state(beanFactory != null, "No BeanFactory available");
				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 {
						// 根据类型从容器中获取
						Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
						if (arg == null && !this.required) {
							arguments = null;
							break;
						}
						arguments[i] = arg;
					}
					catch (BeansException ex) {
						throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
					}
				}
				// 进行缓存
				...
			}
			if (arguments != null) {
				try {
					// 通过反射,调用注解标注的方法
					ReflectionUtils.makeAccessible(method);
					method.invoke(bean, arguments);
				}
				catch (InvocationTargetException ex) {
					throw ex.getTargetException();
				}
			}
		}

总结:
AutowiredAnnotationBeanPostProcessor 的属性注入是通过类型注入的,如果是属性,则直接从Spring容器中根据类型获取bean,通过反射赋值。如果是方法,则获取方法的参数列表,从容器中获取对应的参数,获取到后通过反射调用方法。


五、postProcessMergedBeanDefinition

该方法是在.AbstractAutowireCapableBeanFactory#doCreateBean, 创建bean的时候进行的调用了 MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition。如下:
在这里插入图片描述

AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition的实现如下:

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

findAutowiringMetadata(beanName, beanType, null); 在上面我们已经解析过了,可以很明显知道,Spring在这里做了一个缓存,因为postProcessMergedBeanDefinition 的调用时机早于 postProcessProperties & postProcessPropertyValues

InjectionMetadata#checkConfigMembers 这里个人认为是做了一个注入对象的缓存

	public void checkConfigMembers(RootBeanDefinition beanDefinition) {
		Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
		// 遍历待注入的 bean(被封装成了 element )
		for (InjectedElement element : this.injectedElements) {
			// 获取 Member,包含了 bean 信息
			Member member = element.getMember();
			// 如果没被缓存则进行缓存,否则直接跳过
			if (!beanDefinition.isExternallyManagedConfigMember(member)) {
				beanDefinition.registerExternallyManagedConfigMember(member);
				checkedElements.add(element);
				if (logger.isTraceEnabled()) {
					logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
				}
			}
		}
		this.checkedElements = checkedElements;
	}

以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring源码分析衍生篇五:AutowiredAnnotationBeanPostProcessor 的相关文章

  • GBDT 学习

    花絮 最近加班疯掉了 比九九六还要多 不行啊 这个便宜一定要从老板身上占回来 另外不知道朋友们是怎么学习一个新算法的 不过我一般是直接百度很多关于这个算法的博客来看 你会发现有些地方可能相互补充 有的地方可能互相矛盾 这种情况可不少见 总之
  • python是一门面向对象的编程语言_python面向对象(面向对象、面向过程、类、参数self)...

    年轻人 你渴望力量吗 你渴望拥有对象吗 让我们面向对象 重建 家园 吧 一 面向对象的简介 众所周知 python是一门面向对象的编程语言 但是 你知道什么是面向对象吗 在说面向对象之前 我们先来说一说什么是对象 之前的博客有简单涉入 一
  • ANT 安装使用及build.xml文档模板

    一 安装 下载地址 http archive apache org dist ant 0 前提 已经正确设定了JAVA HOME的系统变量 1 直接解压 到D apache ant 1 7 2 系统环境变量中设置 path 加上 D apa
  • chatgpt赋能powershell

    最近chatgpt非常火爆 获得超高曝光度的同时 也让大家对ai和ai工具有了新的认识 关于chatgpt 可以参考这篇文章 今天主要推荐一个可以与powershell集成的ai工具 其后端也是openai的服务 可以有效提高工作效率 Po
  • vue3 element plus NavMenu 导航无法选中(同级页面跳转,导航无法高亮)

    需求 导航选中的是公司信息 当跳转到公司信息编辑的时候 上面的导航公司信息还是要选中状态 点击导航 回到信息展示页面 下面是我的路由 在vue2里面 使用element ui 像下面这样做是可以的 动态给el menu item的index
  • 第十一届蓝桥杯大赛软件类省赛第二场 Java 大学 B试题 B: 寻找 2020 解答

    说明 以下介绍三种方法 三者的区别仅在于文件读取方式不同 方法1直接把 txt文件复制粘贴到console控制台上 使用了Scanner类 方法2和方法3都是从电脑文件夹读取文件 但是具体读取细节有些差别 方法2使用了io接口的三个包 Fi
  • Lua 数据类型 —— 表

    一 简介 表永远是匿名的 表本身和保存表的变量之间没有固定关系 对于一个表而言 当程序不再有变量指向他时 垃圾收集器会最终删除这个表并重用其占用的内存 Lua 不会进行隐藏拷贝或创建新表 操作的都是指向表的指针 二 元素 1 键 表的键可以
  • 2.7 深入理解ContentProvider

    第7章 深入理解ContentProvider 7 1 概述 本章重点分析ContentProvider SQLite Cursor query close函数的实现及ContentResolver openAssetFileDescrip
  • 线程安全(上)

    前言 在多线程中 并不是说知道怎么使用就完事了 学习完如何使用多线程之后 我们了解到多线程的并发性和随机调度的特性是我们程序员不容易控制的 所以一旦操作不当就会带来许多安全问题 那我们就开始学习线程安全吧 目录 1 使用多线程带来的风险 硬
  • Rust交叉编译简述 —— Arm

    使用系统 WSL2 Kali Microsoft Store 命令列表 rustup target list 当前官方支持的构建目标架构列表 rustup target add aarch64 unknown linux gnu 添加目标架
  • Tensorflow错误InvalidArgumentError see above for traceback): No OpKernel was registered to support Op

    调用tensorflow gpu运行错误 错误信息如下 2023 06 21 15 36 14 007389 I tensorflow core platform cpu feature guard cc 141 Your CPU supp
  • 十三、java版 SpringCloud分布式微服务云架构之Java Character 类

    Java Character 类 Character 类用于对单个字符进行操作 Character 类在对象中包装一个基本类型 实例 char ch a Unicode 字符表示形式 char uniChar u039A 字符数组 char
  • vs的运行库 MT MTd MD MDd区别

    MT mutithread Static 多线程库 编译器会从运行时库里面选择多线程静态连接库来解释程序中的代码 即连接LIBCMT lib库 MTd mutithread debug Static 多线程调试版 连接LIBMITD lib
  • 刷脸支付新技术必将带来全新体验

    刷脸支付充满前景 占领支付市场只是时间问题 新技术必将带来新体验 从前的人脸确认登录是证明你是你 而如今的人脸识别是在验证你是谁 刷脸支付的到来 用户连密码都不需要设置了 你自身便是一张行走的活体 秒识别 秒验证让支付生活更加方便快捷 与现
  • android系统的初始化失败怎么办,android初始化失败

    android初始化失败 03 08 16 56 53 235 19683 19711 F libc Fatal signal 6 SIGABRT code 6 in tid 19711 Thread 737 03 08 16 56 53
  • 推荐!国外程序员整理的 C++ 资源大全

    关于 C 框架 库和资源的一些汇总列表 由 fffaraz 发起和维护 内容包括 标准库 Web应用框架 人工智能 数据库 图片处理 机器学习 日志 代码分析等 标准库 C 标准库 包括了STL容器 算法和函数等 C Standard Li
  • 快速部署SpringBoot项目

    快速部署SpringBoot 1 使用宝塔安装 CentOS yum install y wget wget O install sh http download bt cn install install 6 0 sh sh instal
  • 【第十三届蓝桥杯】省赛C/C++ B组 题目+题解

    目录 A 九进制转十进制 B 顺子日期 C 刷题统计 D 修剪灌木 E X 进制减法 F 统计子矩阵 G 积木画 H 扫雷 I 李白打酒加强版 J 砍竹子 A 九进制转十进制 本题总分 5 分 问题描述 九进制正整数 2022 转换成十进制
  • 使用Python接口自动化测试post请求和get请求,获取请求返回值

    目录 引言 请求接口为Post时 传参方法 方法一 通过json dunps body 转化成json格式的字符串 然后传递给data 方法二 在传参时直接格式化成json格式传参 获取接口请求响应数据 request 接口请求时 不对数据

随机推荐

  • github提交失败:error: failed to push some refs to https://github.com/***/git.git

    1 问题 在github远程创建仓库后 利用gitbash进行提交本地文件的时候出现如下错误 报错 error failed to push some refs to https github com git git 2 原因 远程仓库与本
  • AD里面PCB怎样设置原点

    在PCB图中 点击EDIT 选择ORIGIN 点击set 设置原点 如果是元器件封装时 点击EDIT 选择set reference 有三个选项 下面细讲 1 选择pin1 会将原点自动设置在pin1的中心 2 选择 center 会将原点
  • jquery如何获取表单全部数据

    Jquery 如何获取表单的全部数据 用于ajax提交 var formData var t Form serializeArray each t function formData this name this value console
  • [Node] Node.js 包管理工具详解npm yarn cnpm npx pnpm

    Node js专栏 Node js 初级知识 个人简介 一个不甘平庸的平凡人 个人主页 CoderHing的个人主页 格言 路漫漫其修远兮 吾将上下而求索 你的一键三连是我更新的最大动力 目录 1 npm包管理工具 包管理工具npm 常见的
  • 淘宝装修HTML代码大全

    http blog sina com cn s blog 506f1f940100hv9d html 淘宝网店装修HTML代码大全 包括淘宝装修代码 插入图片代码 公告滚动代码 不不一定要懂网站知识 不一定要懂HTML语言 看完这个就可以装
  • 五线谱音名和组别对照表_利用吉他指板快速识记五线谱

    如果你开始读五线谱 最重要的建议就是读五线谱时 忘记六线谱 忘记 忘记 忘记 因为五线谱和六线谱完全是两个不同的概念 虽然六线谱只比五线谱多出一条横线 关键是二者记谱的机理风马牛不相及 五线谱是采用音高记谱 而六线谱是音位记谱 虽然音高和音
  • Linux kernel 关机的底层操作

    Linux关机相关命令如 halt shutdown poweroff和reboot 其实它们底层都是调用名为reboot的system call 其具体实现是在内核目录的kernel sys c中的 SYSCALL DEFINE4 reb
  • 用python怎么输出个人信息,python怎么输出个人信息

    大家好 给大家分享一下python输入自己的姓名 输出hello 某某某同学 很多人还不知道这一点 下面详细解释一下 现在让我们来看看 1 python要求用户输入姓名并输出 name input 请输入姓名 print 你好 name 代
  • 内核hwmon驱动框架详解以及海思芯片温度驱动分析

    1 hwmon驱动框架介绍 hwmon即hardware monitoring framework 硬件监视框架 可以把温度传感器 风扇 电源管理等设备的驱动都利用hwmon框架去实现 在设备的归类上比较容易理解 2 hwmon驱动框架分析
  • 解决ubuntu22.04不能使用todesk问题(显示没有x11桌面)

    这个版本默认使用的是Wayland 所以可能有点问题 x11的适配比较好 也懒得对比了 毕竟todesk好用多了 sudo nano etc gdm custom conf 将WaylandEnable false前面的注释去掉 ctrl
  • Tuning Muxers

    Tuning Muxers WebLogic Server uses software modules called muxers to read incoming requests on the server and incoming r
  • C++ 通讯录管理系统

    include showMenu h using namespace std void showMenu cout lt lt lt lt endl cout lt lt 1 添加联系人 lt lt endl cout lt lt 2 显示
  • Deep Learning Tutorials(一):开头语

    万事开头难 当你开始看这些时候 有可能你已经开始了研究生生活 不在像本科时候过着那种得过且过 考试不挂科的日子 你整天盲目 漫无目的的过日子实际上是在浪费自己的生命 所以坚持每天进步吧 回到正事 你可能开始从事深度学习研究或者有关机器学习方
  • 在PyCharm中查看调用函数的说明文档

    说明文档对函数进行说明解释 帮助更好理解函数的功能 在PyCharm编写代码时 可以通过鼠标悬停 查看调用函数的说明文档 可以查看python内置函数的说明文档 我们也可以给自己创建的函数添加说明文档 辅助理解函数的作用 如下
  • 为什么计算机是32位64位,64位是x86还是x64_为什么64位是X64,32位是X86?

    其它网友回答 windows系统64位的是x64 说明这个系统是可以向下兼容32位程序的64位系统 32位的不是x32而是x86说明该系统是可以向上兼容非32位程序的32位系统 其它网友回答 同意楼上说法 其它网友回答 1 x86的由来 8
  • 时序预测

    时序预测 MATLAB实现时间序列回归之Bootstrapped测试 目录 时序预测 MATLAB实现时间序列回归之Bootstrapped测试 基本介绍 程序设计 学习小结 参考资料 基本介绍 对传统规格测试中尺寸失真的另一种反应是自举
  • idea连接MYSQL报错汇总

    idea连接MYSQL报错汇总 The last packet sent successfully to the server was 0 milliseconds ago The driver has not received any p
  • Face_Recognition 人脸识别函数详解

    加载人脸图片文件 load image file file mode RGB 通过 PIL image open 加载图片文件 mode 有两种模式 RGB 3通道 和 L 单通道 返回 numpy array 查找人脸位置 人脸分割 fa
  • FPGA基础知识19(FPGA异步时钟设计中的同步策略 异步时钟 打两拍 握手机制)

    需求说明 IC设计基础 内容 异步时钟处理方法 来自 时间的诗 原文 https wenku baidu com view 586e8afb7e21af45b307a8b8 html 原文 http ee ofweek com 2011 1
  • Spring源码分析衍生篇五:AutowiredAnnotationBeanPostProcessor

    文章目录 一 前言 二 AutowiredAnnotationBeanPostProcessor 三 determineCandidateConstructors 四 postProcessProperties postProcessPro