Spring源碼分析@Autowired、@Resource註解的不同

       關於Spring中@Autowired、@Resource和@Inject註解的區別請看:@Autowired、@Resource和@Inject註解的區別(最詳細),本片文章將會帶領你進行源碼分析@Autowired、@Resource註解的不同。

       在上面所說的那篇博客中,我們知道:

          Spring對於@Autowired、@Resource註解使用不同的後置處理器進行處理

          @Autowired、@Resource之間的處理方式不同,@Autowired是根據類型,@Resource是根據名稱

       在進行源碼分析之前,你需要了解以下Bean的生命週期:Spring中bean的生命週期(最詳細)其中在第五次調用bean的後置處理器時,完成屬性的依賴注入,第五次調用bean的後置處理器的步驟:拿到Spring容器中所有的實現了BeanPostProcessor接口的類,然後判斷其是否爲InstantiationAwareBeanPostProcessor接口的實現類,如果是調用postProcessProperties方法,完成屬性賦值。

       在@Autowired、@Resource和@Inject註解的區別(最詳細)文章中,已經知道了Spring使用AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor類分別處理@Autowired註解和@Resource註解,他們都實現了InstantiationAwareBeanPostProcessor類,所以標註了@Autowired、@Resource會在第六次調用bean的後置處理器的時候完成屬性注入。

                                

 

     從上面兩張類的關係圖可以看到其都間接實現了InstantiationAwareBeanPostProcessor類,下面我們就一起分析一下源碼:

代碼塊1.AbstractAutowireCapableBeanFactory#populateBean方法

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
	// state of the bean before properties are set. This can be used, for example,
	// to support styles of field injection.
	boolean continueWithPropertyPopulation = true;

	//1.第五次後置處理器,對應着我之前博客裏面寫的bean生命週期,首先判斷這個bean是否是合成的,這個絕大多數是不是合成的,然後判斷是否有InstantiationAwareBeanPostProcessors的接口
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		  //2.拿到所有的BeanPostProcessors處置處理器
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
				//3.逐一判斷這個後置處理器是否是InstantiationAwareBeanPostProcessor類型的,因爲BeanPostProcessor接口是爲了統一進行管理bean後置處理器的
				//BeanPostProcessor還有子接口,用於實現不同的作用,可以參考bean後置處理器那篇博客
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					continueWithPropertyPopulation = false;
					break;
				}
			}
		}
	}
		//4.如果爲true,則說明在第五次調用後置處理的時候返回爲false,這樣就不會進行屬性注入了
		//所以當你想bean中不進行屬性注入,可以實現InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法
	if (!continueWithPropertyPopulation) {
		return;
	}

	PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

	// 5.解析自動裝配模式爲AUTOWIRE_BY_NAME和AUTOWIRE_BY_TYPE(現在幾乎不用,現在默認是AUTOWIRE_NO)
	/**
	 * <bean id="fruit" class="com.joonwhee.open.demo.simple.Fruit" autowire="byName">
	 *     <property name="color" value="Red"/>
	 * </bean>
	 * id值跟Fruit裏的屬性名一致
	 * <bean id="apple" class="com.joonwhee.open.demo.simple.Apple"/>
	 *	public class Fruit {
	 * 		private Apple apple;//apple 會根據名稱完成自動注入
	 * 	    private String color;
	 * }
	 * */
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
		MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
		// Add property values based on autowire by name if applicable.
		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
			autowireByName(beanName, mbd, bw, newPvs);
		}
		// Add property values based on autowire by type if applicable.
		if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
			autowireByType(beanName, mbd, bw, newPvs);
		}
		pvs = newPvs;
	}
		//6.檢查是否有InstantiationAwareBeanPostProcessors接口的類
	boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
	boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

	if (hasInstAwareBpps || needsDepCheck) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
		PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
		if (hasInstAwareBpps) {
			//7.執行第六次後置處理器,完成屬性賦值
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					//在Spring5.1之後,使用的是postProcessProperties方法完成屬性注入
					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;
				}
			}
		}
		//8.依賴檢查,對應depends-on屬性
		if (needsDepCheck) {
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}
	}
	//9.將所有PropertyValues中的屬性填充到bean中
	if (pvs != null) {
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

       AbstractAutowireCapableBeanFactory#populateBean方法是在Spring啓動時,完成非懶加載單實例bean註冊到容器時會執行的方法,可以對着Spring中bean的生命週期(最詳細)來進行分析,populateBean方法會執行bean生命週期的第五、第六次後置處理器,第五次就不做分析了,關鍵進行分析第六次完成屬性注入的生命週期,在populateBean方法中的第7步,會拿到所有的BeanPostProcessors方法,然後判斷是否是InstantiationAwareBeanPostProcessor類型的,如果是則執行postProcessPropertyValues方法完成屬性注入,因爲AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor類都實現了InstantiationAwareBeanPostProcessor接口,所以會在第7步進行調用各自的方法,我們先看一下getBeanPostProcessors方法,看代碼塊2。

      然後看一下AutowiredAnnotationBeanPostProcessor的postProcessProperties方法,看代碼塊3.

      CommonAnnotationBeanPostProcessor的postProcessProperties方法,看代碼塊8.

代碼塊2.AbstractBeanFactory#getBeanPostProcessors方法

        /**
	 * Return the list of BeanPostProcessors that will get applied
	 * to beans created with this factory.
	 */
        //就是簡單的返回容器中所有的beanPostProcessors
	public List<BeanPostProcessor> getBeanPostProcessors() {
		return this.beanPostProcessors;
	}

代碼塊3. AutowiredAnnotationBeanPostProcessor#postProcessProperties方法

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        //1.獲取這個類中所有標註了@Autowired的屬性和方法,並把它們封裝在InjectionMetadata對象中
	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
	try {
	        //2.完成bean的屬性注入
		metadata.inject(bean, beanName, pvs);
	}
	catch (BeanCreationException ex) {
		throw ex;
	}
	return pvs;
}

       第1步,解析出這個類中所有的標註了@Autowired的屬性和方法,看代碼塊4,第2步,完成對bean屬性的注入,看代碼塊6.

