1. 程式人生 > >AutowiredAnnotationBeanPostProcessor依賴注入解析

AutowiredAnnotationBeanPostProcessor依賴注入解析

AutowiredAnnotationBeanPostProcessor依賴注入解析

Spring的bean建立流程

當我們對spring深入瞭解後,我們會發現spring對bean的建立和管理的流程是很清晰的明瞭的,通過這篇文章

Spring載入applicationContext.xml,我們可以很清楚的知道其實spring建立bean的流程是這樣的:

首先,spring會先準備哪些bean是交由它建立的,這就是Spring載入applicationContext.xml這篇文章裡面講到的,載入applicationContext配置檔案其實就是為了後續建立bean而做的準備階段,怎麼讓spring知道自己要載入和建立哪些bean呢?這是通過spring提供的各個註解來實現,通過Service、Component、Controller等註解,spring可以掃描這些bean,併為他們準備了後續要建立bean所需要的資訊,在spring裡叫做definition

其次,當準備階段做完後,Spring就會開啟建立bean的過程,就是遍歷所有的需要初始化的BeanDefinition,然後例項化這些bean,但是例項化的bean,實際上裡面的屬性和方法等物件是未例項化的,所以當建立了bean之後我們就需要去為這些目標bean裡面注入他所需要的物件,這就是我們今天要講的重點,spring的依賴注入。

最後一個流程就是遍歷每個目標bean裡面被註解Autowired、Value等註解標識的屬性以及方法,然後例項化這些元素,然後注入。

控制反轉和依賴注入

在我看來,控制反轉和依賴注入是同一種概念,兩種的唯一區別就是,控制反轉是在理論層面上提出來的,而依賴注入更偏向於實現層面上的,簡單來說,依賴注入是控制反轉的一種實現形式,並無差別。在Spring中bean的建立就是通過依賴注入的形式實現控制反轉的。

Spring的依賴注入的整個流程,首先Spring會遍歷所有需要建立並例項化的bean,先例項化bean接下來就是為例項化好的bean,這時候我們稱該bean為目標,為目標bean注入相應的物件,這些物件可以通過屬性注入和方法注入兩種方式來實現。

其次,Spring會找到目標bean裡面的所有被Autowired、Value註解的屬性和方法,然後執行建立例項化bean操作並返回值;

最後,通過返回的值反射注入到對應的物件,整個流程就結束了。整個流程如下:

AutowiredAnnotationBeanPostProcessor簡介

AutowiredAnnotationBeanPostProcessor是BeanPostProcessor的實現類,能夠自動裝配被註解Autowired、Value註解的屬性、setter方法以及配置方法,這些類成員能夠被自動的檢測到並注入。當然,AutowiredAnnotationBeanPostProcessor還支援JSR-330的Inject註解。簡單來講,AutowiredAnnotationBeanPostProcessor就是個依賴注入的處理器。

AutowiredAnnotationBeanPostProcessor只提供一個構造方法,該構造法設定了AutowiredAnnotationBeanPostProcessor處理器能夠支援哪些註解,預設情況下支援3種Autowired、Value以及Inject,構造方法如下:

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.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

當然,AutowiredAnnotationBeanPostProcessor也支援自定義註解的發現,AutowiredAnnotationBeanPostProcessor類提供了兩個方法來進行修改,如下:

public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) {
		Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
		this.autowiredAnnotationTypes.clear();
		this.autowiredAnnotationTypes.add(autowiredAnnotationType);
	}

	
	public void setAutowiredAnnotationTypes(Set<Class<? extends Annotation>> autowiredAnnotationTypes) {
		Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
		this.autowiredAnnotationTypes.clear();
		this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes);
	}

所以,我們還能自定義Spring沒有的註解,讓AutowiredAnnotationBeanPostProcessor處理器也能自動的裝配我們自定義的註解成員變數。

AutowiredAnnotationBeanPostProcessor依賴注入的入口函式

依賴注入是在什麼時候執行的?答案是很明顯的,依賴注入的時機是在bean被建立之後,就會馬上執行依賴注入,所以AutowiredAnnotationBeanPostProcessor是在建立bean之後被呼叫,而呼叫的入口就是AutowiredAnnotationBeanPostProcessor的一個函式,函式如下:

public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

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

當bean建立完後就會觸發該方法來執行bean裡面的屬性和方法的注入流程,不過postProcessPropertyValues這個方法在後續高版本的Spring被替換掉了,但是具體的的注入流程沒變化,不影響我們講解依賴注入,讓我們繼續解析依賴注入的過程。引數bean其實就是建立好的要為其注入屬性的目標bean,所以注入是在bean建立完後才執行的。其實在該方法被呼叫之前,還有一個方法會先被呼叫,這個方法就是postProcessMergedBeanDefinition,該方法的作用其實比較簡單,就是為了快取下後續需要用到資料,其實他的程式碼和上面的程式碼很像,如下:

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (beanType != null) {
			InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
			metadata.checkConfigMembers(beanDefinition);
		}
	}

