Spring component-scan原始碼分析(三) -- @Autowired等註解的處理
本篇文章分析注入註解(@Autowired、@Value等)的處理,其邏輯在AutowiredAnnotationBeanPostProcessor類中。
可以看到AutowiredAnnotationBeanPostProcessor類實現了一些增強處理的介面,其中關鍵方法是postProcessMergedBeanDefinition和postProcessProperties方法。
- postProcessMergedBeanDefinition是MergedBeanDefinitionPostProcessor介面的方法,Spring是在建立了物件例項後還沒填充注入屬性前呼叫的;
- postProcessProperties是InstantiationAwareBeanPostProcessor介面的方法,Spring是在建立了物件例項後,整理收集完例項的注入屬性描述(PropertyValues類)後呼叫的;
總之呼叫順序postProcessMergedBeanDefinition -> postProcessProperties
1 postProcessMergedBeanDefinition方法
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
//1.1 對bean把帶有注入註解的欄位、方法封裝起來,最後封裝成一個InjectionMetadata物件
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
//1.2 把需要注入的Member(java反射包下的類,描述Field、Method、Constructor的資訊)記錄在beanDefinition
metadata.checkConfigMembers(beanDefinition);
}
1.1 對bean把帶有注入註解的欄位、方法封裝起來,最後封裝成一個InjectionMetadata物件
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
//以類名或bean名作為快取的key
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
//獲取快取
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
//用了double check
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
//clazz改變了才進入這裡
//從pvs中清理重複的屬性
metadata.clear(pvs);
}
//【標記1】對當前類進行解析所有要注入的的屬性
metadata = buildAutowiringMetadata(clazz);
//快取
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
【標記1】對當前類進行解析所有要注入的的屬性
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
//遍歷類的成員
ReflectionUtils.doWithLocalFields(targetClass, field -> {
//拿到@Autowired、@Value、@Inject(JSR-330的)註解
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
//不支援注入的靜態的變數
if (Modifier.isStatic(field.getModifiers())) {
...省略log
return;
}
//拿到註解的required屬性
boolean required = determineRequiredStatus(ann);
//封裝成AutowiredFieldElement
currElements.add(new AutowiredFieldElement(field, required));
}
});
//遍歷類的方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
//拿到實際的泛型方法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
//判斷是不是實際的泛型方法
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
//拿到@Autowired註解
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
//不支援靜態方法注入
if (Modifier.isStatic(method.getModifiers())) {
...省略log
return;
}
//沒引數的話沒什麼意義啊
if (method.getParameterCount() == 0) {
...省略log
}
//拿到註解的required屬性
boolean required = determineRequiredStatus(ann);
//這裡規定了要符合javaBean規範(PropertyDescriptor是java.beans包下的類,可以拿到setter、getter方法)
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
//封裝成AutowiredMethodElement
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
//繼續解析父類
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
//封裝成InjectionMetadata
return new InjectionMetadata(clazz, elements);
}
可以看到該方法是對類中需要注入的欄位、方法分別封裝成AutowiredFieldElement、AutowiredMethodElement,然後統一存入集合,最後封裝成一個InjectionMetadata物件。
1.2 把需要注入的Member(java反射包下的類,描述Field、Method、Constructor的資訊)記錄在beanDefinition
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
for (InjectedElement element : this.injectedElements) {
Member member = element.getMember();
////這裡是為了防止重複?
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
checkedElements.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
}
}
}
//記錄以檢查過的
this.checkedElements = checkedElements;
}
該方法是屬於InjectionMetadata類的,上面已經提過,類中需要注入的欄位、方法會被封裝後傳進來,injectedElements就是裝載它們的集合。而這個方法做的是,利用beanDefinition中的externallyManagedConfigMembers(一個Set),來判斷是否已存在相同的Member,沒有就加入checkedElements。
2 postProcessProperties方法
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
//在postProcessMergedBeanDefinition已經解析過並快取了,直接可以拿到
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方法,然後呼叫InjectionMetadata類中的inject方法直接進行注入處理。
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
//checkedElements為空就遍歷injectedElements
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
//有Field和Method兩種注入方式
for (InjectedElement element : elementsToIterate) {
...省略log
element.inject(target, beanName, pvs);
}
}
}
該方法會去checkedElements或injectedElements進行遍歷處理待注入欄位或方法。上面提到,欄位的注入會被封裝成AutowiredFieldElement,方法的注入會被封裝成AutowiredMethodElement,它們都是InjectedElement型別。
先看看AutowiredFieldElement#inject
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;
//想容器註冊依賴的bean
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
//封裝成ShortcutDependencyDescriptor進行快取
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);
}
}
}
該方法主要邏輯是從容器beanFactory獲得相應的bean,然後反射賦值
AutowiredMethodElement#inject的邏輯和AutowiredFieldElement的差不多,只不過變成了一個或多個bean的處理。主要邏輯還是從容器beanFactory中獲取相應的bean,最終反射呼叫方法。
總結:
注入註解的處理時機是在Spring建立bean後還沒進行屬性填充之前,通過從beanFactory容器中獲取相應的bean,最後反射賦值。