代碼塊4.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata方法

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
	// Fall back to class name as cache key, for backwards compatibility with custom callers.
	//1.得到bean的名稱
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// Quick check on the concurrent map first, with minimal locking.
	//2.先從緩存中獲取已經解析過的類,在完成對一個類的解析之後,會進行緩存,第二次就不用在去解析了
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
	//3.判斷是否刷新
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
	    //4.加鎖
		synchronized (this.injectionMetadataCache) {
		    //6.再次從緩存中獲取解析之後的類
			metadata = this.injectionMetadataCache.get(cacheKey);
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				if (metadata != null) {
					metadata.clear(pvs);
				}
				//7.完成對類的解析,獲取所有的標註了@Autowired的屬性和方法
				metadata = buildAutowiringMetadata(clazz);
				//8.放入到緩存中
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}

      在第7步中會完成對類的解析,解析出所有標註了@Autowired的屬性和方法,看代碼塊5.

代碼塊5.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata方法

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

	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

                //1.解析類中屬性標註@Autowired註解的情況
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			AnnotationAttributes ann = findAutowiredAnnotation(field);
			if (ann != null) {
			    //2.如果這個屬性是static修飾的,即使被@Autowired標註也不會進行屬性的自動注入,直接返回
				//這裏使用的是lamda表達式,雖然有返回,但是不是退出buildAutowiringMetadata方法,不懂的自行百度
				if (Modifier.isStatic(field.getModifiers())) {
					return;
				}
				//3.判斷@Autowired註解裏面的required字段
				boolean required = determineRequiredStatus(ann);
				//4.封裝成AutowiredFieldElement對象,添加到currElements集合中
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});

		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			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())) {
				    //5.如果這個方法是static修飾的,即使被@Autowired標註也不會進行屬性的自動注入,直接返回
					return;
				}
				boolean required = determineRequiredStatus(ann);
				//6.判斷@Autowired註解裏面的required字段
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});

		elements.addAll(0, currElements);
		//7.解析父類,這個可以看出Spring想的真周到
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return new InjectionMetadata(clazz, elements);
}

      可以看到如果屬性和方法被static修飾的話,是不會完成屬性的自動注入的,在第7步中,還會遞歸解析當前類的父類,這一點我感覺Spring做的真周到,你想到的沒想到的,Spring都會幫你做。

