前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@Autowired 与 @Resource 有何不同

@Autowired 与 @Resource 有何不同

作者头像
程序猿杜小头
发布2023-03-05 14:40:48
5790
发布2023-03-05 14:40:48
举报
文章被收录于专栏:程序猿杜小头程序猿杜小头

本文基于 Spring Boot 3.0.0 (Spring 6.0.2)。

@Autowired来自于 spring-beans 模块;而@Resource则来自于 jakarta.annotation-api 模块,它是 Jakarta EE 规范中的内容。虽然 @Autowired 与 @Resource 均用于实现依赖注入,但 Spring 对二者的处理逻辑是不一样的。

面向 @Autowired 与 @Resource 注解的依赖注入发生于 Bean 加载流程中 属性填充 populateBean 阶段,具体逻辑位于InstantiationAwareBeanPostProcessor实现类的postProcessProperties() 方法内。InstantiationAwareBeanPostProcessor 主要有两个实现类,分别是:AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor 优先于 AutowiredAnnotationBeanPostProcessor 执行,因此咱们先来分析 @Resource 注解,然后是 @Autowired 注解。

1 @Resource

在 CommonAnnotationBeanPostProcessor 中,postProcessProperties() 方法负责为 Bean 实例内由 @Resource 注解标识的 成员变量、setter 方法 注入依赖。

代码语言:javascript
复制
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
        implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
    @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;
    }
}

从上述内容来看,postProcessProperties() 方法做了两件事。

  1. 首先,构建一个InjectionMetadata实例,该实例内封装了若干ResourceElement,而每个 ResourceElement 实例代表了一个由 @Resource 注解标识的 字段 或 setter 方法。
  2. 然后,调用 InjectionMetadata 的inject()方法完成依赖注入。

接着跟进到 InjectionMetadata 中的 inject() 方法,主要就是遍历 ResourceElement 并调用其 inject() 方法。

代码语言:javascript
复制
public class InjectionMetadata {
    public void inject(Object target, String beanName, 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);
            }
        }
    }
}

ResourceElement 继承自LookupElement,LookupElement 又继承自InjectedElement;从 InjectionMetadata 中 inject() 方法可以看出一些端倪:InjectedElement 肯定抽象了整体的注入逻辑,留一些拓展给子类覆盖,这是老套路了。

继续跟进到 InjectedElement 中的 inject() 方法。

代码语言:javascript
复制
public abstract static class InjectedElement {
    protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
        if (this.isField) {
            Field field = (Field) this.member;
            ReflectionUtils.makeAccessible(field);
            field.set(target, getResourceToInject(target, requestingBeanName));
        } else {
            try {
                Method method = (Method) this.member;
                ReflectionUtils.makeAccessible(method);
                method.invoke(target, getResourceToInject(target, requestingBeanName));
            } catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }

    protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
        return null;
    }
}

没错,InjectedElement 最终是通过反射机制来实现依赖注入的。getResourceToInject()方法用于获取所需要的依赖,InjectedElement 并没有为该方法提供具体的实现,需要由其子类来覆盖。关于 InjectedElement 的继承关系如下图所示。

现在!是时候到 ResourceElement 中一探究竟了。

代码语言:javascript
复制
private class ResourceElement extends LookupElement {

    private final boolean lazyLookup;

    public ResourceElement(Member member, AnnotatedElement ae, 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 = StringUtils.uncapitalizeAsProperty(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());
    }

    @Override
    protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
        return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
                getResource(this, requestingBeanName));
    }
}

ResourceElement 构造方法内有两处逻辑值得大家关注:

  • 如果 @Resource 注解的 name 属性值为 EMPTY STRING (默认值),那么 ResourceElement 实例中isDefaultName属性值为 true,进一步表明 ResourceElement 实例中name属性值要么是成员变量名称,亦或是转换后的 setter 方法名称,具体是通过 StringUtils.uncapitalizeAsProperty(resourceName.substring(3)) 转换的;另外,我们还看到了EmbeddedValueResolver的身影,这意味着 @Resource 注解的 name 属性值是可以使用占位符的。
  • 如果 @Resource 注解的 type 属性值为 Object.class (默认值),则 ResourceElement 实例中lookupType属性值要么是成员变量所对应的 Class,亦或是 setter 方法参数所对应的 Class;如果 @Resource 注解的 type 属性值非默认值,则 ResourceElement 实例中lookupType属性值直接就是 Resource 注解中 type 属性所指定的了。

此外,ResourceElement 中的 getResourceToInject() 方法具体会委派 CommonAnnotationBeanPostProcessor 的 getResource() 方法来获取具体的依赖。

