Spring源码:bean创建(四)属性注入

Spring源码——bean的加载 前言 内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。 本文主要涉及书中第五章的部分,依照书中内容以及个人理解对Spring进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework 在前几篇文章中,我们已经完成了将XML配置文件的信息转换为 B... 阅读详情

一、populateBean

在创建了bean的实例后,Spring的下一步工作就是为bean注入其依赖的属性:

	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		//传入的beanWrapper为空,如果属性不为空就抛出异常,否则返回null
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		// 应用后处理器InstantiationAwareBeanPostProcessor,调用其postProcessAfterInstantiation方法
		// 在注入属性前修改bean状态,如果方法中发挥false的话,可以终止后面代码运行,直接返回
		boolean continueWithPropertyPopulation = true;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}
		if (!continueWithPropertyPopulation) {
			return;
		}

		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
		//为bean注入PropertyValues中包含的属性
		//这种注入方式适用的是配置文件中通过<property>配置的属性并且显示在配置文件中配置了autowireMode属性
		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// 根据名称自动注入
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// 根据类型自动注入
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
		//如果存在InstantiationAwareBeanPostProcessor后处理器 || 需要进行依赖检查
		//一般情况下,Spring会提前注册几种存在InstantiationAwareBeanPostProcessor
		if (hasInstAwareBpps || needsDepCheck) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						//应用后处理器InstantiationAwareBeanPostProcessor,调用其postProcessPropertyValues方法
						//作用是对需要进行依赖检查的属性进行处理
						//Autowired等注解的属性就是在这里完成注入的
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}
		//注入配置文件中<property>配置的属性
		if (pvs != null) {
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

处理流程:

  1. 应用InstantiationAwareBeanPostProcessor处理器的postProcessAfterInstantiation方法。该方法可以控制是否继续进行属性填充
  2. 获取配置文件中配置的property属性
  3. 应用InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法,完成对注入属性的验证、处理。值得注意的是,AutowiredAnnotationBeanPostProcessor会负责注入标注有@Autowired等注解的属性.
  4. 将配置的<property>属性注入到bean中

二、配置属性的注入

对于通过配置文件中给<bean>配置<property>节点来完成注入的代码实现在applyPropertyValues中:

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		if (pvs == null || pvs.isEmpty()) {
			return;
		}
		MutablePropertyValues mpvs = null;
		List<PropertyValue> original;
		if (System.getSecurityManager() != null) {
			if (bw instanceof BeanWrapperImpl) {
				((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
			}
		}
		//BeanDefinition中定义的PropertyValues的类型就是MutablePropertyValues,所以为true
		if (pvs instanceof MutablePropertyValues) {
			mpvs = (MutablePropertyValues) pvs;
			//属性是否已经转换过类型了
			if (mpvs.isConverted()) {
				// 如果属性已经转换为对应的类型,则直接设置到BeanWrapper中
				try {
					bw.setPropertyValues(mpvs);
					return;
				}
				//catch 
			}
			//获取配置文件中配置的原始值
			original = mpvs.getPropertyValueList();
		}
		else {
			original = Arrays.asList(pvs.getPropertyValues());
		}
		//获取类型转换器,如果没有配置的话为null
		TypeConverter converter = getCustomTypeConverter();
		//如果类型转换器为null,则使用BeanWrapper作为类型转换器,因为BeanWrapper实现了TypeConverter接口
		if (converter == null) {
			converter = bw;
		}
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

		// 遍历属性,并将配置的值转换为对应的类型
		List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			if (pv.isConverted()) {
				deepCopy.add(pv);
			}
			else {
				String propertyName = pv.getName();
				Object originalValue = pv.getValue();
				//解析原始值,例如RuntimeBeanReference类型,会获取BeanFactory中其真正的bean;String或TypedStringValue类型会视为一个表达式进行求值等
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				//是否能进行类型转换 = 可以写入 && 没有'.'或者'[' 符号
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					//将value转换为对应的类型
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}

				//为了避免每次创建bean的时候都重复转换类型,Spring会尽可能地将转换好的值放入对应的BeanDefinition中
				if (resolvedValue == originalValue) {
					//如果原始值并未进行解析
					if (convertible) {
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				}
				else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					//需要进行解析的情况
					pv.setConvertedValue(convertedValue);
					deepCopy.add(pv);
				}
				else {
					//无法被缓存
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}

		// 将解析、转换好的属性设置到BeanWrapper中
		//之所以bean需要BeanWrapper包装一次,也是为了方便给bean填充属性
		try {
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}
	}

最终是通过BeanWrapper的setPropertyValues来完成属性的注入,而前面的工作主要是解析配置文件配置的原始值,因为原始值可能是最终注入的值(String类型),也可能需要被解析后才能被应用,所以Spring花费大量工作来解析原始值。解析完成后,如果有必要还得转换值的类型,最终将解析、转换好的值注入到bean中。

	public void setPropertyValues(PropertyValues pvs) throws BeansException {
		setPropertyValues(pvs, false, false);
	}

	public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {
		//保存注入时抛出的PropertyAccessException
		List<PropertyAccessException> propertyAccessExceptions = null;
		//获取需要注入的propertyValue列表
		List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
				((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
		for (PropertyValue pv : propertyValues) {
			try {
				// 注入属性
				setPropertyValue(pv);
			}
			catch (NotWritablePropertyException|NullValueInNestedPathException ex) {
				if (!ignoreUnknown) {
					throw ex;
				}
			} catch (PropertyAccessException ex) {
				if (propertyAccessExceptions == null) {
					propertyAccessExceptions = new LinkedList<PropertyAccessException>();
				}
				propertyAccessExceptions.add(ex);
			}
		}

		// 如果注入时抛出的PropertyAccessException不为空,Spring会收集起来,在这里统一抛出
		if (propertyAccessExceptions != null) {
			PropertyAccessException[] paeArray =
					propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
			throw new PropertyBatchUpdateException(paeArray);
		}
	}

在上面的代码中主要是进行统一的异常处理和遍历属性列表,真正的属性注入还需要继续向下调用设置单个属性的方法setPropertyValue:

	public void setPropertyValue(PropertyValue pv) throws BeansException {
		setPropertyValue(pv.getName(), pv.getValue());
	}
	
	public void setPropertyValue(String propertyName, Object value) throws BeansException {
		AbstractNestablePropertyAccessor nestedPa;
		try {
			//将属性访问的工作交给AbstractNestablePropertyAccessor完成
			//当配置的属性名propertyName中包含'.'或者'['这样字符时,代表需要设置嵌套属性
			//如果存在嵌套属性,Spring会递归向下获取最终设置的属性,比如:a.b.c,Spring会递归调用获取到b,c是需要设置的属性
			//如果没有嵌套属性的话。会返回BeanWrapper自身
			nestedPa = getPropertyAccessorForPropertyPath(propertyName);
		}
		catch (NotReadablePropertyException ex) {
			throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
					"Nested property in path '" + propertyName + "' does not exist", ex);
		}
		PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
		//设置属性
		nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
	}

上面主要是解析propertyName,处理嵌套的属性,确定最终需要设置的属性

	protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
		//如果propertyName中存在'['的情况,即存在数组下标
		if (tokens.keys != null) {
			processKeyedProperty(tokens, pv);
		}
		else {
			//非数组、容器类型的属性设置
			processLocalProperty(tokens, pv);
		}
	}

属性设置分为了数组类型设置和非数组类型设置,我们分析更为普遍的非数组类型:

	private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
		//属性设置交由PropertyHandler完成
        //Spring会使用反射获取属性的getter和setter方法
		PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
		//如果不存在属性或者属性不是可写的
		if (ph == null || !ph.isWritable()) {
			//如果属性是Optional类型,则直接返回
			if (pv.isOptional()) {
				//日志,略
				return;
			} else {
				//其他情况会抛出异常
				throw createNotWritablePropertyException(tokens.canonicalName);
			}
		}

		Object oldValue = null;
		try {
			//获取属性值
			Object originalValue = pv.getValue();
			Object valueToApply = originalValue;
			//需要转换值的类型
			if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
				//如果已经完成了类型转换,直接使用就可以了
				if (pv.isConverted()) {
					valueToApply = pv.getConvertedValue();
				}
				else {
					//如果需要读取旧值(默认为false) && 值是可读的
					if (isExtractOldValueForEditor() && ph.isReadable()) {
						try {
							oldValue = ph.getValue();
						}
						catch (Exception ex) {
							//无法读取旧值
						}
					}
					//类型装换
					valueToApply = convertForProperty(
							tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
				}
				pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
			}
			//使用PropertyHandler完成属性的注入
			ph.setValue(this.wrappedObject, valueToApply);
		}
		//catch略
	}

下面就是注入属性的代码:

		public void setValue(final Object object, Object valueToApply) throws Exception {
			//获取解析好的setter方法
			final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
					((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
					this.pd.getWriteMethod());
			//设置Method的访问权限
			if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							writeMethod.setAccessible(true);
							return null;
						}
					});
				}
				else {
					writeMethod.setAccessible(true);
				}
			}
			//调用setterMethod的invoke方法,通过反射,完成属性的注入
			final Object value = valueToApply;
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
						@Override
						public Object run() throws Exception {
							writeMethod.invoke(object, value);
							return null;
						}
					}, acc);
				}
				catch (PrivilegedActionException ex) {
					throw ex.getException();
				}
			}
			else {
				writeMethod.invoke(getWrappedInstance(), value);
			}
		}
	}

