天天看點

@autowired注解_Spring架構你敢寫精通,面試官就敢問@Autowired注解的實作原理

@autowired注解_Spring架構你敢寫精通,面試官就敢問@Autowired注解的實作原理

面試官:Spring架構中的@Autowired注解可以标注在哪些地方?

小小白:@Autowired注解可以被标注在構造函數、屬性、setter方法或配置方法上,用于實作依賴自動注入。

面試官:有沒有研究過@Autowired注解的實作原理?

小小白:看過它的實作源碼。

面試官:那你說一下@Autowired注解的工作原理?

小小白:@Autowired注解的作用是由AutowiredAnnotationBeanPostProcessor實作的,檢視該類的源碼會發現它實作了MergedBeanDefinitionPostProcessor接口,進而實作了接口中的postProcessMergedBeanDefinition方法,@Autowired注解正是通過這個方法實作注入類型的預解析,将需要依賴注入的屬性資訊封裝到InjectionMetadata類中,InjectionMetadata類中包含了哪些需要注入的元素及元素要注入到哪個目标類中,在Spring容器啟動的過程中初始化單例bean的時候通過populateBean方法實作對屬性的注入。

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

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

public class InjectionMetadata {
	private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
	private final Class<?> targetClass;
	private final Collection<InjectedElement> injectedElements;
	private volatile Set<InjectedElement> checkedElements;           

面試官:AutowiredAnnotationBeanPostProcessor類的postProcessMergedBeanDefinition方法是在什麼時候被調用的?

小小白:Spring容器在啟動的時候會執行AbstractApplicationContext類的refresh方法,在refresh方法執行的過程中先注冊AutowiredAnnotationBeanPostProcessor,然後在對非延遲初始化的單例bean進行初始化時,會間接調用。具體實作細節分析如下。

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 重點看這裡:在這裡對AutowiredAnnotationBeanPostProcessor注冊
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 重點看這裡:對非延遲初始化的單例bean進行初始化
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}           

refresh方法中registerBeanPostProcessors(beanFactory)完成了對AutowiredAnnotationBeanPostProcessor的注冊,當執行finishBeanFactoryInitialization(beanFactory)方法對非延遲初始化的單例bean進行初始化時,會執行到AbstractAutowireCapableBeanFactory類的doCreateBean方法,在這個方法中有如下這麼一段代碼。

synchronized (mbd.postProcessingLock) {
	if (!mbd.postProcessed) {
		try {
			applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Post-processing of merged bean definition failed", ex);
		}
		mbd.postProcessed = true;
	}
}           

在這段代碼中會執行applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName),深入到這個applyMergedBeanDefinitionPostProcessors方法中。

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
	for (BeanPostProcessor bp : getBeanPostProcessors()) {
		if (bp instanceof MergedBeanDefinitionPostProcessor) {
			MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
			bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
		}
	}
}           

可以看到,if的條件判斷邏輯是否屬于MergedBeanDefinitionPostProcessor,而AutowiredAnnotationBeanPostProcessor正好實作了MergedBeanDefinitionPostProcessor接口,是以在這裡調用AutowiredAnnotationBeanPostProcessor類的postProcessMergedBeanDefinition方法。

面試官:你在說一下注入的過程?

小小白:使用AutowiredFieldElement實作對标注在屬性上的注入,使用AutowiredMethodElement對标注在方法上的注入。注入過程:根據需要注入的元素的描述資訊,按類型或名稱查找需要的依賴值,如果依賴沒有執行個體化先執行個體化依賴,然後使用反射進行指派。

面試官:@Resource或者@Autowired注解有什麼差別?

小小白:雖然@Resource和@Autowired都可以書寫标注在屬性或者該屬性的setter方法之上,但是@Resource預設是按照名稱來裝配注入的,隻有當找不到與名稱比對的bean才會按照類型來裝配注入;@Autowired預設是按照類型裝配注入的,預設情況下它要求依賴對象必須存在如果允許為null,可以設定它required屬性為false,如果想按照名稱來注入,則需要結合@Qualifier一起使用;@Resource注解是由JDK提供,而@Autowired是由Spring提供。

往期推薦:

大廠都聊分布式系統,面試不知道分布式鎖如何聊下去

面試官:SpringBoot中關于日志工具的使用,我想問你幾個常見問題

面試被問為什麼使用Spring Boot?答案好像沒那麼簡單

面試官:Spring架構内置了哪些可擴充接口,咱們一個一個聊

Spring聲明式事務處理的實作原理,來自面試官的窮追拷問

Spring MVC相關面試題就是無底洞,反正我是怕了

說實話,面試這麼問Spring架構的問題,我快扛不住了

沒使用加号拼接字元串,面試官竟然問我為什麼

面試官一步一步的套路你,為什麼SimpleDateFormat不是線程安全的

都說ThreadLocal被面試官問爛了,可為什麼面試官還是喜歡繼續問

Java注解是如何玩轉的,面試官和我聊了半個小時

如何去除代碼中的多次if而引發的一連串面試問題

String引發的提問,我差點跪了

就寫了一行代碼,被問了這麼多問題

面試官:JVM對鎖進行了優化,都優化了啥?

synchronized連環問

下一篇: 軟體的本質

繼續閱讀