Spring源码分析(九)Bean实例化的后置处理

news2024/11/14 19:59:05

目录

    • 1.1 后置处理
      • 1.1.1 调用父类方法
      • 1.1.2 获取元数据
      • 1.1.3 注解元信息检查
      • 1.1.4 Member、InjectedElement、InjectionMetadata

  • 官网:Home
  • 参考书籍:Spring源码深度解析-郝佳编著-微信读书

上一节我们详细的解释了Bean的创建过程,工厂,构造器等等,但是Bean创建后的处理我们还没有详细讲解,下面我们来看看Bean在创建之后做了那些处理
AbstractAutowireCapableBeanFactory

   protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器
        BeanWrapper instanceWrapper = null;
        // 单例模型,则从未完成的 FactoryBean 缓存中删除
        if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }

        // 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }

        // 包装的实例对象
        final Object bean = instanceWrapper.getWrappedInstance();
        // 包装的实例对象的类型
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // 检测是否有后置处理
        // 如果有后置处理,则允许后置处理修改 BeanDefinition
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    // applyMergedBeanDefinitionPostProcessors
                    // 后置处理修改 BeanDefinition
                    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 是否正在被创建
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            // 提前将创建的 bean 实例加入到ectFactory 中
            // 这里是为了后期避免循环依赖
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        /*
         * 开始初始化 bean 实例对象
         */
        Object exposedObject = bean;
        try {
            // 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性
            // 则会递归初始依赖 bean
            populateBean(beanName, mbd, instanceWrapper);
            // 调用初始化方法
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

        /**
         * 循环依赖处理
         */
        if (earlySingletonExposure) {
            // 获取 earlySingletonReference
            Object earlySingletonReference = getSingleton(beanName, false);
            // 只有在存在循环依赖的情况下,earlySingletonReference 才不会为空
            if (earlySingletonReference != null) {
                // 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                // 处理依赖
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                        StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                        "] in its raw version as part of a circular reference, but has eventually been " +
                                        "wrapped. This means that said other beans do not use the final version of the " +
                                        "bean. This is often the result of over-eager type matching - consider using " +
                                        "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }
        try {
            // 注册 bean
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

我们来总结一下上面函数的大体步骤:

  • 如果是单例则需要首先清除缓存
  • 实例化bean,将BeanDefinition转换为BeanWrapper
  • MergedBeanDefinitionPostProcessor的应用
  • 依赖处理
  • 属性填充,将所有属性填充至bean的实例中
  • 循环依赖检查
  • 注册DisposableBean
  • 完成创建并返回

1.1 后置处理

     // 检测是否有后置处理
        // 如果有后置处理,则允许后置处理修改 BeanDefinition
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    // applyMergedBeanDefinitionPostProcessors
                    // 后置处理修改 BeanDefinition
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

让我们走进这个方法看看呗,将 MergedBeanDefinitionPostProcessor 应用于指定的 Bean 定义,调用其 postProcessMergedBeanDefinition 方
AbstractAutowireCapableBeanFactory

	protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
		// 循环遍历
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof MergedBeanDefinitionPostProcessor) {
				MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
				bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
			}
		}
	}

我们来看看关键方法postProcessMergedBeanDefinition,但是我们可以发现它其实是一个接口方法,有很多实现类
image.png
image.png
从源码中可以看到,接口MergedBeanDefinitionPostProcessor中定义了两个方法:

  • postProcessMergedBeanDefinition()
  • resetBeanDefinition()
  • postProcessMergedBeanDefinition()方法是Spring用于找出所有需要注入的字段并同时做缓存的
  • resetBeanDefinition()方法是在BeanDefinition被修改后清除容器的缓存
  • 根据代码可以看到, Spring其实就是调用了所有类型为MergedBeanDefinitionPostProcessor后置处理器的 postProcessMergedBeanDefinition方法, 在没有手动的添加bean的后置处理器的条件下,
  • 实现了该方法的后置处理器有以下几个:
  • ApplicationListenerDetector
  • ScheduledAnnotationBeanPostProcessor
  • RequiredAnnotationBeanPostProcessor
  • InitDestroyAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • AutowiredAnnotationBeanPostProcessor

由于这里的实现类很多,我们就以其中一个为案例进行源码分析:CommonAnnotationBeanPostProcessor

1.1.1 调用父类方法

CommonAnnotationBeanPostProcessor

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 调用父类方法
		super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
        // 调用方法获取生命周期元数据
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        // 验证相关方法
		metadata.checkConfigMembers(beanDefinition);
	}