代碼塊6.InjectionMetadata#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);
	//1.拿到類中(包括父類)標註了@Autowired註解的屬性和方法
	if (!elementsToIterate.isEmpty()) {
		for (InjectedElement element : elementsToIterate) {
			//2.遍歷,然後對每一個完成屬性注入,這個是分屬性和方法的,他們都實現了InjectedElement類,重寫了inject方法
			//這裏已屬性注入爲例
			element.inject(target, beanName, pvs);
		}
	}
}

     第2步遍歷類中所有的標註了@Autowired屬性和方法,具體看代碼塊7

代碼塊7.AutowiredFieldElement#inject方法

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        //1.記住這裏是調用InjectedElement的方法,標註了@Autowired註解的屬性和方法都會被封裝成InjectedElement類,
	//所以這裏使用this.member就是獲取封裝在InjectedElement的屬性
	Field field = (Field) this.member;
	Object value;
	//2.是否已有解析之後的緩存,如果有,則直接從緩存裏面取
	if (this.cached) {
		value = resolvedCachedArgument(beanName, this.cachedFieldValue);
	}
	else {
	        //3.將屬性和@Autowired註解裏面的@Autowired字段封裝成DependencyDescriptor對象
		DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
		desc.setContainingClass(bean.getClass());
		
		Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
		TypeConverter typeConverter = beanFactory.getTypeConverter();
		try {
		        //4.從容器中獲取所依賴的bean,由於該方法過於複雜,我只看懂了一點,所以這個方法就不行解析,我大致說一下過程
			//首先會判斷需要注入屬性的類型是否是Array、Collection、Map類型的,如果是,則查找所有屬性的泛型類型進行注入
			//例如Map<String,Student> map,然後Spring判斷是Map類型,將就回去查找Value所對應的類型是Student,
			//然後就會查找所有的Student類型,以bean的id爲key,以Student的實例爲Value注入到這個map中
			//如果不是Array、Collection、Map類型的,就是去查找所有的需要注入屬性的類型,如果只有一個,則直接注入,
			//如發現多個就會查找一個最優,會先通過@Primary註解查找最優,如果找不到會通過@Priority註解查找最優,如果該找不到
			//就是用基本策略,使用屬性的名稱進行逐個匹配查找到的bean的id,如果屬性的名稱和bean的id相同即爲最優,如果還找打不到
			//那麼就不要意思了,直接報錯了
			value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
		}
		catch (BeansException ex) {
			throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
		}
		//5.放到緩存中,下次不用進行解析了
		synchronized (this) {
			if (!this.cached) {
				if (value != null || this.required) {
					this.cachedFieldValue = desc;
					registerDependentBeans(beanName, autowiredBeanNames);
					//步驟如果autowiredBeanNames大於1,說明是Array、Collection、Map類型
					if (autowiredBeanNames.size() == 1) {
						String autowiredBeanName = autowiredBeanNames.iterator().next();
						//6.如果是1個,也需要判斷是不是Array、Collection、Map類型
						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);
	}
}
}

        其中第4步是最重點,也是最難得看懂的,如果想要了解這個方法,可以參考大神寫的博客:Spring IoC:createBean 詳解(上),這篇文章從代碼塊9開始講的就是這個方法,反正我是看懵逼了,有興趣的可以看看,到這裏關於@Autowired源碼就介紹完了。

      下面我們來看一下CommonAnnotationBeanPostProcessor的postProcessProperties方法。

代碼塊8.CommonAnnotationBeanPostProcessor#postProcessProperties方法

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        //1.獲取這個類中所有標註了@Resource的屬性和方法,並把它們封裝在InjectionMetadata對象中
	InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
	try {
	        //2.完成bean的屬性注入
		metadata.inject(bean, beanName, pvs);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
	}
	return pvs;
}

       CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor的postProcessProperties方法基本一樣。

      第1步,解析出所有標註了@Resource的屬性和方法,並把它們封裝在InjectionMetadata對象中,具體看代碼塊9。

      第2步,完成bean的屬性注入,具體看代碼塊13.