至此,配置文件中的peoperty已经注入到了bean中

三、Autowired注解的属性注入

在属性填充populateBean代码中,除了调用applyPropertyValues注入配置文件中配置的属性,在它前面有一段代码会应用 InstantiationAwareBeanPostProcessor后处理器的postProcessPropertyValues方法:

		if (hasInstAwareBpps || needsDepCheck) {
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						//应用InstantiationAwareBeanPostProcessor后处理器
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						//返回值为null时会属性的填充,直接返回
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}
		//配置文件的属性注入
		applyPropertyValues(beanName, mbd, bw, pvs);

在InstantiationAwareBeanPostProcessor的子类中,就包含了注解属性注入的实现方法,InstantiationAwareBeanPostProcessor的子类有:

重点需要关注的有以下几个实现类:

  • RequiredAnnotationBeanPostProcessor:检查属性的setter方法上是否标注有@Required注解,如果带有此注解,但是未配置属性(配置文件中的<property>元素),那么就会抛出异常
  • AutowiredAnnotationBeanPostProcessor:遍历bean的Field属性,寻找标注有@Autowired和@Value注解的字段,并完成属性注入(如果能加载到javax.inject.Inject注解的话,也会扫描该注解)
  • CommonAnnotationBeanPostProcessor:遍历bean的字段,寻找通用的依赖(javax中规定的)注解,完成属性注入。通用注解包括有:javax.annotation.Resource,javax.xml.ws.WebServiceRef,javax.ejb.EJB。

