@Resource注解原理

1.通过CommonAnnotationBeanPostProcessor后置处理器完成@Resource解析

  • CommonAnnotationBeanPostProcessor加入容器的时间点也是在容器的启动过程中AnnotationConfigUtils#registerAnnotationConfigProcessors
        if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
        }
  • CommonAnnotationBeanPostProcessor调用位置在两处
    第一处是在AbstractAutowireCapableBeanFactory#doCreateBean,调用方法postProcessMergedBeanDefinition,只对@Resource属性进行解析,不注入
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    // 应用MergedBeanDefinitionPostProcessor,bean合并后的处理,AutoWired注解正是通过此方法实现诸如类型的预解析
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

第二处是在AbstractAutowireCapableBeanFactory#populateBean,调用的是postProcessPropertyValues,也有@Resource解析的过程,但是第一步的解析结果已经放入缓存了,所以在这一步会从缓存取出,然后对属性进行注入

        if (hasInstAwareBpps) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);             
        }

2.CommonAnnotationBeanPostProcessor#postProcessProperties

    @Override
    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;
    }
  • @Resouce注解解析
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
  • 属性注入
metadata.inject(bean, beanName, pvs);

3.CommonAnnotationBeanPostProcessor#findResourceMetadata

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;
    }
  • 从缓存中取出注解元信息,若没有获取到,调用buildResourceMetadata获取注解信息
  • 将注解信息加入到缓存

4.CommonAnnotationBeanPostProcessor#buildResourceMetadata

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 -> {
                if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
                    }
                    currElements.add(new WebServiceRefElement(field, field, null));
                }
                else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@EJB annotation is not supported on static fields");
                    }
                    currElements.add(new EjbRefElement(field, field, null));
                }
                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;
                }
                if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
                        }
                        if (method.getParameterCount() != 1) {
                            throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
                        }
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
                    }
                    else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@EJB annotation is not supported on static methods");
                        }
                        if (method.getParameterCount() != 1) {
                            throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
                        }
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new EjbRefElement(method, bridgedMethod, pd));
                    }
                    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);
    }
  • 对webServiceRefClass,ejbClass,Resource.class注解进行解析,重点关注Resource.class注解
  • doWithLocalFields对成员变量上的@Resource解析,doWithLocalMethods对方法上的@Resource解析
  • 被@Resource注解的成员变量或方法是静态的,抛出异常
  • 在创建ResourceElement对象时,会对@Resource是否使用默认名称进行判断

5.CommonAnnotationBeanPostProcessor#ResourceElement

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();
            this.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));
                }
            }
            else if (embeddedValueResolver != null) {
                resourceName = embeddedValueResolver.resolveStringValue(resourceName);
            }
            if (Object.class != resourceType) {
                checkResourceType(resourceType);
            }
            else {
                // No resource type specified... check field/method.
                resourceType = getResourceType();
            }
            this.name = (resourceName != null ? resourceName : "");
            this.lookupType = resourceType;
            String lookupValue = resource.lookup();
            this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
            Lazy lazy = ae.getAnnotation(Lazy.class);
            this.lazyLookup = (lazy != null && lazy.value());
        }
  • this.isDefaultName = !StringUtils.hasLength(resourceName)判断是否使用默认名称,并根据相应的逻辑生成名字

6.InjectionMetadata#inject

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);
                }
                element.inject(target, beanName, pvs);
            }
        }
    }
  • 遍历elementsToIterate,对每个进行属性注入
  • element.inject调用的是InjectionMetadata内部的注入方法

7.InjectionMetadata#inject

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();
                }
            }
        }
  • 判断是成员变量还是方法,并进行相应的属性注入
  • getResourceToInject获取注入的对象

8.ResourceElement#getResourceToInject

        @Override
        protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
            return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
                    getResource(this, requestingBeanName));
        }
  • ResourceElement是CommonAnnotationBeanPostProcessor的内部类
  • 判断是注入属性是否懒加载

9.CommonAnnotationBeanPostProcessor#getResource

    protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
            throws NoSuchBeanDefinitionException {

        if (StringUtils.hasLength(element.mappedName)) {
            return this.jndiFactory.getBean(element.mappedName, element.lookupType);
        }
        if (this.alwaysUseJndiLookup) {
            return this.jndiFactory.getBean(element.name, element.lookupType);
        }
        if (this.resourceFactory == null) {
            throw new NoSuchBeanDefinitionException(element.lookupType,
                    "No resource factory configured - specify the 'resourceFactory' property");
        }
        return autowireResource(this.resourceFactory, element, requestingBeanName);
    }

10.CommonAnnotationBeanPostProcessor#autowireResource

    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();
            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;
    }
  • this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)为真,那么获取属性的逻辑与@Autowired的逻辑一样,先ByType后ByName查找
  • 否则通过resource = factory.getBean(name, element.lookupType)获取属性,此时是根据ByName注入属性

11.总结

所以可以看到@Resource注解并不是大家传统意义说的先byName后byType。

  • 关于this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name) 这个判断,由于前两个大多数情况都是返回true,那么焦灼点就是第三个条件
  • 如果第三个条件返回false,就会执行后面的resolveBeanByName,否则执行resolveDependency
  • resolveBeanByName的逻辑是根据name和type调用getBean;resolveDependency执行的逻辑是先根据类型查找,再根据名字确定。如果此处****!factory.containsBean(name)****返回true了,name执行resolveDependency时,相当于只执行了byType,给用户造成了一种先byName后byType的假象
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容