代碼塊9.CommonAnnotationBeanPostProcessor#findResourceMetadata方法

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.
	     //1.得到bean的名稱
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// Quick check on the concurrent map first, with minimal locking.
		//2.先從緩存中獲取已經解析過的類,在完成對一個類的解析之後,會進行緩存,第二次就不用在去解析了
	InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
	    //3.判斷是否刷新
	if (InjectionMetadata.needsRefresh(metadata, clazz)) {
	       //4.加鎖
		synchronized (this.injectionMetadataCache) {
		        //5.再次從緩存中獲取解析之後的類
			metadata = this.injectionMetadataCache.get(cacheKey);
			if (InjectionMetadata.needsRefresh(metadata, clazz)) {
				if (metadata != null) {
					metadata.clear(pvs);
				}
				    //6.完成對類的解析,獲取所有的標註了@Resource的屬性和方法
				metadata = buildResourceMetadata(clazz);
				    //7.放入到緩存中
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	return metadata;
}

      這個方法和AutowiredAnnotationBeanPostProcessor的一樣就不多介紹,關鍵看一下第6步,進行解析@Resource註解,解析的時候就不一樣了,具體看代碼塊10。

代碼塊10.CommonAnnotationBeanPostProcessor#buildResourceMetadata方法

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

	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
		//1.解析標註了@Resource註解的屬性,
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
		    //2.判斷是否標註了@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));
			}
			//3.判斷是否標註了@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));
			}
			//4.判斷是否標註了@Resource註解,
			else if (field.isAnnotationPresent(Resource.class)) {
			    //5.@Resource註解如果標註在static修飾的屬性上,直接報錯,不知道上面你是否記得@Autowired是怎麼處理static修飾的屬性的
				//忘記的翻看上文代碼
				if (Modifier.isStatic(field.getModifiers())) {
					throw new IllegalStateException("@Resource annotation is not supported on static fields");
				}
				//6.判斷是否是需要被忽略的類型
				if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
					currElements.add(new ResourceElement(field, field, null));
				}
			}
		});
                //7.解析標註了@Resource註解的方法
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
				return;
			}
			if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				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));
				}
				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));
				}
				//8.判斷方法上是否標註了@Resource註解
				else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
				        //9.@Resource註解如果標註在static修飾的方法上,直接報錯,
					if (Modifier.isStatic(method.getModifiers())) {
						throw new IllegalStateException("@Resource annotation is not supported on static methods");
					}
					//10.獲取其@Resource標註方法的形參列表
					Class<?>[] paramTypes = method.getParameterTypes();
					//11.如果不是1個就報錯,這Spring對於@Resource要求的真實苛刻啊,兩個爲啥不行呢,我是不知道的
					//有知道的可以告訴我一聲,我猜是鼓勵大家用SPring自家的註解
					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);
		//12.遞歸解析父類的@Resource註解
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return new InjectionMetadata(clazz, elements);
}

        到這裏我們已經知道了:

       一:使用@Resource時,如果是static則直接報錯,使用@Autowired則不會。

       二:使用@Resource標註在方法上面時,方法的參數只能有一個,沒有或多個參數則直接報錯,使用@Autowired則沒有限制。

       在第6步,會判斷是否是被忽略的類型,如果不是,則添加到集合中,我們看一下ResourceElement類是如何實現的,看代碼塊11