那我们首先来看看父类方法的调用
InitDestroyAnnotationBeanPostProcessor


	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		// 调用方法获取生命周期元数据
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        // 验证相关方法
		metadata.checkConfigMembers(beanDefinition);
	}
  1. 调用方法获取生命周期元数据并保存
  2. 验证相关方法

我们来看看Spring是如何调用方法获取生命周期元数据并保存的,当然就是下面的方法findLifecycleMetadata方法
InitDestroyAnnotationBeanPostProcessor

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
		if (this.lifecycleMetadataCache == null) {
			// Happens after deserialization, during destruction...
            // 销毁过程中,反序列调用
			return buildLifecycleMetadata(clazz);
		}
        // 首先尝试从缓存中获取数据
		// Quick check on the concurrent map first, with minimal locking.
		LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
		if (metadata == null) {
            // 从缓存中获取数据失败,尝试再次加锁,再次获取数据
			synchronized (this.lifecycleMetadataCache) {
				metadata = this.lifecycleMetadataCache.get(clazz);
				if (metadata == null) {
                    // 构建生命周期
					metadata = buildLifecycleMetadata(clazz);
                    // 将构建好的生命周期放在缓存中
					this.lifecycleMetadataCache.put(clazz, metadata);
				}
				return metadata;
			}
		}
		return metadata;
	}
  • 在bean销毁过程中,首先尝试从缓存中获取元数据,如果从缓存中获取失败则尝试加锁创建元数据
  • 加锁后,再次获取元数据,防止多线程重复执行,构建生命周期元数据,将构建好的元数据放入缓存中

下面我们再来看看一个关键方法构建生命周期数据buildLifecycleMetadata方法
InitDestroyAnnotationBeanPostProcessor

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
		// 分别实例化回调方法@PostConstruct,@PreDestroy
    	List<LifecycleElement> initMethods = new ArrayList<>();
    
		List<LifecycleElement> destroyMethods = new ArrayList<>();
        // 目标类
		Class<?> targetClass = clazz;

		do {
            // 保存每一轮循环的方法
            // 初始化方法
			final List<LifecycleElement> currInitMethods = new ArrayList<>();
            // 销毁方法
			final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
            // 利用反射获取当前类的所有方法
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                // 注解类型:@PostConstruct
				if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                    // 封装成LifecycleElement
					LifecycleElement element = new LifecycleElement(method);
                    // 添加到集合中
					currInitMethods.add(element);
					if (logger.isTraceEnabled()) {
						logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
					}
				}
                // 注解类型:@PreDestroy
				if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
                    // 封装成LifecycleElement添加到集合中
					currDestroyMethods.add(new LifecycleElement(method));
					if (logger.isTraceEnabled()) {
						logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
					}
				}
			});
        	// 将此次循环的获取到的方法存放在总集合中
			initMethods.addAll(0, currInitMethods);
            // 将此次循环的获取到的方法存放在总集合中
			destroyMethods.addAll(currDestroyMethods);
            // 目标类
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
    	// 返回一个LifecycleMetadata
		return new LifecycleMetadata(clazz, initMethods, destroyMethods);
	}

