眾所周知@Resource 和 @Autowired兩大注解是開發中最常用的兩大注解。兩者有一定的區別:
@Autowired
@Autowired是spring框架提供的注解類,默認按照類型進行裝配。當在容器中找不到對應類型的bean時會拋出NoSuchBeanDefinitionException異常,當存在多個符合條件的bean且沒有指定選擇策略時會拋出BeanNotOfRequiredTypeException異常。
@Autowired也可以按bean的名稱進行裝配,這個時候需要借助@Qualifier注解。
@Autowired
@Qualifier("injectServiceA")
private InjectService injectService;
指定注入的InjectService類型的bean名稱為injectServiceA。
@Autowired還有一個布爾屬性required,默認是true,如果設置成false,找不到對應的依賴不會報錯。就需要代碼邏輯上來判斷是否有當前服務。
@Autowired(required = false)
private InjectService injectService1;
@Resource
@Resource時Java EE提供的注解規范,在 Java EE 容器中同樣可以使用。@Resource 默認情況下(未顯示指定bean名稱)按照名稱(name)進行自動裝配。如果按照名稱找不到會在嘗試按類型進行裝配。這樣看來@Resource相對來說更健壯一些。
@Resource注解可以屬性和set方法上。使用在屬性上就是按屬性名進行尋找bean。
@Resource
private InjectService otherService;
如果要指定名字,可以使用name屬性
@Resource(name="injectServiceA")
private InjectService injectService2;
除了使用name屬性也可以使用@Qualifier注解指定bean名稱。
@Resource
@Qualifier("injectServiceB")
private InjectService injectService3;
也可以指定按類型來裝配
@Resource(type = InjectService.class)
private InjectService injectService4;
因為時按照名稱進行裝配,不會存在多個符合條件的bean,但是會存在類型不匹配問題,拋出BeanNotOfRequiredTypeException異常。
@Primary
不管是@Resource還是@Autowired都存在一個問題,按類型查找時候如果存在多個符合條件的bean就無法完成注入,這個時候可以在被注入的bean上添加@Primary來標識當前bean是多個候選實例中優選選用的bean。找到多個是會優選使用帶有@Primary注解的bean。
@Autowired源碼分析
沒錯@Autowired類型的屬性注入是通過context初始化時候添加的類后置處理器AutowiredAnnotationBeanPostProcessor來完成的。
在AnnotationConfigApplicationContext容器構造方法初始化reader時候會調用AnnotationConfigUtils.registerAnnotationConfigProcessors來注冊processors,這里就會加載注解自動注入的bean后置處理器AutowiredAnnotationBeanPostProcessor。
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
AutowiredAnnotationBeanPostProcessor繼承自SmartInstantiationAwareBeanPostProcessor。
那么什么時候調用的postProcessor呢,在bean的初始化過程中,創建完實例后就會進行屬性注入,主要在 AbstractAutowireCapableBeanFactory#populateBean方法來完成。
//...
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {//是否有InstantiationAwareBeanPostProcessor處理器if (pvs == null) {pvs = mbd.getPropertyValues();}//逐個拿出InstantiationAwareBeanPostProcessor,調用postProcessProperties方法。for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}
}
AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);metadata.inject(bean, beanName, pvs);return pvs;
}
這里首先會根據注解讀取所有帶有指定注解的bean屬性和方法。注解包括@Autowired和@Value。如果支持JSR-330,也包含@Inject注解。 這個是在AutowiredAnnotationBeanPostProcessor的構造函數里初始化的。找到帶有對應的注解會封裝成InjectedElement對象集合放到InjectionMetadata的injectedElements屬性里,是一個注解。然后metadata.inject方法會將injectedElements一一進行注入,調用InjectedElement.inject()方法。
AutowiredFieldElement#inject()主要邏輯
Field field = (Field) this.member;
value = resolveFieldValue(field, bean, beanName);
if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);
}
最后會調用beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);方法獲取依賴的值,這里面就有@Autowired注入根據類型的邏輯。將值設置給field完成依賴注入。
@Resource注解源碼分析
@Resource注解的處理過程和@Autowired的過程大致是一致的,只不過其使用的bean處理器是CommonAnnotationBeanPostProcessor。其找到的符合條件的屬性封裝成ResourceElement extends LookupElement。獲取屬性值調用CommonAnnotationBeanPostProcessor.getResourceToInject()方法。
這個就留給自己去看吧。