环境:Spring5.3.23
源码解读:如何正确使用@Resource和@Autowired注解?
1.注解区别
@Resource 和 @Autowired 都可以用于,依赖注入。但它们之间存在一些明显的区别。
1.提供方:
- @Autowired 是 Spring 提供的注解。
- @Resource 是 JDK提供的注解。
2.装配方式:
- @Autowired 默认按类型装配,即默认情况下必须要求依赖对象存在。如果要允许 null 值,可以设置它的 required 属性为
false。如果想使用名称装配可以结合 @Qualifier 注解进行使用。 - @Resource 默认按照名称进行装配,名称可以通过 name 属性进行指定。如果没有指定 name
属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在 setter 方法上默认取属性名进行装配。当找不到与名称匹配的 bean时才按照类型进行装配。
综合来看,@Resource 和 @Autowired 在提供方和装配方式上存在明显的区别。
2.源码分析
这里都将以字段注入的方式分析。
@Resource注解
该注解的处理器是CommonAnnotationBeanPostProcessor。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 获取所有使用@Resource注解的字段,InjectionMetadata包含了一个List集合InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);metadata.inject(bean, beanName, pvs);return pvs;
}
InjectionMetadata注入核心类
/*** InjectionMetadata类* target 待注入的实例对象 * beanName 当前Bean的名称* pvs 因为是基于字段注入,所以这里没有用*/
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> elementsToIterate = ...for (InjectedElement element : elementsToIterate) {// 注入;这里当前的element实例是ResourceElement, 这里是调用父类InjectedElement方法element.inject(target, beanName, pvs);}
}
public abstract static class InjectedElement {protected void inject(...) {// 基于字段注入if (this.isField) {Field field = (Field) this.member;ReflectionUtils.makeAccessible(field);// getResourceToInject获取实例field.set(target, getResourceToInject(target, requestingBeanName));}}
}
private class ResourceElement extends LookupElement {public ResourceElement() {Resource resource = ae.getAnnotation(Resource.class);// 是否指定了name属性;指定要注入的beanNameString resourceName = resource.name();Class<?> resourceType = resource.type();// 如果设置了name,则为false,否则使用字段名,也就是truethis.isDefaultName = !StringUtils.hasLength(resourceName);if (this.isDefaultName) {// 获取字段名resourceName = this.member.getName();// ...}// ...this.name = (resourceName != null ? resourceName : "");Lazy lazy = ae.getAnnotation(Lazy.class);// 字段上是否使用了@Lazy注解,我们这里不考虑@Lazy情况this.lazyLookup = (lazy != null && lazy.value());}protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {// 字段没有添加@Lazy注解,所以为false,执行elsereturn (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :// 执行这里getResource方法getResource(this, requestingBeanName));}
}
接下来进入到CommonAnnotationBeanPostProcessor中getResource方法
public class CommonAnnotationBeanPostProcessor {protected Object getResource(LookupElement element, @Nullable String requestingBeanName) {// ...return autowireResource(this.resourceFactory, element, requestingBeanName); }protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) {String name = element.name;if (factory instanceof AutowireCapableBeanFactory) {AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;// 默认如果@Resource没有指定name属性,所以这里的name为字段名// factory.containsBean(name) 判断当前容器中是否有以该字段为名的 Bean// 不存在则进入;先按照名称匹配,如果不存在则进入if,if中则按照类型查找if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {autowiredBeanNames = new LinkedHashSet<>();// 这里的逻辑就是按照当前字段类型在容器中查找Beanresource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}} else {// 如果容器中存在字段名的Bean,则以beanName在容器中查找Beanresource = beanFactory.resolveBeanByName(name, descriptor);autowiredBeanNames = Collections.singleton(name);}}}
}
以上就是@Resource注解的原理
@Autowired注解
该注解的处理器是AutowiredAnnotationBeanPostProcessor。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 获取所有使用@Autowired注解的字段,InjectionMetadata包含了一个List集合InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);// 进入InjectionMetadata#inject方法metadata.inject(bean, beanName, pvs);return pvs;
}
/*** InjectionMetadata* target 待注入的实例对象 * beanName 当前Bean的名称* pvs 因为是基于字段注入,所以这里没有用*/
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> elementsToIterate = ...for (InjectedElement element : elementsToIterate) {// 注入;这里当前的element实例是AutowiredFieldElement, 这里是调用父类InjectedElement方法element.inject(target, beanName, pvs);}
}
进入AutowiredFieldElement#inject方法
private class AutowiredFieldElement {protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;// 首次cached=falseif (this.cached) {// ...} else { // 解析字段获取bean对象value = resolveFieldValue(field, bean, beanName);}if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}}private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());try {// 进入DefaultListableBeanFactory方法value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}}
}
DefaultListableBeanFactory
public class DefaultListableBeanFactory {public Object resolveDependency(DependencyDescriptor descriptor, ...) {// ...Object result = ...result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);return result}public Object doResolveDependency(DependencyDescriptor descriptor, ...) {// 该方法中就会按照类型进行查找相应的bean。// 当有多个相同类型的bean时会调用下面的方法进行处理if (matchingBeans.size() > 1) {// 存在多个相同类型时,进行处理autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);}}protected String determineAutowireCandidate() {// 有没有@Primary注解String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);if (primaryCandidate != null) {return primaryCandidate;}// 有没有@Priority注解,值越小,优先级越高String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);if (priorityCandidate != null) {return priorityCandidate;}// Fallback 回退处理,如果以上情况都不存在则按照名称匹配for (Map.Entry<String, Object> entry : candidates.entrySet()) {String candidateName = entry.getKey();Object beanInstance = entry.getValue();if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||// 按照名称匹配matchesBeanName(candidateName, descriptor.getDependencyName())) {return candidateName;}}return null;}
}
到这里你应该清楚了@Resource@Autowired注入的区别了,自身再通过源码走一遍流程,以后就不用在死记硬背这些东西了。