重点讲解AutowiredAnnotationBeanPostProcessor的实现:

	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
		//遍历获取带有自动注入注解的字段和方法,并且将字段信息、注解信息
		//注解包括:@Value、@Autowired、@Inject(javax.inject.Inject,如果能加载到这个类的话)
		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;
	}
	
	

上面代码的步骤为:

  1. 寻找带有@Value、@Autowired、@Inject(javax.inject.Inject,如果能加载到这个类的话)注解的字段或方法,并且将字段或方法、bean名、bean类型封装起来
  2. 完成属性注入

1、寻找注解标记的属性

寻找注解过的属性在findAutowiringMetadata中实现:

	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
		// 先尝试从缓存中查找,cacheKey为beanName或类名
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		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);
					}
					try {
						//解析后放入缓存
						metadata = buildAutowiringMetadata(clazz);
						this.injectionMetadataCache.put(cacheKey, metadata);
					}
					//catch略
				}
			}
		}
		return metadata;
	}

因为对类的解析需要大量用到反射,所以为了尽力提高性能,Spring会将解析好的字段、方法信息保存到缓存中,上面的代码也在主要是对缓存进行管理,具体的解析工作在buildAutowiringMetadata方法中完成:

	private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		//存放解析好的属性信息
		LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
		Class<?> targetClass = clazz;

		//Spring会一直递归向上遍历bean的父类
		do {
			//存放当前解析的类中带有注解的属性
			final LinkedList<InjectionMetadata.InjectedElement> currElements =
					new LinkedList<InjectionMetadata.InjectedElement>();

			//doWithLocalFields方法中,其实就是获取class的所有字段,对每个字段调用FieldCallback方法
			//
			ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
				@Override
				public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
					AnnotationAttributes ann = findAutowiredAnnotation(field);
					if (ann != null) {
						if (Modifier.isStatic(field.getModifiers())) {
							return;
						}
						//如果注解中存在required属性,获取属性的值,并保存起来
						//如果不存在required属性则默认为true
						boolean required = determineRequiredStatus(ann);
						//使用AutowiredFieldElement类型,将字段信息封装起来
						currElements.add(new AutowiredFieldElement(field, required));
					}
				}
			});
			//与Field逻辑相同,都是遍历所有声明的方法,判断是否带有特定的注解
			//如果存在注解标注的方法,并使用AutowiredMethodElement封装起来
			ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
				@Override
				public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
					if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
						return;
					}
					AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
					if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
						if (Modifier.isStatic(method.getModifiers())) {
							//log...
							return;
						}
						if (method.getParameterTypes().length == 0) {
							//log...
						}
						boolean required = determineRequiredStatus(ann);
						//与field不同的一点就是,AutowiredMethodElement会获取属性描述符,一起封装
						PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
						currElements.add(new AutowiredMethodElement(method, required, pd));
					}
				}
			});

			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		//Spring会一直递归向上遍历bean的父类
		while (targetClass != null && targetClass != Object.class);
		//将封装有注入属性的列表一起封装到InjectionMetadata中
		return new InjectionMetadata(clazz, elements);
	}

