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

一.@Resource注解简单介绍

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

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

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

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

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

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

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

二.注入源码详解

代码定位到
AbstractAutowireCapableBeanFactory类的populateBean方法

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

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


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

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

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

if(field.isAnnotationPresent(Resource.class))
复制代码

如果条件成立,就会构建一个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 = 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());
   }

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

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

2.@Resource属性值注入

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

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

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

if (this.isField) {
   Field field = (Field) this.member;
   ReflectionUtils.makeAccessible(field);
   field.set(target, getResourceToInject(target, requestingBeanName));
}


 

你可能感兴趣的:(spring,java,spring,boot,后端,程序人生)