Spring 依赖注入解析

Spring 依赖注入解析

依赖注入分类

手动注入

在 Spring 中,手动注入方式分为两种,一种是 set 方法注入,一种是构造方法注入。

  • set 方法注入
<bean name="userService" class="com.lingxi.service.UserService">
	<property name="orderService" ref="orderService"/>
</bean>
  • 构造方法注入
<bean name="userService" class="com.lingxi.service.UserService">
	<constructor-arg index="0" ref="orderService"/>
</bean>

自动注入

  • 基于 XML 的autowire自动注入
    在使用 xml 配置 bean 时,可以在标签中指定 autowire 属性,例如:
	<!--  指定autowire属性后,Spring会对该该bean的所有属性进行赋值,(需要有对应属性的set方法)赋值的方式为byName  -->
	<!--  autowire可选值为:  
				1. byType
				2. byName
				3. constructor
				4. default
				5. no
	-->
    <bean id="userService" class="com.lingxi.service.UserService" autowire="byName"/>
  1. byName:解析 setXXX 方法,获取 XXX 属性名;根据获取到的名称,去容器中获取 bean。
  2. byType:解析 set 方法中的参数类型,去容器中获取 bean,若获取到多个,则抛出异常
  3. constructor:通过构造方法,首先根据类型去容器中获取 bean,若获取到多个,则根据名称进行进一步确定
  4. no:不进行自动注入。
  5. default:表示使用默认的自动装配方式。默认值根据上下文的不同而有所区别,一般情况下为 autowire=“no”。
  • 基于@Autowired注解的自动注入
    @Autowired注解可以用在属性、set 方法、构造方法上。该注解其实是 byType 和 byName 的结合,首先通过类型去找去寻找 Bean;若查询到多个,则根据名称查询。
  1. 属性上:先根据属性类型去找 Bean,如果找到多个再根据属性名确定一个
  2. 构造方法上:先根据方法参数类型去找 Bean,如果找到多个再根据参数名确定一个
  3. set方法上:先根据方法参数类型去找 Bean,如果找到多个再根据参数名确定一个

两种自动注入的区别:
XML 中的 autowire 控制的是整个 bean 的所有属性,而@Autowired注解是直接写在某个属性、某个 set 方法、某个构造方法上的。

依赖注入原理分析

该图为 Spring 实例化 Bean 的流程:
在这里插入图片描述

寻找注入点

  1. 首先会执行 postProcessMergedBeanDefinition() 方法,去寻找注入点。

注入点:被@Autowired注解标记了的属性或方法。

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		// 寻找注入点,并将注入点缓存
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		// 检查 Spring Bean 的配置成员合法,如是否存在循环依赖、构造方法参数类型、数量是否正确
		metadata.checkConfigMembers(beanDefinition);
	}
  1. 通过 findAutowiringMetadata() 方法,去寻找注入点。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		
		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);
					}
					// 解析注入点并缓存
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}
  1. 解析注入点具体方法
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		// 如果一个Bean的类型是String...,那么则根本不需要进行依赖注入
		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<>();

			// 遍历targetClass中的所有Field,即处理属性注入
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				// field上是否存在@Autowired、@Value、@Inject中的其中一个
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
					// static filed不是注入点,不会进行自动注入
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}

					// 构造注入点
					// 处理注解中的 required 属性
					// 该属性默认为true:在容器中查询不到该bean,则报错;若改为false,容器中不存在该bean,则跳过
					boolean required = determineRequiredStatus(ann);
					// 将查询到的注入点,放到缓存宗
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});

			// 遍历targetClass中的所有Method
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				
				// 处理 桥接方法:是一种为了实现某些Java语言特性而由编译器自动生成的方法。
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				// method上是否存在@Autowired、@Value、@Inject中的其中一个
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					// static method不是注入点,不会进行自动注入
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					// 处理set方法没有入参的方法
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
						}
					}
					// 处理required属性
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					// 添加缓存
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});
			// 将字段注入点缓存、方法注入点缓存放入一起
			// 列表插入放到首位原因:
			// 		整个do...while循环是会遍历其父类,每次都放到首位,在初始化时,可以现初始化父类,在初始化子类
			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
		
		return InjectionMetadata.forElements(elements, clazz);
	}

流程总结:
在这里插入图片描述

为什么被 static 修饰的属性或方法,不被标记为注入点?

假如 Spring 会对 static 修饰的属性和方法标记为注入点:

/**
	定义两个 Bean,这两个 Bean 都是原型
*/
@Component
@Scope("prototype")
public class OrderService {

}

@Component
@Scope("prototype")
public class UserService  {

	@Autowired
	private static OrderService orderService;
	
}

从 Spring 容器中,获取两个 userService 对象:

// 由于UserService和OrderService都是原型,每次获取都会重新创建对象
// 因此userService1和userService2时两个不同的对象,其中的orderServce也是不同的对象
// 假设userService1的orderService对象为orderService1,userService2的orderService对象为orderService2

UserService userService1 = context.getBean("userService")
UserService userService2 = context.getBean("userService")

// 由于被static修饰的属性和变量是属于类的,执行完以上两行代码后,userSerice1的orderSerivce为orderService2,并不是orderService1,则出现了歧义。

注入点注入

注入点注入主要方法为 postProcessProperties()

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		// 找注入点(所有被@Autowired注解了的Field或Method)
		// 即重新执行寻找注入点方法,该方法有缓存,可以直接从缓存读取
		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;
	}


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) {
				element.inject(target, beanName, pvs);
			}
		}
	}