上面代码中,Spring先从当前类开始解析其字段和方法,并且会一直递归向上解析父类,把解析到的字段或方法信息封装到AutowiredFieldElement或AutowiredMethodElement中。

2、属性注入

回顾下先前的代码:

显示查找带有特定注解的属性,已经完成了,下一步就是属性注入,inject:

	public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> elementsToIterate =
				(this.checkedElements != null ? this.checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			for (InjectedElement element : elementsToIterate) {
				element.inject(target, beanName, pvs);
			}
		}
	}

代码较短,主要内容就是遍历先前解析好的AutowiredFieldElement或AutowiredMethodElement,调用其inject方法:

		//AutowiredFieldElement
		protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
			//AutowiredFieldElement中保存的是Field对象,如果是AutowiredMethodElement则会保存Method对象
			Field field = (Field) this.member;
			//最终需要注入的值
			Object value;
			//如果已经被缓存过,就可以从缓存中获取
			if (this.cached) {
				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
			}
			else {
				//使用DependencyDescriptor封装Field
				//其属性包括:field:Field对象,declaringClass:Field所在类,fieldName:字段名
				//required:是否允许为空,eager:是否懒加载(注解标注的字段都为false)
				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
				desc.setContainingClass(bean.getClass());
				Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
				TypeConverter typeConverter = beanFactory.getTypeConverter();
				try {
					//通过BeanFactory根据DependencyDescriptor信息解决依赖
					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;
							//在BeanFactory中注册依赖关系
							registerDependentBeans(beanName, autowiredBeanNames);
							//如果与属性类型相同的注入bean只有一个,那么就可以设置到缓存中
							if (autowiredBeanNames.size() == 1) {
								String autowiredBeanName = autowiredBeanNames.iterator().next();
								if (beanFactory.containsBean(autowiredBeanName)) {
									if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
										this.cachedFieldValue = new ShortcutDependencyDescriptor(
												desc, autowiredBeanName, field.getType());
									}
								}
							}
						}
						else {
							this.cachedFieldValue = null;
						}
						this.cached = true;
					}
				}
			}
			//通过Field对象的反射,将值设置到bean中
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

上面代码步骤为:

  1. 先尝试从缓存中获取属性值
  2. 如果没有被缓存,则通过beanFactory.resolveDependency方法,从BeanFactory中获取属性值
  3. 如果属性值满足条件(符合注入条件的bean只有一个、BeanFactory包含该bean、与属性类型相同),就缓存起来
  4. 通过反射,将属性值设置到bean中

beanFactory.resolveDependenc会将BeanFactory中,所有与传入参数的类型匹配的bean找出来,有autowiredBeanName保存这些匹配的beanName

 

 

 

