@Autowired尋找注入點
- 本文源碼基于spring-framework-5.3.10。
- 在屬性注入的時候,spring需要找到那些屬性需要注入!
- 源碼位于:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(RootBeanDefinition, Class<?>, String)
- 他在Bean的生命周期的合并BeanDefinition中調用。
@Autowired注解可以寫的位置
- 屬性上:先根據屬性類型去找Bean,如果找到多個再根據屬性名确定一個
- 構造方法上:先根據方法參數類型去找Bean,如果找到多個再根據參數名确定一個
- set方法上:先根據方法參數類型去找Bean,如果找到多個再根據參數名确定一個
@Autowired尋找注入點原理
- 周遊目前類的所有的屬性字段Field
-
檢視字段上是否存在@Autowired、@Value、@Inject中的其中任意一個,存在則認為該字段是一個注入點
如果字段是static的,則不進行注入
- 擷取@Autowired中的required屬性的值
-
将字段資訊構造成一個AutowiredFieldElement對象,作為一個注入點對象添加到currElements集合中。
周遊目前類的所有方法Method
- 判斷目前Method是否是橋接方法,如果是找到原方法
- 檢視方法上是否存在@Autowired、@Value、@Inject中的其中任意一個,存在則認為該方法是一個注入點
- 如果方法是static的,則不進行注入
- 擷取@Autowired中的required屬性的值
- 将方法資訊構造成一個AutowiredMethodElement對象,作為一個注入點對象添加到currElements集合中。
- 周遊完目前類的字段和方法後,将周遊父類的,直到沒有父類。
- 最後将currElements集合封裝成一個InjectionMetadata對象,作為目前Bean對于的注入點集合對象,并緩存。
@Autowired尋找注入點:postProcessMergedBeanDefinition源碼分析
/**
* 在合并BeanDefinition的時候調用
*/
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 尋找注入點
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
// 檢查配置
metadata.checkConfigMembers(beanDefinition);
}
/**
* 尋找注入點的具體邏輯
*/
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
// 得到目前Bean的名字
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
// metadata:可以簡單了解為一個注入點的集合
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 讀取到了類的中繼資料之後,進行對字段、普通方法、構造方法的處理
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
// metadata:可以簡單了解為一個注入點的集合
metadata = this.injectionMetadataCache.get(cacheKey);
// 雙重檢測在不在緩存,沒有變化
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
// 清理一些要跳過的自己設定的值?已經注入的值
if (metadata != null) {
metadata.clear(pvs);
}
// 解析注入點
metadata = buildAutowiringMetadata(clazz);
// 将目前Bean的注入資訊緩存
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
/**
* 尋找解析注入點
*/
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 如果一個Bean的是java.開頭的類,那麼則根本不需要進行依賴注入
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
// 定義一個存放注入資訊的集合:包含每一個注入點
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
// 得到目前Bean的class對象
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不是注入點,不會進行自動注入。static屬于類,原型Bean在建立的時候會有屬性值覆寫問題
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判斷是否是必須的,簡單了解就是@autowired(required = true)
boolean required = determineRequiredStatus(ann);
// 注入點加入到上面的集合中
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 周遊targetClass中的所有Method
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 橋接方法,找到真正的方法
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不是注入點,不會進行自動注入。static屬于類,原型Bean在建立的時候會有屬性值覆寫問題
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// set方法最好有入參,無參數會列印日志。這裡也就是說,提供一個s方法,标記位@Autowired,他會在依賴注入的時候(初始化前)執行!
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
// 判斷是否是必須的,簡單了解就是@autowired(required = true)
boolean required = determineRequiredStatus(ann);
// 屬性描述器中存在目前的屬性(set方法或者get方法的名稱)
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// 注入點加入到上面的集合中
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 頭插法的方式将目前類的所有注入點放入集合中
elements.addAll(0, currElements);
// 得到他的父類,繼續循環,直到沒有父類!
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
// 生成InjectionMetadata對象
return InjectionMetadata.forElements(elements, clazz);
}
一個字段能不能成為注入點
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
// 得到目前字段上所有的注解
MergedAnnotations annotations = MergedAnnotations.from(ao);
// 包含符合注入點的條件直接傳回(@Autowired、@Value、@Inject),否則傳回null
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
有什麼樣的标記會成為注入點?
public AutowiredAnnotationBeanPostProcessor() {
// @Autowired會成為注入點
this.autowiredAnnotationTypes.add(Autowired.class);
// @Value會成為注入點
this.autowiredAnnotationTypes.add(Value.class);
try {
// @Inject也會成為注入點
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
結束語
- 擷取更多本文的前置知識文章,以及新的有價值的文章,讓我們一起成為架構師!
- 關注公衆号,可以讓你對MySQL、并發程式設計、spring源碼有深入的了解!
- 關注公衆号,後續持續高效的學習JVM!
- 這個公衆号,無廣告!!!每日更新!!!