代码语言:javascript
复制
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
        implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {

    protected Object getResource(LookupElement element, String requestingBeanName) throws NoSuchBeanDefinitionException {
        return autowireResource(this.resourceFactory, element, requestingBeanName);
    }

    protected Object autowireResource(BeanFactory factory, LookupElement element, String requestingBeanName) throws NoSuchBeanDefinitionException {
        Object resource;
        Set<String> autowiredBeanNames;
        String name = element.name;

        if (factory instanceof AutowireCapableBeanFactory autowireCapableBeanFactory) {
            // new LookupDependencyDescriptor((Field) this.member, this.lookupType)
            // or
            // new LookupDependencyDescriptor((Method) this.member, this.lookupType)
            DependencyDescriptor descriptor = element.getDependencyDescriptor();
            if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
                autowiredBeanNames = new LinkedHashSet<>();
                // 核心逻辑
                resource = autowireCapableBeanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
            } else {
                // 核心逻辑
                resource = autowireCapableBeanFactory.resolveBeanByName(name, descriptor);
                autowiredBeanNames = Collections.singleton(name);
            }
        } else {
            resource = factory.getBean(name, element.lookupType);
            autowiredBeanNames = Collections.singleton(name);
        }

        if (factory instanceof ConfigurableBeanFactory configurableBeanFactory) {
            for (String autowiredBeanName : autowiredBeanNames) {
                if (requestingBeanName != null && configurableBeanFactory.containsBean(autowiredBeanName)) {
                    configurableBeanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
                }
            }
        }

        return resource;
    }
}

上述 autowireResource() 方法交代了两个极为重要的知识点:

  • 如果当前 ResourceElement 实例的isDefaultName属性值为 true 且 ResourceElement 实例的name属性值在一级缓存 singletonObjects 以及 beanDefinitionMap 中不存在,那么通过 AutowireCapableBeanFactory 的resolveDependency()方法来获取依赖。这说明需要根据 ResourceElement 实例中的 lookupType 来加载所需依赖 ,因为根据 name 来完成依赖的加载是不可能的了。
  • 如果如果当前 ResourceElement 实例的isDefaultName属性值为 true,且 ResourceElement 实例的name属性值在一级缓存 singletonObjects 以及 beanDefinitionMap 中存在;或者当前 ResourceElement 实例的isDefaultName属性值为 false,那么则委派 AutowireCapableBeanFactory 的resolveBeanByName()方法来拿到所需的依赖。这说明直接根据 ResourceElement 实例中的 name 来获取所需依赖即可 。

下面分小节对 AutowireCapableBeanFactory 中的 resolveDependency() 方法与 resolveBeanByName() 方法进行分析。

1.1 resolveDependency()

resolveDependency() 方法具体会委派doResolveDependency()方法去干活。

代码语言:javascript
复制
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
                                      Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            Object shortcut = descriptor.resolveShortcut(this);
            if (shortcut != null) {
                return shortcut;
            }
            // descriptor 实际为 LookupDependencyDescriptor 类型
            // descriptor.getDependencyType() 就是 ResourceElement 实例中的 lookupType
            Class<?> type = descriptor.getDependencyType();
            Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
            if (value != null) {
                if (value instanceof String strValue) {
                    String resolvedValue = resolveEmbeddedValue(strValue);
                    BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                            getMergedBeanDefinition(beanName) : null);
                    value = evaluateBeanDefinitionString(resolvedValue, bd);
                }
                TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
                try {
                    return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
                } catch (UnsupportedOperationException ex) {
                    return (descriptor.getField() != null ?
                            converter.convertIfNecessary(value, type, descriptor.getField()) :
                            converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
                }
            }
            // lookupType 如果为 Array、Collection 或 Map
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }
            // lookupType 为简单的引用类型,执行到这里说明所需要的依赖是单个 Bean,不会是集合之类的
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
            if (matchingBeans.isEmpty()) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                return null;
            }

            String autowiredBeanName;
            Object instanceCandidate;
            // 如果根据 lookupType 获取到多个依赖 Bean,那么需要根据  @Primary 和 @Priority 过滤
            // 确保最终只有一个依赖 Bean
            if (matchingBeans.size() > 1) {
                autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                if (autowiredBeanName == null) {
                    if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                        return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                    } else {
                        return null;
                    }
                }
                instanceCandidate = matchingBeans.get(autowiredBeanName);
            } else {
                // We have exactly one match.
                Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
                autowiredBeanName = entry.getKey();
                instanceCandidate = entry.getValue();
            }

            if (autowiredBeanNames != null) {
                autowiredBeanNames.add(autowiredBeanName);
            }
            if (instanceCandidate instanceof Class) {
                instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
            }
            Object result = instanceCandidate;
            if (result instanceof NullBean) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                result = null;
            }
            if (!ClassUtils.isAssignableValue(type, result)) {
                throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
            }
            return result;
        } finally {
            ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
        }
    }
}