Spring手撸源码系列-给Bean对象注入属性 上一章是给Bean对象注入构造参数,这一章就来实现给Bean对象注入属性,一般来说类有构造方法也会有全局变量的属性,那么我们在创造的时候就需要注入进来,不能不管,这样才是一个完整的创建对象。 对于属性的创建除了基本类型也会有引用类型,引用其他的Bean对象等等都要考虑。 1.工程目录 标红的是新添加的 2.UML类图 为什么要添加这些类?马上介绍,首先目前加的三个类都是关于属性方面的,涉及的以前类的更改也是根据需要填充属性而更改的。 PropertyValue: 定义属性类,这个. 阅读详情

相关推荐

第 05 章:一鸣惊人,为Bean对象注入属性和依赖Bean的功能实现

作者:小傅哥 博客:https://bugstack.cn 沉淀、分享、成长,让自己和他人都能有所收获!???? 《Spring 手撸专栏》目录 第 1 章:开篇介绍,我要带你撸 Spring 啦! 第 2 章:小试牛刀,实现一个简单的Bean容器 第 3 章:初显身手,运用设计模式,实现 Bean 的定义、注册、获取 第 4 章:崭露头角,基于Cglib实现含构造函数的类实例化策略 第 5 章:一鸣惊人,为Bean对象注入属性和依赖Bean的功能实现 第 6 章:待归档… 一、前言 超

bugstack虫洞栈 3688

spring源码学习 applyPropertyValues()方法

applyPropertyValues()方法也并不是非常的复杂,他的主线逻辑就是,将pvs中的pv对象中的属性值,尽可能进行转换以后,注入到bean中。如果转入的pvs对象是MutablePropertyValues类型的,那么将其转变为mpvs对象,如果mpvs对象已经转换过,直接注入就可以。如果mpvs对象没经过转换,或者pvs对象不是MutablePropertyValues类型的,那么就要遍历pvs属性列表了。

m0_51571683的博客 1441

语音AI Agent落地实战:从ASR-NLP-TTS到多通道协同决策

语音AI Agent并非简单的自动语音识别(ASR)、自然语言处理(NLP)和文本转语音(TTS)三模块串联,而是在真实场景中需兼顾实时性、鲁棒性与隐私合规的端到端交互系统。其核心原理在于打破‘文本中间态’依赖,通过声学-文本-上下文多通道协同决策,融合原始音频特征、N-best置信度与设备环境信号,实现动态意图推演。技术价值体现在低延迟(如120ms离线唤醒)、高抗噪(-5dB SNR下WER≤25%)及强可控性(断网降级、本地热更新)。典型应用场景覆盖车载语音、智能家居与银行网点等边缘受限环境,尤其适用

weixin_29065959的博客 326

Spring官网阅读(十六)Spring中的数据绑定

在前面的文章我们学习过了Spring中的类型转换以及格式化,对于这两个功能一个很重要的应用场景就是应用于我们在XML中配置的Bean的属性值上,如下: <bean class="com.dmz.official.converter.service.IndexService" name="indexService"> <property name="name" value="...

做一个认真的程序员 4539

GBT 27930-2015/2023国标充电协议A类CAN报文ID说明

国标充电CAN的ID,仅供学习参考。

qqq080的博客 7801

Cesium进阶学习四、shadertoy(着色器)

Cesium进阶学习着色器

程迷学习的博客 4321

Spring的Bean实例化、属性注入、对象注入、复杂注入(基于xml配置方式)

Bean实例化的三种方式:(通过配置文件创建对象)(1)使用类的无参构造创建(最常使用)(2)使用静态工厂创建(3)使用实例工厂创建代码实例:(1)项目结构:(2)导入spring的核心jar包依赖:(3)applicationContext.xml的配置:&lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:co...

张维鹏的博客 2324

深入解析mSATA与mini-PCIE接口的硬件设计要点

本文深入解析mSATA与mini-PCIE接口的硬件设计要点,涵盖信号定义、电源设计、高速信号处理及PCB布局布线等关键内容。通过实战案例和调试技巧,帮助工程师规避常见设计陷阱,提升电路设计效率,特别适合工业控制和嵌入式系统开发场景。

weixin_28741413的博客 317

spring复习:(18)给bean的属性赋值