构建生命周期元数据(解析带@PostConstruct和@PreDestroy注解的方法),当构建完成后会封装成LifecycleMetadata对象,将元数据缓存到lifecycleMetadataCache集合中并返回,此时就完成✅相关方法(初始化方法和销毁方法)的扫描解析和缓存工作
示例中并未显式调用被@PostContruct注解的init()方法和被@PreDestroy注解修饰的destroy()方法,那么问题来了:这些被添加进回调方法列表中的方法是在哪里调用呢执行的?@PostContruct注解的init()方法和被@PreDestroy注解修饰的destroy()方法是在哪里调用执行的呢?,让我们回到我们刚开始的源码部分,开始实例化Bean
AbstractAutowireCapableBeanFactory

 /*
         * 开始初始化 bean 实例对象
         */
        Object exposedObject = bean;
        try {
            // 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性
            // 则会递归初始依赖 bean
            populateBean(beanName, mbd, instanceWrapper);
            // 调用初始化方法
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

当然是实例化Bean的时候
AbstractAutowireCapableBeanFactory

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		// 注释 4.12 securityManage 是啥,不确定=-=
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// 如果没有 securityManage,方法里面校验了 bean 的类型,需要引用 Aware 接口
			// 对特殊的 bean 处理:Aware/ BeanClassLoader / BeanFactoryAware
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// 熟悉么,后处理器又来了
			// 注释 7.3 bean 实例化阶段,调用已经注册好的 beanPostProcessor 的 postProcessBeforeInitialization 方法
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// 激活用户自定义的 init-method 方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// bean 实例化阶段,调用已经注册好的 beanPostProcessor 的 postProcessAfterInitialization 方法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

下面会到我们的主题,当我们从生命周期中有了元数据,接下里就是如何获取了,下面就验证方法
InitDestroyAnnotationBeanPostProcessor


	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		// 调用方法获取生命周期元数据
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        // 验证相关方法
		metadata.checkConfigMembers(beanDefinition);
	}

InitDestroyAnnotationBeanPostProcessor
将初始化和销毁的回调方法注册到beanDefinition中,并且标记已经检查过的方法,放入checkedInitMethods中的checkedInitMethods/checkedDestroyMethods集合中

/**
		 * 检查配置。将初始化与销毁的回调方法放在beanDefinition中
		 * @param beanDefinition
		 */
		public void checkConfigMembers(RootBeanDefinition beanDefinition) {
			Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
			for (LifecycleElement element : this.initMethods) {
				String methodIdentifier = element.getIdentifier();
				if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
					// 初始化方法
					beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
					checkedInitMethods.add(element);
					if (logger.isTraceEnabled()) {
						logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + element);
					}
				}
			}
			Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
			for (LifecycleElement element : this.destroyMethods) {
				String methodIdentifier = element.getIdentifier();
				if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
					// 销毁方法
					beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
					checkedDestroyMethods.add(element);
					if (logger.isTraceEnabled()) {
						logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + element);
					}
				}
			}
			this.checkedInitMethods = checkedInitMethods;
			this.checkedDestroyMethods = checkedDestroyMethods;
		}

导致父类的初始化方法已经分析完毕,下面我们来到子类的分析
CommonAnnotationBeanPostProcessor

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 调用父类方法
		super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
        // 调用方法获取生命周期元数据
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        // 验证相关方法
		metadata.checkConfigMembers(beanDefinition);
	}

那我们看看调用方法获取生命周期元数据

1.1.2 获取元数据

	/**
	 * 获取元数据
	 * @param beanName
	 * @param clazz
	 * @param pvs
	 * @return
	 */
	private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
		// Fall back to class name as cache key, for backwards compatibility with custom callers.
		// 缓存key
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		// Quick check on the concurrent map first, with minimal locking.
		// 从缓存中获取元数据
		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);
					}
					// 创建元数据
					metadata = buildResourceMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}

Spring会先从缓存injectionMetadataCache中获取当前bean对应得注入元数据, 如果needsRefresh返回了true, 那么Spring就会调用buildAutowiringMetadata方法开始构建注入元数据, 构建完成后就会将其放入 到缓存injectionMetadataCache中
InjectionMetadata

	public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class<?> clazz) {
		return (metadata == null || metadata.targetClass != clazz);
	}

  • needsRefresh的判断很简单, 即metadata == null || metadata.targetClass != clazz
  • 当metadata不存在于缓存的时候肯定是要进行构建的, 由于findAutowiringMetadata方法会在属性注入的时 候也被调用, 所以通常情况下会拿到缓存中的数据

