我是靠谱客的博主 等待水池,最近开发中收集的这篇文章主要介绍spring中Bean的生命周期之属性填充(1),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

概述

本文主要介绍生命周期中属性填充,涉及到spring 的自动注入,@Autowired注解,以及属性填充相关后置处理器的实现
Bean的生命周期再笔者《spring中bean的生命周期总览》一文中已有介绍,这里不再过多介绍

属性填充

直接进入属性填充的相关方法 :populateBean(beanName, mbd, instanceWrapper)

	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				return;
			}
		}
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;				
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);

					if (pvsToUse == null) {
						if (filteredPds == null) {
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return;
						}
					}
					pvs = pvsToUse;
				}
			}
		}
		if (needsDepCheck) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}
		if (pvs != null) {
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

在这个方法里面有三个参数
String beanName : bean的name
RootBeanDefinition mbd : 合并后的RootBeanDefintion
BeanWrapper bw : 实例化后的Bean的包装类

这个方法比较长,可以分为三个模块
模块一: 提供了一个扩展点,再这个扩展点,可以通过继承BeanPostProcessor对BeanDefinition进行操作
模块二:ByName和ByType自动注入的实现
模块三:注解的自动注入的实现

模块一

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}

这里可以算是spring提供的一个扩展点,在这里可以通过继承InstantiationAwareBeanPostProcessor这个后置处理器对BeanDefinition进行修改

但是这不免有个疑问,进入这个方法的时候,spring已经完成了类的加载已经相关bean的实例化,那么修改BeanDefinition又有什么意义?
首先按照笔者的理解,spring的Bean和java的Bean是不一样的,一个完整的spring的bean,笔者认为是需要经过一个完整的spring的bean的生命周期的,所以笔者认为这个阶段的Bean不是一个springBean,或者说是一个不完整的springBean,而再后序阶段的生命周期中也的的确确使用到了BeanDefinition里面的信息,例如自动注入,例如生命周期回调,所以这里对BeanDefintion的修改一定是有意义的

模块二

		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

这里会对byType 和 byName的自动注入模型进行处理,那么@Autowired是在这里处理的么?
@Autowired并不是在这里进行处理的,这里一般是处理xml配置的自动注入,这里不免更有疑问了,同样是自动注入,为什么注解的自动配置不是在这里进行配置的呢?
spring的发展是有一个过程的,我们都知道在以往的spring中,都是采用xml配置的方法,而注解功能是后来加入到spring中的,对于以往的xml配置而言,在进行对xml解析的时候,就会将标签中的autowire 这个属性封装到BeanDefinition中,但是对于注解而言,是没有autowire这个属性的,所以根本不会进入到这个判断中。同时注解的自动注入支持更加强大的功能,例如@Autowired会先byType再byName,所以对于注解的自动注入需要有单独的一套现实

还是回到当前的代码,来看看对于这一块的自动注入,spring是如何实现的
1)在当前创建的BeanDefintion中去获取PropertyValues 这个数据结构,这个里面存储的是我们当前生成Bean中的属性以及对应的值,也
就是说,如果我们使用xml配置手动的设置了注入的属性,那么在对xml解析的时候,就会存放在这个数据结构中,如果没有手动配置,
那么当前这个属性对应的就是空,这里的自动注入,是先将要注入的值找到放入PropertyValues这个数据结构中,然后统一的对当前真正
创建的Bean进行赋值
2) 判断具体的自动注入模型是 byName 还是 byType,然后调用对应的方法

	protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
		// 找到有对应set方法的属性
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			if (containsBean(propertyName)) {
				// 根据属性名去找bean,这就是byName
				Object bean = getBean(propertyName);
				// 给属性赋值
				pvs.add(propertyName, bean);
				registerDependentBean(propertyName, beanName);
				...
			}
			else {
				...
			}
		}
	}

1))通过unsatisfiedNonSimpleProperties 方法 找到所有的set方法所对应的名字,例如setAAA,那么找到的名字就是AAA
这里也就解释了为什么set方法的方法名要与待填充的属性名匹配

protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
		Set<String> result = new TreeSet<>();
		PropertyValues pvs = mbd.getPropertyValues(); // 在BeanDefinition中添加的属性和值
		PropertyDescriptor[] pds = bw.getPropertyDescriptors();
		for (PropertyDescriptor pd : pds) {
			if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
					!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
				result.add(pd.getName());
			}
		}

		return StringUtils.toStringArray(result);
	}