其中applyPropertyValues(beanName, mbd, bw, pvs);

amadeus_liu2的博客 822

北航机器学习2020-2021秋季学期期末试题回忆

北航机器学习2020-2021秋季学习期末试题回忆 fanjn, zhangyy, yuanq, wanggq, weixy, anh, wangyy, zhouxy, likc1 【注:考试使用纯白的A3答题纸,考试时间2h,各位同学可以自行模拟考试】 此试卷1-8题为10分,第9题为20分 1.2077年人类发射近地卫星,一共发射800颗,根据大小和轨道远近,有着不同的宜居条件(+表示宜居),有以下表格(数字八九不离十,其他属性什么的原题就是交替的) 数量 大小 轨道 是否宜居 30

weixin_45262065的博客 7610

信捷 XDH Ethercat 正负极限配置详解与实战技巧

本文详细解析了信捷XDH系列PLC通过EtherCAT总线配置伺服电机正负硬极限的完整流程与实战技巧。内容涵盖硬件开关选型安装、EtherCAT IO信号映射、软件参数设置(包括关键的极性配置)、调试验证方法,以及硬极限与软极限的协同策略。重点阐述了如何避免常见配置错误,确保运动控制系统的最后一道物理安全防线可靠有效,为工程师提供从理论到实践的全面指导。

wdx012345的博客 601

Spring源码--IOC容器实现(6)--Bean依赖注入

前言 Github:https://github.com/yihonglei/thinking-in-spring 在前面文章中分析了容器初始化过程,建立一个可以使用的容器以及Bean对象的创建过程。 1)BeanDefinition的Resource定位 2)BeanDefinition的载入和解析 ...

qq_26729155的博客 233

S4 Hana SD -信贷管理 - 02

在S4版本中,客户的信用管理数据在事务代码BP维护,业务伙伴角色是UKM000。在维护过程中,我们并不能直接输入信贷控制范围这个组织架构,而是需要输入一个信用段,并且在信用段的维度下去维护相应信用管理数据。对于信用段,可以分为主信用段(信用段是0000)和其他临时信用段,我们可以通过配置把次级信用段的客户信贷负债汇总到主信用段。当然,在后台也有配置,会把信用段和信贷控制范围这两者进行关联.3.2SPROIMG > 财务供应链管理 > 信用管理 > 信贷风险监视 > 主数据 > 创建信用段。

weixin_46525734的博客 1965

Spring 源码系列(6): Bean 的诞生:从 getBean 到 doCreateBean 全链路

文章摘要 Spring的getBean()方法背后隐藏着复杂的Bean创建流程。核心链路为doGetBean → getSingleton → createBean → doCreateBean,其中doCreateBean采用"实例化→提前暴露→属性填充→初始化"的四步策略。该流程首先检查缓存,未命中时通过getSingleton模板确保单例唯一性,并在doCreateBean中完成对象构建。循环依赖的解决关键在于提前暴露机制——将未完成初始化的Bean引用存入三级缓存。属性填充阶段处理依赖注入,初始化阶

不才不才不不才的博客 337

kubeadm init 运行时kebelet启动失败问题

解决k8s kubeadm init时遇到的kubelet问题

ZDPZN的博客 6074

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Spring源码解析(四):单例bean的创建流程

接下来说下Spring源码解析(三):bean容器的刷新中最重要的实例化剩余的非懒加载bean进入beanFactory.preInstantiateSingletons();方法} // 创建beanDefinitionNames的副本beanNames用于后续的遍历,以允许init等方法注册新的bean定义 List < String > beanNames = new ArrayList < >(this . beanDefinitionNames);

从基础到源码解析的学习记录 1259

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Spring源码分析七:bean的属性注入⑤ - populateBean

文章目录一、前言二、 一、前言 本文是我打算系统阅读Spring 源码的第一篇章,从中也创建了一些衍生文章,衍生文章的意义是因为自己在看源码的过程中,部分知识点并不了解或者对某些知识点产生了兴趣,所以为了更好的阅读源码。 这篇文章应该是接着 Spring源码分析二:单例bean的获取 - createBean 的继续分析过程。 本文主要是分析Spring 具体创建bean的过程。 本文的分析代码在 AbstractAutowireCapableBeanFactory#doCreateBean 方法中 二

