一文弄懂Spring源码之@Resource注解

时间:2025-04-11 11:27:35

一.@Resource注解简单介绍

@Resource注解标注的属性默认按照ByName进行注入,由J2EE提供

如果我们想按照ByType注入,代码要这样写:

public class LaController {
    //按类型注入
    @Resource(type=)
    private LaService  laService;
}
复制代码

如果LaService接口存在两个实现类,且两个实现类都会被spring扫描到,在注入的时候就会报错:nested exception is
: No qualifying bean of type '' available: expected single matching bean but found 2: la2ServiceImpl,laServiceImpl

type也可以是实现类class,例如:

public class LaController {
    //按类型注入
    @Resource(type=)
    private LaService  laService;
}
复制代码

这样就会注入La2ServiceImpl实例,就不会出现上面的报错了。

二.注入源码详解

代码定位到
AbstractAutowireCapableBeanFactory类的populateBean方法

//是否有实例化相关的BeanPostProcessor
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

if (hasInstAwareBpps || needsDepCheck) {
   if (pvs == null) {
      pvs = ();
   }
   PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, );
   if (hasInstAwareBpps) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            //调用处理属性值的方法
            pvs = (pvs, filteredPds, (), beanName);
            if (pvs == null) {
               return;
            }
         }
      }
   }
   if (needsDepCheck) {
      checkDependencies(beanName, mbd, filteredPds, pvs);
   }
}
复制代码


InstantiationAwareBeanPostProcessor有一个实现类叫做CommonAnnotationBeanPostProcessor,这个类会对@Resource标注的属性进行赋值。

1.找到所有的@Resource标注的属性

利用java反射,找到类中所有的Fields,然后判断属性上是否标记了@Resource注解

if(())
复制代码

如果条件成立,就会构建一个ResourceElement出来。我们来看下ResourceElement类

private class ResourceElement extends LookupElement {

   private final boolean lazyLookup;
   //构造函数
   public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
      super(member, pd);
      Resource resource = ();
      //属性名字
      String resourceName = ();
      //属性的类型
      Class<?> resourceType = ();
       = !(resourceName);
      if () {
         resourceName = ();
         if ( instanceof Method && ("set") && () > 3) {
            resourceName = ((3));
         }
      }
      else if (embeddedValueResolver != null) {
         resourceName = (resourceName);
      }
      if ( != resourceType) {
         checkResourceType(resourceType);
      }
      else {
         // No resource type specified... check field/method.
         resourceType = getResourceType();
      }
       = (resourceName != null ? resourceName : "");
       = resourceType;
      String lookupValue = ();
       = ((lookupValue) ? lookupValue : ());
      Lazy lazy = ();
       = (lazy != null && ());
   }

   @Override
   protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
      return ( ? buildLazyResourceProxy(this, requestingBeanName) :
            getResource(this, requestingBeanName));
   }
}
复制代码

最后会将这个类中所有标注有@Resource注解的属性构造出来的ResourceElement都放到LinkedList中。最终会包装出一个注入元数据(InjectionMetadata)对象出来。

2.@Resource属性值注入

既然我们拿到了所有@Resource属性,那么循环对这些属性注入就可以了。

属性注入核心原理=反射+getBean方法。

getBean方法获取Bean对象,然后调用Field的set方法,对属性进行赋值。我们看下代码片段:

if () {
   Field field = (Field) ;
   (field);
   (target, getResourceToInject(target, requestingBeanName));
}