详解@Resource解析过程
一、Resource注解解析過程
@Resource和@Autowired處理的邏輯基本相同,都是先查找注入點,然后再根據(jù)注入點進行屬性注入,所不同的是@Resource的解析是由CommonAnnotationBeanPostProcessor來完成的,@Autowired解析由AutowiredAnnotationBeanPostProcessor完成,下面介紹@Resource解析的全過程
1.1 尋找注入點
尋找注入點的時機與@Autowired一致,都是在實例化后,調(diào)用所有實現(xiàn)了MergedBeanDefinitionPostProcessor接口的postProcessMergedBeanDefinition()方法,同樣Resource注解的處理類CommonAnnotationBeanPostProcessor也實現(xiàn)了MergedBeanDefinitionPostProcessor接口,所以會調(diào)用該類的postProcessMergedBeanDefinition()方法來尋找注入點
方法的第一行代碼會去調(diào)用InitDestroyAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()來緩存和檢查初始化方法和銷毀方法
第二行代碼去查找@Resource注解的元數(shù)據(jù)
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition); }根據(jù)beanName先去緩存中獲取,如果緩存中沒有或者InjectionMetadata中的目標class與當前beanType不符,則調(diào)用buildResourceMetadata來生成注解元數(shù)據(jù)
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {// Fall back to class name as cache key, for backwards compatibility with custom callers.String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// Quick check on the concurrent map first, with minimal locking.InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}metadata = buildResourceMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata; }循環(huán)遍歷當前類和父類的屬性和方法,判斷是否有@Resource注解,如果有,則生成ResourceElement對象,ResourceElement繼承自InjectionMetadata.InjectedElement,將ResourceElement對象對象放入到InjectedElement對象結合中,然后生成InjectionMetadata對象返回
對于靜態(tài)屬性和靜態(tài)方法,@Autowired不會進行處理,但@Resource則直接拋出異常
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();ReflectionUtils.doWithLocalFields(targetClass, field -> {……else if (field.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new ResourceElement(field, field, null));}}});ReflectionUtils.doWithLocalMethods(targetClass, method -> {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}……else if (bridgedMethod.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static methods");}Class<?>[] paramTypes = method.getParameterTypes();if (paramTypes.length != 1) {throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new ResourceElement(method, bridgedMethod, pd));}}}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz); }ResourceElement的構造方法如下,@Resource注解可以通過name和type兩個方法指定注入Bean的名字和類型,如果沒有指定name,則使用默認的名稱,使用參數(shù)名或set方法去掉set前綴當作name
如果指定了type屬性,檢查屬性的類型或方法的第一個參數(shù)類型是否與指定的類型匹配,如果不匹配將拋出異常
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {super(member, pd);Resource resource = ae.getAnnotation(Resource.class);String resourceName = resource.name();Class<?> resourceType = resource.type();// 使用@Resource時沒有指定具體的name,那么則用field的name,或setXxx()中的xxxthis.isDefaultName = !StringUtils.hasLength(resourceName);if (this.isDefaultName) {resourceName = this.member.getName();if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {resourceName = Introspector.decapitalize(resourceName.substring(3));}}// 使用@Resource時指定了具體的name,進行占位符填充else if (embeddedValueResolver != null) {resourceName = embeddedValueResolver.resolveStringValue(resourceName);}// @Resource除開可以指定bean,還可以指定type,type默認為Objectif (Object.class != resourceType) {// 如果指定了type,則驗證一下和field的類型或set方法的第一個參數(shù)類型,是否和所指定的resourceType匹配checkResourceType(resourceType);}else {// No resource type specified... check field/method.resourceType = getResourceType();}…… }1.2 屬性注入
屬性注入是在屬性填充的時候過程中完成,調(diào)用InstantiationAwareBeanPostProcessor實現(xiàn)類的postProcessProperties(),@Resource注解的屬性注入是在CommonAnnotationBeanPostProcessor的postProcessProperties()中完成
-
開始注入
獲取已經(jīng)緩存的注入點,然后調(diào)用InjectedElement的inject(),這個地方不同于@Autowired,Autowired注解的屬性注入分別會調(diào)用AutowiredFieldElement和AutowiredMethodElement的inject()
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);}return 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) {element.inject(target, beanName, pvs);}} } -
獲取注入值
InjectedElement的inject()源碼如下,直接調(diào)用getResourceToInject()獲取依賴值進行注入
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)throws Throwable {if (this.isField) {Field field = (Field) this.member;ReflectionUtils.makeAccessible(field);field.set(target, getResourceToInject(target, requestingBeanName));}else {if (checkPropertySkipping(pvs)) {return;}try {Method method = (Method) this.member;ReflectionUtils.makeAccessible(method);method.invoke(target, getResourceToInject(target, requestingBeanName));}catch (InvocationTargetException ex) {throw ex.getTargetException();}} }如果屬性或方法標注了@Lazy注解,則生成一個代理對象,否則調(diào)用getResource()獲取注入對象
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :getResource(this, requestingBeanName)); }getResource()源碼如下,調(diào)用核心的autowireResource()獲取bean對象
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {……// 根據(jù)LookupElement從BeanFactory找到適合的bean對象return autowireResource(this.resourceFactory, element, requestingBeanName); }如果當前的beanFactory屬于AutowireCapableBeanFactory,如果根據(jù)名稱找不到bean對象時,還可以調(diào)用resolveDependency()根據(jù)type獲取或生成bean對象
@Resource注解查找bean對象時,先判斷是否使用默認名,如果用默認名,則先根據(jù)默認名判斷BeanFactory是否有,如果有的話直接調(diào)用getBean()獲取bean對象,如果沒有,再調(diào)用resolveDependency()根據(jù)類型去查找或生成bean對象,resolveDependency()是@Autowired解析的核心方法
如果指定了名稱,則直接調(diào)用getBean()方法去查找bean對象
如果找到了bean對象,則將它的name緩存在autowiredBeanNames中,最后調(diào)用beanFactory.registerDependentBean()緩存beanName之間的依賴關系,當前注入的bean對象的被哪些bean依賴,緩存的是beanNaem,而不是bean對象
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {Object resource;Set<String> autowiredBeanNames;String name = element.name;if (factory instanceof AutowireCapableBeanFactory) {AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;DependencyDescriptor descriptor = element.getDependencyDescriptor();// 假設@Resource中沒有指定name,并且field的name或setXxx()的xxx不存在對應的bean,那么則根據(jù)field類型或方法參數(shù)類型從BeanFactory去找if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {autowiredBeanNames = new LinkedHashSet<>();resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}}else {resource = beanFactory.resolveBeanByName(name, descriptor);autowiredBeanNames = Collections.singleton(name);}}else {resource = factory.getBean(name, element.lookupType);autowiredBeanNames = Collections.singleton(name);}if (factory instanceof ConfigurableBeanFactory) {ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;for (String autowiredBeanName : autowiredBeanNames) {if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);}}}return resource; }
1.3 與@Autowired區(qū)別
1、性能上兩者沒有任何區(qū)別,@Resource是Jdk提供了注解,Spring對其進行了具體實現(xiàn),在獲取依賴注入值的過程中,@Autowired先根據(jù)類型來查找,然后進行Qualifier篩選等層層篩選,而@Resource先根據(jù)名城查找,如果使用的是默認名,并且beanFactory根據(jù)名稱找不到的時候,才會調(diào)用與@Autowired一樣的邏輯根據(jù)類型來找
2、如果@Resource指定了名稱,則只會根據(jù)名稱來查找bean對象,找不到直接拋異常
總結
以上是生活随笔為你收集整理的详解@Resource解析过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 企业支付宝转账到个人银行卡(免费率 无限
- 下一篇: mysql查询bom清单_U8 数据库B