猫吻鱼的博客 4622

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【Spring源码分析 1】使用Maven配置Spring步骤,Spring的Bean创建以及属性注入的源码分析

目录 1. Maven项目配置Spring 1.1 pom.xml中导入spring、日志和测试的依赖包 1.2 定义实体类 1.3 定义spring的配置文件applicationContext.xml(bean标签属性和子标签解释) 2. 测试代码 3. 源码解析 3.1解析bean,生成beanDefinition,注册到beanFactory 3.1.1 解析bean工作的关键实现 3.1.2 注册beanDefinition工作的关键实现 3.2 读取beanFactory..

Elevenoo的博客 1294

Spring源码(四):bean的创建

本节紧接上一篇,深入讨论一个标准bean在Spring中究竟是如何生成&销毁的(重点关注Spring对bean的构造方法、注入字段、各种扩展方法的处理,以及围绕整个bean的全生命周期,Spring留了那些扩展点给用户实现)

好看的HK 1202

Spring源码剖析-Bean的实例化-属性注入

前言 这一章节我们来讨论创建Bean过程中的属性注入,在Spring的IOC容器启动过程中,会把定义的Bean封装成BeanDefinition注册到一个ConcurrentHashMap中,然后对单利的lazy-init=false 的Bean并进行实例化。创建Bean的代码在 AbstractAutowireCapableBeanFactory#doCreateBean 中,当Bean创建成功之后,会调用AbstractAutowireCapableBeanFactory#populateBean 方

墨家巨子@俏如来 1427

Spring源码分析:多个bean同时满足注入条件时,spring的选择

通过Spring源码分析,spring是如何解决依赖注入问题,并且在找到满足条件的bean时,如果需要注入的接口有多个实现类,spring会如何解决

finalcola的博客 9374

手撕源码之Spring中Bean的创建

本文结合源码,讲解了Spring中Bean的创建流程,配合流程图,让读者能更好理解Spring的Ioc容器的本质。

weixin_45749194的博客 881

Spring中Bean注入源码分析

该实现类是一个抽象类,即该类是bean的工厂类,其中主要实现的是可以复用的具体逻辑,该抽象类中包含获取bean、beanPostProcessor、初始化bean、销毁bean等对bean的操作抽象类。//获取bean的方式平时使用ApplicationContext就是最终调用该方法@Override@Override/*** 从容器中获取bean的主要逻辑*/else {else {//省略部分代码try {//先获取beanDefinition。

weixin_74412978的博客 913

Spring源码系列:Bean的加载

Spring源码系列:Bean的加载前言一. Bean的加载 前言 在我们前面两章,我们讲完了Spring如何加载、解析资源文件以及如何将其转化为BeanDefinition的(标签的解析)流程。那么接下来我们来看下Bean的一个加载过程。我们来从这段代码为切入口: public static void main(String[] args) { ClassPathResource resource = new ClassPathResource("test.xml"); DefaultListable

Zong_0915的博客 2058

Spring源码讲解(四)Spring中bean的属性注入

由于Spring源码过于庞大,文章中不会列出细节,须要大家花时间下去研究哦 获取要注入的属性的源信息 当BeanFactory(AbstractAutowireCapableBeanFactory)调用doCreateBean方法实例化完一个对象之后,会调用第三次后置处理器: 可以看到第三次后置处理器是找寻实现了MergedBeanDefinitionPostProcessor接口的后置处理器执行其postProcessMergedBeanDefinition方法,Spring中关键的两个实现

一只蜗牛的博客 1137

spring源码:bean加载之创建bean-属性填充

一、介绍   提前曝光bean的Object-Factory是spring为了解决bean循环依赖的一种方法。 什么是循环依赖 spring加载一个bean需要两个步骤:1.创建bean;2.给这个bean填充属性 如上图,加载类A的步骤为: 先创建A,然后给A填充属性的时候,发现A依赖一个类B属性,所以去加载B; 创建B,然后给B填充属性的时候,发现B依赖一个类C属性,所以去加载C; 创...

咔咔学长 503
上一篇: Spring源码:bean创建(三):createBeanInstance
下一篇: Spring源码:bean创建(五)解决依赖
finalcola
博客等级 码龄9年 37粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值