buildAutowiringMetadata

	/**
	 * 构建元数据
	 * @param clazz
	 * @return
	 */
	private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
		// 如果不是候选类,则返回空
		if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}
		// 构建元数据
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			// 当前元素
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			// 遍历字段
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				// 如果是 @WebServiceRef 注解字段
				if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
					if (Modifier.isStatic(field.getModifiers())) {
						throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
					}
					currElements.add(new WebServiceRefElement(field, field, null));
				}
				// 如果是@EJB 注解字段
				else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
					if (Modifier.isStatic(field.getModifiers())) {
						throw new IllegalStateException("@EJB annotation is not supported on static fields");
					}
					currElements.add(new EjbRefElement(field, field, null));
				}
				// 如果是@Resource注解字段
				else if (field.isAnnotationPresent(Resource.class)) {
					if (Modifier.isStatic(field.getModifiers())) {
						throw new IllegalStateException("@Resource annotation is not supported on static fields");
					}
					if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
						currElements.add(new ResourceElement(field, field, null));
					}
				}
			});

			// 遍历方法
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				//
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				
				if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					// 如果是 @WebServiceRef 注解字段
					if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
						if (Modifier.isStatic(method.getModifiers())) {
							throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
						}
						if (method.getParameterCount() != 1) {
							throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
						}
						PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
						currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
					}
					// 如果是@EJB 注解字段
					else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
						if (Modifier.isStatic(method.getModifiers())) {
							throw new IllegalStateException("@EJB annotation is not supported on static methods");
						}
						if (method.getParameterCount() != 1) {
							throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
						}
						PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
						currElements.add(new EjbRefElement(method, bridgedMethod, pd));
					}
					// 如果是@Resource注解字段
					else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
						if (Modifier.isStatic(method.getModifiers())) {
							throw new IllegalStateException("@Resource annotation is not supported on static methods");
						}
						// 获取参数类型
						Class<?>[] paramTypes = method.getParameterTypes();
						if (paramTypes.length != 1) {
							throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
						}
						if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
							// 获取属性描述符
							PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
							currElements.add(new ResourceElement(method, bridgedMethod, pd));
						}
					}
				}
			});
			// 添加到元素集合中
			elements.addAll(0, currElements);
			// 获取父类
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
		// 构建元数据
		return InjectionMetadata.forElements(elements, clazz);
	}

  • 可以看到, Spring会创建一个elements的list来存放所有需要被注入的数据, 然后在while循环中, 开始取该targetClass中的元数据, 获取完成后放入到elements中, 之后开始获取targetClass的父类中素有需要被注入的数据, 直到Object为止
  • 当一个类及其所有的祖先类中的元数据被扫描完成后, Spring就会将其放入到InjectionMetadata中返回, 接下来我们开始分析Spring在这个while循环是如何获取一个个的InjectedElement的,首先是字段元数据的获取
ReflectionUtils.doWithLocalFields(targetClass, field -> {
   // 省略 @WebServiceRef 和 @EJB 相关的处理逻辑
   else if (field.isAnnotationPresent(Resource.class)) {
      if (Modifier.isStatic(field.getModifiers())) {
         throw new IllegalStateException("@Resource annotation is not supported on static fields");
      }
      if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
         currElements.add(new ResourceElement(field, field, null));
      }
   }
});

根首先是对当前类型所有属性的判断,再次要对属性判断三个条件。

  1. 属性需要被@Resource注解标记。
  2. 属性不能是静态的。
  3. 属性类型不在ignoredResourceTypes集合中

符合条件的属性信息会被封装成 ResourceElement 对象并添加到currElements集合中。
下面是对方法的获取

ReflectionUtils.doWithLocalMethods(targetClass, method -> {
    // 找到我们定义的方法
   Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
   if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
      return;
   }
   if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
      // 省略 @WebServiceRef 和 @EJB 相关的处理逻辑
      else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
         if (Modifier.isStatic(method.getModifiers())) {
            throw new IllegalStateException("@Resource annotation is not supported on static methods");
         }
         Class<?>[] paramTypes = method.getParameterTypes();
         if (paramTypes.length != 1) {
            throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
         }
         if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
            PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
            currElements.add(new ResourceElement(method, bridgedMethod, pd));
         }
      }
   }
});