是不是很像,都呼叫了findAutowiringMetadata方法,而findAutowiringMetadata這個方法是有快取的處理的,所以重複呼叫的話是直接從快取獲取資料,加快注入的效率。

查詢目標類的注入屬性及方法

接下來我們繼續討論findAutowiringMetadata這個方法,這個方法其實就是去查詢被Autowired、@Value註解的所有屬性和方法,當然包括了目標類的所有父類都會去查詢,所以查詢的過程其實是個遞迴的過程。找到所有的成員後,會執行注入元資料物件的建立即例項化InjectionMetadata物件並返回。我們來看下它的程式碼:

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, 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);
					}
					try {
						metadata = buildAutowiringMetadata(clazz);
						this.injectionMetadataCache.put(cacheKey, metadata);
					}
					catch (NoClassDefFoundError err) {
						throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
								"] for autowiring metadata: could not find class that it depends on", err);
					}
				}
			}
		}
		return metadata;
	}

從原始碼可以看出,查詢的過程經過了快取的處理,依賴注入的元資料建立完後都會put進去injectionMetadataCache這個map裡面管理,下次進來的key如果是一樣的話並存在於map裡面就不會執行建立而是直接取快取裡面獲取。那Spring到底是怎麼樣建立InjectionMetadata物件的,還有InjectionMetadata裡面有什麼內容,讓我們繼續來揭曉,首先我們看下InjectionMetadata這個類具體如下:
InjectionMetadata的結構圖
這裡面我們關注幾個屬性:

  1. injectedElements屬性;

    所有的注入物件,包括屬性、setter方法以及配置方法都會被掃描到並儲存到這個集合中。InjectedElement有兩種實現分別是:AutowiredFieldElement和AutowiredMethodElement,從名字可以看出一種是對field的包裝一種是對method的包裝。

  2. inject(Object target, String beanName, PropertyValues pvs)方法;

    遍歷上面的injectedElements集合呼叫元素的注入方法其實是呼叫各自的具體的實現類的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.inject(target, beanName, pvs);
    			}
    		}
    	}
    

所以,buildAutowiringMetadata這個方法其實是完成了metadata的建立工作。

例項化需要注入的物件

我們以InjectedElement的一個實現類來解析依賴注入的操作,這個類就是上面提到的AutowiredFieldElement,這個類主要是非方法的屬性的注入操作,我們直接看它過載了inject方法如下:

protected void inject(Object bean, String beanName, 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<String>(1);
				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)) {
									if (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);
			}
		}

這個方法做了3個流程,第一個流程是去建立依賴物件,通過value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);這句實現,第二個就是將返回的值做快取處理;第三個就是反射set值。很明顯依賴bean的例項化是在resolveDependency裡面去完成的,接下來我們繼續看這個方法到底是怎麼完成bean的建立的,函式裡面有很多流程判斷,有些流程我也沒弄明白到底為什麼那麼做,目前只弄明白了主要流程,所以我們直接進入到主流程裡面,在resolveDependency裡面會去呼叫doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)這個方法,而這個方法就是依賴bean的主流程,這裡面會去建立bean的操作,程式碼如下,我們來講解下這裡面的具體實現:

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
			Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				return shortcut;
			}

			Class<?> type = descriptor.getDependencyType();
			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);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}

			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;

			if (matchingBeans.size() > 1) {
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(type, 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();
			}

			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			return (instanceCandidate instanceof Class ?
					descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

首先,會先判斷這個物件是不是@Value註解的,如果是就是去執行value解析,解析出value的值直接返回,如下就是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);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}

如果不是@Value的話,那麼就進入到解析bean的過程,下面這個是解析多重bean的(這個多重bean概念還沒搞清楚是什麼,看具體實現好像是個集合,集合裡面有不同的元素,這些元素的type是不一樣的)

Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

接著如果不是多重bean物件,則會去匹配單個的bean:

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

如果匹配到多個的話就會通過一種策略去決策哪個bean來注入,具體不張開講,不懂。找到了bean definition後,如果是個Class的話就會去執行建立bean的操作,又重新來一次bean建立流程不停的遞迴直到所有物件都建立完畢了就直接返回該例項物件。建立bean其實是下面這句程式碼做的:

descriptor.resolveCandidate(autowiredBeanName, type, this)

其實這裡面的實現很簡單就是呼叫beanFactory的getbean方法去例項化bean,如下:

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {

		return beanFactory.getBean(beanName, requiredType);
	}

getBean裡面又會執行bean的建立,bean建立後又會執行依賴注入流程,這樣遞迴直到所有的依賴bean都建立例項完後,才返回最外層的bean並執行反射注入到目標bean裡面。