在这个方法里面又出现了一个新的数据结构,PropertyDescriptor翻译过来就是属性的描述,对于这个数据结构,其实如果一个类里面一个属性也没有也可能会生成一个属性的描述,因为这个属性的描述不是去找属性,而是找set方法,例如setAAA,那么找到的属性描述就是AAA这个类了,会将set方法看成一个对象
在这个数据结构里面又有五个重要的属性:
beanClass:这个属性所属的class
readMethod:存储的是对应的get方法
writeMethod:存储的是对应的set方法
writeMethodParameter:保存着writeMethod(也就是set方法的)参数和参数的类型
name:set方法所对应的名字,例如setAAA,解析出来的名字就是aAA
如果没有提供对应的set/get方法,那么这里就会是null
最后进过一些校验,将这些方法的描述的名字转为数组然后返回

2))然后遍历这些名字,在遍历的时候会进行一个判断 containsBean(propertyName)
进入这个判断里面

	public boolean containsBean(String name) {
		String beanName = transformedBeanName(name);
		if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
			return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
		}
		// Not found -> check parent.
		BeanFactory parentBeanFactory = getParentBeanFactory();
		return (parentBeanFactory != null && parentBeanFactory.containsBean(originalBeanName(name)));
	}

containsSingleton(beanName) || containsBeanDefinition(beanName)
这两个判断的意思是,判断当前单例池里面是否有这个Bean,或者当前的BeanDefinitionMap中是否有这个Bean对应的BeanDefinition
为什么要执行这个判断?
例如AAA里面有一个属性BBB,当我们对AAA进行属性填充的时候,BBB不一定是已经完成了实例化,所以这里是确保BBB这个Bean一定是可以出现在spring容器中的

3))接下来一个属性的方法就出现了-----getBean ,接着上面的AAA和BBB的例子,如果说单例池里面有bean,那么就会直接从单例池里面拿,如果说单例池里面没有那就会去创建这个bean,然后将其加入PropertyValues 这个数据结构中

以上就是byName的逻辑,接下来看看byType的逻辑

	protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}

		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);

		// 找到有对应set方法的属性
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			try {
				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
				if (Object.class != pd.getPropertyType()) {
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);

					// 根据类型找bean,这就是byType
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
					if (autowiredArgument != null) {
						pvs.add(propertyName, autowiredArgument);
					}
					for (String autowiredBeanName : autowiredBeanNames) {
						registerDependentBean(autowiredBeanName, beanName);
						...
					}
					autowiredBeanNames.clear();
				}
			}
			catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
			}
		}
	}
  1. byType同样是先unsatisfiedNonSimpleProperties找到set方法所对应的名字

  2. 将获取到的set方法的名字遍历,然后获得其PropertyDescriptor 属性的描述符

  3. 然后根据方法的描述信息生成了另外一个Descriptor叫做DependencyDescriptor
    这个数据结构翻译为中文就是依赖描述,也就是依赖的描述,这里指的依赖其实可以认为是set方法的参数类型
    也就是对set方法参数的类型进行一些描述
    首先清除一个概念,在java中所谓的依赖,例如类AAA中有BBB属性,则说明AAA依赖BBB,
    但是对于spring而言判定依赖关系,不是基于是否有这个属性,而是基于set方法
    对于byType的自动注入而言 ,spring关注的是set方法的参数类型
    对于byName的自动注入而言,spring关注的是set方法后面的字符串的(开头字母小写)

    并且这个依赖描述DependencyDescriptor 接收的是其子类 AutowireByTypeDependencyDescriptor
    根据名字也能判断出来,这个依赖描述,是基于byType来描述bean之间的依赖关系的

  4. 根据新生成的描述信息找要注入的Bean
    这里首先会先根据前面传过来的依赖描述DependencyDescriptor 先获取类型,然后在spring容器里面去找Bean
    这根据类型找bean可能会找到多个bean,然后就会在这多个 bean 里面进行筛选,先会筛选出标记了@Primary的类
    如果没有再会去通过byName找,但是我们知道,这里的依赖注入是处理xml配置的,如果xml只配置了byType,这里是不允许byName进
    行查找的。
    所以对于AutowireByTypeDependencyDescriptor这个子类而言,重写了其父类的getDependencyName方法,使其永远都返回null,从而
    阻止了byName

  5. 将找到的Bean对象加入到 PropertyValues