对于当前类型中的每一个方法,首先要确保是代码中定义的方法而非编译器生成的方法,然后是对方法信息的判断条件。

  1. 方法被@Resource注解标记。
  2. 方法不是静态的。
  3. 方法的必须有且只有一个参数。
  4. 方法的参数类型名称不在ignoredResourceTypes集合中。

符合条件的方法信息,也会被封装成一个 ResourceElement 对象,并添加到currElements集合中。
至此,buildResourceMetadata方法就结束了。回到postProcessMergedBeanDefinition方法中,我们看最后一行代码。

1.1.3 注解元信息检查

CommonAnnotationBeanPostProcessor

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 调用父类方法
		super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
        // 调用方法获取生命周期元数据
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        // 验证相关方法
		metadata.checkConfigMembers(beanDefinition);
	}

InjectionMetadata

InjectionMetadatapublic void checkConfigMembers(RootBeanDefinition beanDefinition) {
   Set<InjectedElement> 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.isTraceEnabled()) {
            logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
         }
      }
   }
   this.checkedElements = checkedElements;
}

逻辑比较清晰,遍历注入元信息中的所有成员,也就是之前处理过的属性和方法信息,对于没有注册为 BeanDefinition 的外部管理配置成员的元素进行注册,并添加到元信息的checkedElements集合中。这一步主要是为了防止重复处理通过注解注入的元素。
:::info

  • applyMergedBeanDefinitionPostProcessors方法通过调用一个个的MergedBeanDefinitionPostProcessor中的postProcessMergedBeanDefinition方法完成了对被@Autowired等注解标注的方法、属性的处理, 将其变为一个个的InjectionMetadata, 最终放入到该后置处理器中的injectionMetadataCache缓存中, 之后便可以通过该后置处理器取得这些注入元数据, 进而完成属性的注入, 这里Spring也通过策略模式, 对不同类型的元数据利用不同的后置处理器进行处理
    :::

1.1.4 Member、InjectedElement、InjectionMetadata

  • 在Java反射中, 我们会经常的遇到Field、Method、Constructor类, 而本次我们提到的第一个类就是Member
  • 该类就是上面几个类的父类, Spring为了能够使得获取到的方法、属性都放在一个地方, 采用了接口编程, 将其

都变成了Member类型

  • 当Spring扫描到一个方法加了@Autowired的时候, 就会将该方法反射获得到Method变为一个Member, 然后将其放到InjectedElement中, 换句话说, InjectedElement就是对一个方法或者属性的一个封装, 除了有Member存储原始的反射信息外, 还会有额外的信息, 比如required属性, 表示是否是必须注入的

一个类中可能会有很多个方法、属性被标注了@Autowired注解, 那么每一个被标注的方法、属性都用一个
InjectedElement表示, 而所有这些InjectedElement均被放入到一个Collections中, 这个集合则存在于
InjectionMetadata中, 即InjectionMetadata中的Collection injectedElements存储
了所有需要被注入的信息, 里面有一个targetClass属性则是存储了这些方法、属性所在的类Class对象

public class InjectionMetadata {
	private final Class<?> targetClass;

	private final Collection<InjectedElement> injectedElements;

  private volatile Set<InjectedElement> checkedElements;
}