代碼塊11.CommonAnnotationBeanPostProcessor類的內部類ResourceElement

/**
 * Class representing injection information about an annotated field
 * or setter method, supporting the @Resource annotation.
 */
private class ResourceElement extends LookupElement {

	private final boolean lazyLookup;

	public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
		super(member, pd);
		//1.從屬性中面獲取@Resource註解
		Resource resource = ae.getAnnotation(Resource.class);
		//2.獲取@Resource註解上面name屬性的值
		String resourceName = resource.name();
		//3.獲取@Resource註解上面type屬性的值
		Class<?> resourceType = resource.type();
		//4.如果@Resource註解name屬性值爲空,則isDefaultName爲ture,就會使用默認的名稱
		this.isDefaultName = !StringUtils.hasLength(resourceName);
		if (this.isDefaultName) {
		        //5.如果使用了默認名稱的話,就會使用標註了@Resource註解屬性的名稱
			resourceName = this.member.getName();
			//6.如果標註了@Resource註解的是方法,那麼就是判斷是否是以set開頭的
			if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
			        //7.如果是以set開頭長度大於3則將set去除,並把首字母變成小寫
				resourceName = Introspector.decapitalize(resourceName.substring(3));
			}
		}
		else if (embeddedValueResolver != null) {
			resourceName = embeddedValueResolver.resolveStringValue(resourceName);
		}
		//8.檢查在@Resource註解中設置的屬性和和標註了@Resource註解的屬性類型是否是一致的
		if (Object.class != resourceType) {
			checkResourceType(resourceType);
		}
		else {
			// No resource type specified... check field/method.
			//9.沒用設置的的話,使用@Resource註解的屬性類型
			resourceType = getResourceType();
		}
		this.name = (resourceName != null ? resourceName : "");
		this.lookupType = resourceType;
		String lookupValue = resource.lookup();
		this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
		//10.判斷是否爲懶加載的
		Lazy lazy = ae.getAnnotation(Lazy.class);
		this.lazyLookup = (lazy != null && lazy.value());
	}

	@Override
	protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
		return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
				getResource(this, requestingBeanName));
	}
}

        這個類主要是用來解析@Resource註解的,因爲@Resource註解有很多屬性,具體看代碼塊12

代碼塊12.@Resource註解

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {

    String name() default "";

    String lookup() default "";

    Class<?> type() default java.lang.Object.class;

    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }

    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    boolean shareable() default true;

    String mappedName() default "";

    String description() default "";
}

     代碼塊12是爲了讓你更好的理解代碼塊11,這兩個要結合着看。

代碼塊13.InjectionMetadata#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);
	 //1.判斷類中標註了@Resource註解的集合是否爲空
	if (!elementsToIterate.isEmpty()) {
		for (InjectedElement element : elementsToIterate) {
		       //2.不爲空,則進行處理
			element.inject(target, beanName, pvs);
		}
	}
}

     第2步會遍歷類中所有加了@Resource註解的屬性和方法,然後對每個完成依賴注入,具體看代碼14

代碼塊14.InjectionMetadata類的內部類InjectedElement#inject方法

/**
 * Either this or {@link #getResourceToInject} needs to be overridden.
 */
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
		throws Throwable {
	//1.判斷是屬性還是方法
	if (this.isField) {
		Field field = (Field) this.member;
		ReflectionUtils.makeAccessible(field);
		//2.getResourceToInject方法會依賴項的查找
		field.set(target, getResourceToInject(target, requestingBeanName));
	}
	else {
		if (checkPropertySkipping(pvs)) {
			return;
		}
		try {
			Method method = (Method) this.member;
			ReflectionUtils.makeAccessible(method);
			method.invoke(target, getResourceToInject(target, requestingBeanName));
		}
		catch (InvocationTargetException ex) {
			throw ex.getTargetException();
		}
	}
}

       重點關注屬性的依賴注入,對於方法的和屬性的差不多,這裏不做過多介紹,在第2步會查找並注入依賴項,具體看代碼塊15.

