环境: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属性;指定要注入的beanName
String resourceName = resource.name();
Class<?> resourceType = resource.type();
// 如果设置了name,则为false,否则使用字段名,也就是true
this.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,执行else
return (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<>();
// 这里的逻辑就是按照当前字段类型在容器中查找Bean
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
} else {
// 如果容器中存在字段名的Bean,则以beanName在容器中查找Bean
resource = 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=false
if (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注入的区别了,自身再通过源码走一遍流程,以后就不用在死记硬背这些东西了。