其中的inject()方法,用于执行依赖注入,对于字段注入点和方法注入点,具有不同的实现。流程基本相似,首先去解析 Field 或 Method,获取参数类型、名称等,通过resolveDependency()方法,在容器中获取 bean 对象,使用反射,对 bean 进行赋值。

  • 字段注入点
    1. 遍历所有的 AutowiredFieldElement 对象。
    2. 将对应的字段封装为 DependencyDescriptor 对象。
    3. 调用 BeanFactory 的resolveDependency()方法,传入 DependencyDescriptor 对象,进行依赖查找,找到当前字段所匹配的 Bean 对象。
    4. 将 DependencyDescriptor 对象和所找到的结果对象 beanName 封装成一个 ShortcutDependencyDescriptor 对象作为缓存,比如如果当前 Bean 是原型 Bean,那么下次再来创建该 Bean 时,就可以直接拿缓存的结果对象 beanName 去 BeanFactory 中去那 bean 对象了,不用再次进行查找了
    5. 利用反射将结果对象赋值给字段。
  • 方法注入点
    1. 遍历所有的 AutowiredMethodElement 对象
    2. 遍历将对应的方法的参数,将每个参数封装成 MethodParameter 对象
    3. 将 MethodParameter 对象封装为 DependencyDescriptor 对象
    4. 调用 BeanFactory 的resolveDependency()方法,传入 DependencyDescriptor 对象,进行依赖查找,找到当前方法参数所匹配的 Bean 对象。
    5. 将 DependencyDescriptor 对象和所找到的结果对象 beanName 封装成一个 ShortcutDependencyDescriptor 对象作为缓存,比如如果当前 Bean 是原型 Bean ,那么下次再来创建该 Bean 时,就可以直接拿缓存的结果对象 beanName 去 BeanFactory 中去那 bean 对象了,不用再次进行查找了
    6. 利用反射将找到的所有结果对象传给当前方法,并执行。

resolveDependency() 方法解析

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
		// 用来获取方法入参名字的
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

		// 所需要的类型是Optional
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		// 所需要的的类型是ObjectFactory,或ObjectProvider
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}
		else {
			// 在属性或set方法上使用了@Lazy注解,那么则构造一个代理对象并返回,真正使用该代理对象时才进行类型筛选Bean
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);

			if (result == null) {
				// descriptor表示某个属性或某个set方法
				// requestingBeanName表示正在进行依赖注入的Bean
				// 该方法作用是解析依赖项。是核心方法
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			// 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				return shortcut;
			}

			Class<?> type = descriptor.getDependencyType();
			// 获取@Value所指定的值
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			if (value != null) {
				if (value instanceof String) {
					// 占位符填充(${})
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ?
							getMergedBeanDefinition(beanName) : null);
					// 解析Spring表达式(#{}),
					// 例如:@Value(#{userService}) 会去容器中寻找name为userService的bean
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				// 将value转化为descriptor所对应的类型
				// 调用类型转化器,如将String类型转为UserService类型
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					// A custom TypeConverter which does not support TypeDescriptor resolution...
					return (descriptor.getField() != null ?
							converter.convertIfNecessary(value, type, descriptor.getField()) :
							converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
				}
			}

			// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			// 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				// required为true,抛异常
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;

			if (matchingBeans.size() > 1) {
				// 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
					}
					else {
						// In case of an optional Collection/Map, silently ignore a non-unique case:
						// possibly it was meant to be an empty collection of multiple regular beans
						// (before 4.3 in particular when we didn't even look for collection beans).
						return null;
					}
				}
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

			// 记录匹配过的beanName
			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			// 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBean
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

该方法的流程图如下:
在这里插入图片描述
在上述流程中,findAutowireCandidates()方法是用于查找自动注入的对象。

	protected Map<String, Object> findAutowireCandidates(
			@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

		// 从BeanFactory中找出和requiredType所匹配的beanName,仅仅是beanName,这些bean不一定经过了实例化,只有到最终确定某个Bean了,如果这个Bean还没有实例化才会真正进行实例化
		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);

		// 根据类型从resolvableDependencies中匹配Bean,resolvableDependencies中存放的是类型:Bean对象,比如BeanFactory.class:BeanFactory对象,在Spring启动时设置
		for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
			Class<?> autowiringType = classObjectEntry.getKey();
			if (autowiringType.isAssignableFrom(requiredType)) {
				Object autowiringValue = classObjectEntry.getValue();
				autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);

				if (requiredType.isInstance(autowiringValue)) {
					result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
					break;
				}
			}
		}


		for (String candidate : candidateNames) {
			// 如果不是自己,则判断该candidate到底能不能用来进行自动注入
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}

		// 为空要么是真的没有匹配的,要么是匹配的自己
		if (result.isEmpty()) {
			// 需要匹配的类型是不是Map、数组之类的
			boolean multiple = indicatesMultipleBeans(requiredType);
			// Consider fallback matches if the first pass failed to find anything...
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
			for (String candidate : candidateNames) {
				if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
						(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}

			// 匹配的是自己,被自己添加到result中
			if (result.isEmpty() && !multiple) {
				
				for (String candidate : candidateNames) {
					if (isSelfReference(beanName, candidate) &&
							(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
							isAutowireCandidate(candidate, fallbackDescriptor)) {
						addCandidateEntry(result, candidate, descriptor, requiredType);
					}
				}
			}
		}
		return result;
	}

该方法流程如下:
在这里插入图片描述

  • 31
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值