模块三

提到这部分内容,就不得不先提到MergedBeanDefinitionPostProcessor这个后置处理器

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
	...
	// 原始对象
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {...}
				mbd.postProcessed = true;
			}
		}
		......
}

再doCreateBean这个方法中可以看到,MergedBeanDefinitionPostProcessor这个后置处理器的执行是再bean的实例化之后的
在applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) 里会调用postProcessMergedBeanDefinition
在这里会有一个后置处理器与@Autowired注解相关,就是AutowiredAnnotationBeanPostProcessor,这个类继承自
MergedBeanDefinitionPostProcessor,所以这里会调用AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) 
	{
		// 获取beanType中的注入点
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

先找到当前创建的Bean的注入点,注入点就是需要注入的点,例如加了@Autowired的字段就是一个注入点,InjectionMetadata 是一个集合,因为一个类可能会依赖多个其他的类
点入InjectionMetadata 这个对象里

public abstract static class InjectedElement {
		protected final Member member;
		protected final boolean isField;
		@Nullable
		protected final PropertyDescriptor pd;
		@Nullable
		protected volatile Boolean skip;
		...

可以发现每一个注入点的类型是InjectedElement,里面还有之前介绍过的PropertyDescriptor (属性描述)
Member是一个接口,是java反射包下的类,其中有两个关键的子类:一个是Method,另一个是Field,这两个类用来存储有关于方法和字段的信息

	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.
		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 = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}

先去缓存里面找,如果缓存里面没有就会调用buildAutowiringMetadata
在这个方法里面有一个非常关键的do-while
在循环里则回去找到相关注解:
1)获取到当前类里面的所有的字段
2)判断当前这个字段上有没有标有@Autowired ,@Value,@Inject这三个注解的任意一个,如果有,则表示可以注入
这三个注解是在构造AutowiredAnnotationBeanPostProcessor这个对象时添加的

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.
		}
	}

3)判断当前的属性是不是静态的,spring不支持静态属性的注入
4)判断当前的注入点 (required == ture) ,是不是必须的,如果是必须的在后续的注入是一定要被赋值的,不然会抛出异常,如果不是必须的,则不会报错
5)new 一个AutowiredFieldElement添加到集合中,(AutowiredFieldElement 是 InjectedElement的子类)
6)对于当前类的注入点寻找完后,如果有父类还会去找父类的注入点
以上所说的都是对属性注入的查找
在这个do-while中还会对方法的注入进行查找,过程类似于对属性的查找,
只是在最后添加集合的时候不是使用AutowiredFieldElement,而是使用AutowiredMethodElement
(静态方法也不支持注入)

现在回到模块三的主体内容
这一块主要的做的事情就是调用AutowiredAnnotationBeanPostProcessor的postProcessProperties

	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		// InjectionMetadata中保存了所有被@Autowired注解标注的属性/方法并封装成一个个的InjectedElement
		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;
	}

可以看到在这个方法里面也调用了findAutowiringMetadata去寻找注入点,但是前面我们已经找过了注入点了,所以这里的findAutowiringMetadata可以直接从缓存里面拿到之前找到的注入点,然后调用 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可能是Method,也可能是Field
				element.inject(target, beanName, pvs);
			}
		}
	}

然后就遍历这个bean中所有的注入点进行注入,在 进行注入的时候可能是属性的注入,也可能是方法的注入
属性的注入

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<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) {
							this.cachedFieldValue = desc;
							registerDependentBeans(beanName, autowiredBeanNames);
							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);
			}
		}
	}

1)根据当前的字段生成一个依赖描述
2)对这个依赖描述的对象进行解析得到一个需要注入的对象
3)最后通过反射将这个对象进行注入到字段里面

方法注入

				...
				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 {
						// 寻找bean
						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);
					}
				}
				...

相比属性注入最大的不同就是,对于方法的注入免会遍历方法的每一个参数,然后进行注入

最后

以上就是等待水池为你收集整理的spring中Bean的生命周期之属性填充(1)的全部内容,希望文章能够帮你解决spring中Bean的生命周期之属性填充(1)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部