代碼塊15.CommonAnnotationBeanPostProcessor類的內部類ResourceElement#getResourceToInject方法

@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
	return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
			getResource(this, requestingBeanName));
}

         判斷是否爲懶加載,如果是則進行懶加載,懶加載會返回一個代理的類用來替代,只有在使用到這個屬性的時候纔回去加載,如果不是懶加載,則調用getResource方法獲取依賴項,具體看代碼塊16.

代碼塊16.CommonAnnotationBeanPostProcessor#getResource方法

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {
	//這個mappedName沒有研究
	if (StringUtils.hasLength(element.mappedName)) {
		return this.jndiFactory.getBean(element.mappedName, element.lookupType);
	}
	if (this.alwaysUseJndiLookup) {
		return this.jndiFactory.getBean(element.name, element.lookupType);
	}
	//1.完成依賴注入
	return autowireResource(this.resourceFactory, element, requestingBeanName);
}

     在第1步中會完成依賴注入,看代碼塊17

代碼塊17.CommonAnnotationBeanPostProcessor#autowireResource方法

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
			throws NoSuchBeanDefinitionException {

	Object resource;
	Set<String> autowiredBeanNames;
	//1.獲取名稱,如果在註解內部指定了name屬性,則name就爲執行的name
	//如果沒有指定,則會獲取標註了@Resource註解中屬性的名稱
	String name = element.name;

	if (factory instanceof AutowireCapableBeanFactory) {
		AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
		DependencyDescriptor descriptor = element.getDependencyDescriptor();
		//2.fallbackToDefaultTypeMatch:判斷是否回退到默認類型匹配
		//isDefaultName:代碼塊11中進行了說明,如果@Resource註解name屬性有值,則isDefaultName爲false,如果@Resource註解name屬性沒有值,則爲true,
		//isDefaultName爲true,element.name是標註了@Resource註解中屬性的名稱
		//查看工廠中是否包含該名稱的bean
		if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
			autowiredBeanNames = new LinkedHashSet<>();
			//3.resolveDependency方法在代碼塊7中介紹了,如果走這個,是和@Autowired註解一樣
			resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
			if (resource == null) {
				throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
			}
		}
		else {
		    //4.根據名稱去查找bean
			resource = beanFactory.resolveBeanByName(name, descriptor);
			autowiredBeanNames = Collections.singleton(name);
		}
	}
	else {
		resource = factory.getBean(name, element.lookupType);
		autowiredBeanNames = Collections.singleton(name);
	}

	if (factory instanceof ConfigurableBeanFactory) {
		ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
		for (String autowiredBeanName : autowiredBeanNames) {
			if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
				beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
			}
		}
	}

	return resource;
}

          在第二步中,比較繞,我們舉例說明:

@Resource
private String student;
//因爲此時@Resource註解中name屬性爲空,
//所以:element.isDefaultName = true
//element.name = student

@Resource(name = "student1")
private String student2;
//因爲此時@Resource註解中name屬性有值,
//所以:element.isDefaultName = false
//element.name = student1,即@Resource內部指定的

       根據第二步我們知道,如果@Resource想要像@Autowired一樣,使用類型進行匹配,需要滿足一下條件,是都需要滿足的

       1.@Resource註解中name屬性沒有進行設置

       2.第一條滿足之後,會查看標註了@Resource屬性的名稱在容器在容器中不存在,什麼意思?比如

@Resource
private String student2;

       id爲student2的bean在容器中不存在。

       只有上面兩條同時存在,則會和@Autowired走一樣的邏輯。

未完待續......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章