<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
建構函式注入
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
,我們應該怎麼實現?我想做法還是比較簡單的
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個處理器我已用黃色標出
// 尋找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種型別的屬性
// 注入 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);
也是不一樣的
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進行屬性的注入
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超詳細分析@Autowired原理的文章就介紹到這了,更多相關Java @Autowired內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45