敲黑板!!!从上述内容来看,doResolveDependency() 方法极度依赖一个方法,它就是findAutowireCandidates()。findAutowireCandidates() 方法是干嘛的呢?它可以根据Class<?>类型的参数 requiredType 来匹配出所有的 beanName,然后通过 beanName 来加载 Bean 实例,最后封装为一个Map<String, Object>类型的结果;在该 Map 中,key 为 beanName、value 为 Bean 实例。至于 findAutowireCandidates() 的内容,需要大家自行去阅读源码了。

如何根据Class<?>类型的参数 requiredType 来匹配出所有的 beanName ?

代码语言:javascript
复制
String[] candidateNames 
        = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());

根据 requiredType 只是匹配到所有的 beanName,还不是 Bean 实例,那么 Bean 实例是如何加载出来的?

代码语言:javascript
复制
public class DependencyDescriptor extends InjectionPoint implements Serializable {
    public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
            throws BeansException {
        return beanFactory.getBean(beanName);
    }
}

可见,resolveDependency() 方法先是根据 lookupType 来拿到所需要的依赖名称,然后根据名称加载出真正的依赖。

1.2 resolveBeanByName()

相较于 resolveDependency() 方法,resolveBeanByName() 逻辑可就直白多了,直接根据 ResourceElement 实例中的 name 来加载 Bean。

代码语言:javascript
复制
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
    @Override
    public Object resolveBeanByName(String name, DependencyDescriptor descriptor) {
        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            // descriptor 实际为 LookupDependencyDescriptor 类型
            // descriptor.getDependencyType() 就是 ResourceElement 实例中的 lookupType
            // name 就是 ResourceElement 实例中的 name
            return getBean(name, descriptor.getDependencyType());
        } finally {
            ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
        }
    }
}

在开始加载由 @Resource 注解标识的依赖之前,会先走factory.containsBean(name)这个逻辑,以决定究竟是基于 byType 策略,还是基于 byName 策略去加载特定依赖。如果 factory.containsBean(name) 为 true,那肯定可以根据 name 加载出所需的依赖,这样最省事,否则不得不根据 type 来加载了。正因为是先走了 factory.containsBean(name) 这一逻辑,所以说 @Resource 注解是优先基于 byName 策略来加载依赖,然后才是 byType 策略!

2 @Autowired

在 AutowiredAnnotationBeanPostProcessor 中,postProcessProperties() 方法负责为 Bean 实例内由 @Autowired 注解标识的 成员变量、setter 方法 和 构造方法 注入依赖。

代码语言:javascript
复制
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
        MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware {
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            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;
    }
}

InjectionMetadata 在第一章节已经介绍过,这里直接去 AutowiredElement 中找 getResourceToInject() 方法。在 AutowiredElement 的两个实现类AutowiredFieldElementAutowiredMethodElement中并没有发现 getResourceToInject() 方法的身影,因为它俩直接覆盖了 inject() 方法。本文这里选取更为常用的 AutowiredFieldElement 作为分析目标。

代码语言:javascript
复制
private class AutowiredFieldElement extends AutowiredAnnotationBeanPostProcessor.AutowiredElement {

    public AutowiredFieldElement(Field field, boolean required) {
        super(field, null, required);
    }

    @Override
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        Field field = (Field) this.member;
        Object value = resolveFieldValue(field, bean, beanName);
        if (value != null) {
            ReflectionUtils.makeAccessible(field);
            field.set(bean, value);
        }
    }

    @Nullable
    private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        Object value;
        try {
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        } catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        return value;
    }
}

在 AutowiredFieldElement 内,resolveFieldValue() 方法承担了加载依赖的重任,其内部实现依然是委托 resolveDependency() 方法,这个方法在上一章节已经介绍过了。

@Resource 注解是优先基于 byType 策略来加载出依赖的名称,然后基于 byName 策略来加载出真正的依赖!

3 最后

建议大家自行阅读 resolveDependency() 方法内关于 泛型 Bean 的注入逻辑。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿杜小头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 @Resource
    • 1.1 resolveDependency()
      • 1.2 resolveBeanByName()
      • 2 @Autowired
      • 3 最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档