談談 -Autowired 的實現原理
@Autowired 使用
構造函數注入
public Class Outer {
private Inner inner;
@Autowired
public Outer(Inner inner) {
this.inner = inner;
}
}
屬性注入
public Class Outer {
@Autowired
private Inner inner;
}
方法注入
public Class Outer {
private Inner inner;
public Inner getInner() {
return inner;
}
@Autowired
public void setInner(Inner inner) {
this.inner = inner;
}
}
目前絕大部分的代碼都使用第 2、第 3 種。第 1 種在 bean 實例化時完成,而第 2、第 3 種的實現原理都是一樣的,在屬性填充時完成。本篇將介紹第二第三種的是實現原理
在開始之前,如果我們自己設計@Autowired
,我們應該怎麼實現?我想做法還是比較簡單的
-
通過反射查找 bean 的 class 下所有註解了 @Autowired 的字段和方法
-
獲取到字段,通過 getBean(字段) 獲取到對應 bean,然後再通過反射調用 field 的 set 將 bean 注入
@Autowired 源碼分析
AutowiredAnnotationBeanPostProcessor
類
該類是@Autowired
的具體實現類,先預覽一下類方法
發現實際有機會介入 bean 的創建操作只有可能是後置處理器,用於後置處理的有 3 個方法,其中一個過時不用,分別是postProcessMergedBeanDefinition
、postProcessProperties
後置處理,我們再看一下這 2 個方法的具體代碼
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
...
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 1. 尋找bean中所有被@Autowired註釋的屬性,並將屬性封裝成InjectedElement類型
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
...
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 1. 尋找通過@Autowired註解的屬性或者方法
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 2. 注入
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;
}
...
}
跟我們的猜想是一樣的,首先先找出所有註解了@Autowired
的屬性或者方法,然後進行注入,當然postProcessMergedBeanDefinition
後置處理器的調用肯定是在postProcessProperties
之前的,這裏我們回顧一下spring bean
的創建過程。
2 個處理器我已用黃色標出
1. 查找所有 @Autowired
// 尋找bean中所有被@Autowired註釋的屬性,並將屬性封裝成InjectedElement類型
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
// 獲取緩存的key值,一般以beanName做key
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);
// 檢測metadata是否需要更新
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 通過clazz類,查找所有@Autowired的屬性或者方法,並封裝成InjectionMetadata類型
metadata = buildAutowiringMetadata(clazz);
// 將metadata加入緩存
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
可以看到 spring 依然在用緩存的方式提高性能,繼續跟蹤核心代碼buildAutowiringMetadata(clazz)
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 查看clazz是否有Autowired註解
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
// 這裏需要注意AutowiredFieldElement,AutowiredMethodElement均繼承了InjectionMetadata.InjectedElement
// 因此這個列表是可以保存註解的屬性和被註解的方法的
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
// 1. 通過do while循環,遞歸的往直接繼承的父類尋找@Autowired
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 2. 通過反射,獲取所有屬性,doWithLocalFields則是循環的對每個屬性應用以下匿名方法
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 判斷當前field屬性是否含有@Autowired的註解
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
// 返回該屬性在類中的修飾符,如果等於static常量,則拋出異常,@Autowired不允許註解在靜態屬性上
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// @Autowired有required屬性,獲取required的值,默認爲true
boolean required = determineRequiredStatus(ann);
// 3. 將field封裝成InjectedElement,並添加到集合中,這裏用的是AutowiredFieldElement
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 4. @Autowired可以註解在方法上
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// 5. 將方法封裝成InjectedElement,並添加到集合中,這裏用的是AutowiredMethodElement
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
// 返回直接繼承的父類
targetClass = targetClass.getSuperclass();
}
// 如果父類不爲空則需要把父類的@Autowired屬性或方法也找出
while (targetClass != null && targetClass != Object.class);
// 6. new InjectionMetadata(clazz, elements),將找到的所有的待注入屬性或方法生成metadata返回
return InjectionMetadata.forElements(elements, clazz);
}
-
外層
do … while …
的循環被用於遞歸的查找父類的@Autowired
屬性或方法 -
通過反射的方式獲取到所有屬性並循環驗證每一個屬性是否被
@Autowired
註解 -
將查找到包含
@Autowired
註解的 filed 封裝成AutowiredFieldElement
,加入到列表中 -
循環查找在方法上的註解
-
將找到的方法封裝成
AutowiredMethodElement
,並加入列表
這裏需要特別強調一點,InjectedElement
被AutowiredFieldElement
、AutowiredMethodElement
所繼承,他們都有各自的 inject 函數,實現各自的注入。因此改ArrayList elements
是擁有 2 種類型的屬性
- 將找到的所有元素列表和 clazz 作爲參數生成 metadata 數據返回
2. 注入
// 注入
metadata.inject(bean, beanName, 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) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
// 循環注入,這裏有可能是AutowiredFieldElement也可能AutowiredMethodElement,因此調用的inject是2個不同的方法
element.inject(target, beanName, pvs);
}
}
}
利用 for 循環,遍歷剛剛我們查到到的 elements 列表,進行注入。
在上面有特別提醒,這裏的 element 有可能是AutowiredFieldElement
類型、或AutowiredMethodElement
類型。各自代表@Autowired
註解在屬性上、以及註解在方法上的 2 種不同元素。因此他們調用的element.inject(target, beanName, pvs);
也是不一樣的
2.1 字段注入 (AutowiredFieldElement)
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
@Override
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);
// 設置class
desc.setContainingClass(bean.getClass());
// 需要被自動注入的beanNames,這裏只有可能 = 1,方法注入時纔有可能爲多個
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();// 獲取類型轉換器
try {
// 通過beanFactory獲取屬性對應的值,比如需要調用getBean("b")獲取依賴的屬性單例,並且通過自動轉型轉爲需要的類型
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);
// 因爲是屬性注入,因此這裏只有可能等於1
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
// 緩存當前value
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
// 通過反射,將value值設置到bean中
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
上方大部分的工作都在做待注入 bean 的獲取以及類型的轉換,如果深究下去可以再把 spring Ioc 講一遍,但是核心還是 getBean(字段) 獲取到對應 bean… 我們這裏就關心核心的語句,就是這 2 句
if (value != null) {
// 通過反射,將value值設置到bean中
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
spring 通過反射的方式,調用 field 的 set 進行屬性的注入
2.2 方法注入 (AutowiredMethodElement)
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
if (checkPropertySkipping(pvs)) {
return;
}
// @Autowired標註在方法上
Method method = (Method) this.member;
Object[] arguments;
if (this.cached) {
// Shortcut for avoiding synchronization...
// 有緩存
arguments = resolveCachedArguments(beanName);
}
else {
// 沒緩存,直接獲取方法上所有的參數
int argumentCount = method.getParameterCount();
arguments = new Object[argumentCount];
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
// 循環所有參數
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
// 通過beanFactory,獲取代注入的bean,並進行類型轉換
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
// 註冊依賴
registerDependentBeans(beanName, autowiredBeans);
// 如果自動注入的個數 = 參數個數,則緩存
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
// 緩存
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
// 緩存方法
this.cachedMethodArguments = cachedMethodArguments;
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
}
}
if (arguments != null) {
try {
// 反射調用注入方法,將獲取到的所有bean作爲參數
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}
這裏與屬性注入最大的區別在於,@Autowired
註解在方法上,方法可以擁有多個參數,因此這裏需要通過循環將一個個獲取,而獲取 bean 的方式於上面一樣,本質都是通過 getBean 獲取。
而核心語句還是 2 句
// 反射調用注入方法,將獲取到的所有bean作爲參數
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
與屬性注入不同的是,當@Autowired
註解在方法上,例如我們註解在 setter 方法上,則只需要直接調用該 setter 方法將參數數組傳入即可以,即使用 invoke 觸發方法,具體屬性賦值的過程在 setter 方法中由用戶自行編寫
程序員追風 專注於分享 Java 各類學習筆記、面試題以及 IT 類資訊。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/izkEr0yLjKkjwZ9zc02rtw