可以看到, 在InjectionMetadata中还有一个checkedElements, 里面也是存储了InjectedElement, 之前提到
injectedElements的时候, 有人可能会认为, 难道Spring在后面进行属性填充的时候, 就是取injectedElements
中的一个个InjectedElement进行反射操作进行注入的吗, 其实不是的, Spring实际取的是checkedElements中
的InjectedElement, 在MergedBeanDefinitionPostProcessor中postProcessMergedBeanDefinition方法中, Spring主要是找到所有被@PostConstruct、@PreDestory、@Autowired、@Resource、@Value标注的属性或者方法, 将其封装成一个个的InjectedElement, 最后放到一个新创建的InjectedMetada中, 完成这些工作后, Spring又会经过一些判断, 最终将这些InjectedElement从injectedElements取出来放到checkedElements中, 在进行属性填充的时候, Spring就会取出一个个的InjectedElement, 通过反射的方完成属性填充
那么上述提到的三个后置处理器有什么作用呢, 其实这是一个策略模式的典型应用, Spring对@PostConstruct、@PreDestory注解的处理(转为InjectedElement)用的InitDestroyAnnotationBeanPostProcessor,
而对@Resource的处理用的CommonAnnotationBeanPostProcessor,
对于@Autowired以及@Value的处理则是用的AutowiredAnnotationBeanPostProcessor,
不同的后置处理器处理不同的注解, 下面我们以@Autowired注解
的处理为例子进行讲解, 其它注解的处理的代码跟这个是类似的, 就不再进行展开了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/958996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV(二):认识Mat容器

目录 1.什么是Mat类 2.Mat类能存储的数据 整数类型&#xff08;有符号和无符号&#xff09;&#xff1a; 浮点数类型&#xff1a; 布尔类型&#xff1a; 3.Mat类的创建 1.利用矩阵宽、高和类型参数创建Mat类 2.利用矩陈Size(结构和数据类型参数创建Mat类 3.利用已有Mat…

三十七个常见Vue面试题,背就完事了四

二十八、Vue.use是干什么的&#xff1f;Vue.use是用来使用插件的。我们可以在插件中扩展全局组件、指令、原型方法等。 会调用install方法将Vue的构建函数默认传入&#xff0c;在插件中可以使用vue&#xff0c;无需依赖vue库 二十九、组件写name有啥好处&#xff1f; 增加name…

恒运资本:什么叫融资通俗的讲是什么意思?

融资浅显的讲是什么意思&#xff1f; 近年来&#xff0c;伴随着互联网金融的鼓起&#xff0c;融资这个词越来越为人们所熟知。但是&#xff0c;关于融资这个词的意思&#xff0c;很多人也许只是有一个含糊的概念&#xff0c;无法做到准确而深入的了解。本文将从多个角度剖析&a…

VB:百元买百鸡问题

VB&#xff1a;百元买百鸡问题 Private Sub Command1_Click()ClsRem 百元买百鸡问题Print "公鸡", "母鸡", "小鸡"For x 0 To 20For y 0 To 33z 100 - x - yIf z Mod 3 0 ThenIf 5 * x 3 * y z / 3 100 ThenPrint x, y, zEnd IfEnd IfNe…

亚运会即将召开,数字孪生体育场能够发挥什么作用?

亚运会&#xff0c;作为亚洲地区最重要的综合性运动盛会之一&#xff0c;将吸引数以千计的运动员、教练员和观众汇聚一堂&#xff0c;共同见证运动的魅力。在如此大规模的体育盛会中&#xff0c;数字孪生技术能够为运动场馆的规划、运营和观众带来前所未有的体验。 数字孪生技术…

ESP-C3入门22. 基于VSCODE使用内置JTAG调试程序

ESP-C3入门22. 基于VSCODE使用内置JTAG调试程序 一、简介1. 内置 jtag 介绍2. OpenOCD3. 准备工作 二、操作步骤1. 接线2. 在VSCode设置端口等信息3. 测试编译烧录 三、调试程序 一、简介 1. 内置 jtag 介绍 在ESP32中&#xff0c;内置了一个用于JTAG调试的特殊程序&#xff…

【实验八】555定时器的应用

【实验内容】 【实验报告】 【实验截图】 1 2 3 【得分】

(c++)类和对象 上篇

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 5.类的作用域 6.类的实例化 7.类的对象大小的计算 8.类成员函数的this指针 1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步…

C++ for 循环

for 循环允许您编写一个执行特定次数的循环的重复控制结构。 语法 C 中 for 循环的语法&#xff1a; for ( init; condition; increment ) {statement(s); }下面是 for 循环的控制流&#xff1a; init 会首先被执行&#xff0c;且只会执行一次。这一步允许您声明并初始化任…

模糊测试面面观 | 模糊测试是如何发现异常情况的?

协议模糊测试是一种用于评估通信协议、文件格式和API实现系统安全性和稳定性的关键技术。在模糊测试过程中&#xff0c;监视器扮演着关键角色&#xff0c;它们能够捕获异常情况、错误响应、资源利用等&#xff0c;为测试人员提供有价值的信息&#xff0c;有助于发现潜在漏洞和问…

STM32-串口通信(串口的接收和发送)

文章目录 STM32的串口通信一、STM32里的串口通信二、串口的发送和接收串口发送串口接收 三、串口在STM32中的配置四、串口接收的两种实现方式1. 需要更改的地方2. 查询RXNE标志位3. 使用中断 总结 STM32的串口通信 本文在于记录自己的学习过程中遇到的问题和总结&#xff0c;各…

LeetCode——栈的压入、弹出序列

这里我用下面的例子子来讲解一下模拟栈的实现。 例子1&#xff1a;pushed [1,2,3,4,5] popped [4,5,3,2,1] 思路&#xff1a;第一步&#xff1a;我们先创建一个栈&#xff0c;然后将pushed的数据压进去 第二步&#xff1a;判断&#xff01; 当压入栈的数据和popped第一个数据…

【实验六】组合逻辑电路的设计

【实验内容】 【实验报告】

向openssl中添加一个最简单的算法

文章目录 一、尝试在sha.c中添加新的函数二、添加自定义算法2.1 添加对应文件2.2 相关配置2.3 编译运行 一、尝试在sha.c中添加新的函数 在尝试添加新算法前&#xff0c;我先尝试在原有的旧算法中添加一个新函数&#xff0c;看是否能被编译并生成对应的动态链接库。 关于open…

unity 跨屏显示

1.代码 /*Type:设置分辨率*/ using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.Runtime.InteropServices;public class ScreenManager : MonoBehaviour {[HideInInspector]//导入设置窗口函数 [DllImport("…

nginx压缩ttf文件 mine.types的作用

最近在运维过程中&#xff0c;前端提到发现在linux上下载某ttl文件&#xff08;字体文件&#xff09;太大&#xff0c;传输过程比较慢&#xff0c;于是就想着使用nginx的gzip进行压缩&#xff0c;经过不断尝试&#xff0c;终于发现在nginx的配置目录/etc/nginx/mine.types 文件…

Windows中安装nvm进行Node版本控制与详细使用教程

1.nvm介绍 nvm英文全程也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和npm都是node.js版本管理工具&#xff0c;但是为了解决node各种不同之间版本存在不兼容的问题&#xff0c;因此可以通过nvm安装和切换不同版本的node。 2.nvm下载 可在点此…

springboot网上家乡特产商城系统 微信小程序

基于特产商城小程序的设计基于现有的智能手机上运行&#xff0c;可以实现管理员服务端&#xff1b;首页、个人中心、用户管理、商品分类管理、商品信息管理、系统管理、订单管理等功能。方便用户微信端&#xff1b;首页、商品信息、商城公告、购物车、我的 等详细的了解及统计分…

JavaScript Web APIs-01学习

复习&#xff1a; splice() 方法用于添加或删除数组中的元素。 **注意&#xff1a;**这种方法会改变原始数组。 删除数组&#xff1a; splice(起始位置&#xff0c; 删除的个数) 比如&#xff1a;1 let arr [red, green, blue] arr.splice(1,1) // 删除green元素 consol…

Ubuntu 22.04.2 LTS 安装python3.6后报错No module named ‘ufw‘

查明原因&#xff1a; vim /usr/sbin/ufw 初步判断是python版本的问题。 # 查看python3软链接 ll /usr/bin/python3 将python3的软链接从python3.6换成之前的3.10&#xff0c;根据自己电脑情况。 可以查看下 /usr/bin 下有什么 我这是python3.10 所以解决办法是 # 移除py…