@Resource是如何被spring所解析的?

1 个回答

  • 吴敏
    你爱你的玫瑰是因为你在你的玫瑰上花了太多的时间!
    ### 一.@Resource注解简单介绍 @Resource注解标注的属性默认按照ByName进行注入,由J2EE提供 如果我们想按照ByType注入,代码要这样写: ```java 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,例如: ```java public class LaController { //按类型注入 @Resource(type=La2ServiceImpl.class) private LaService laService; } ``` 这样就会注入La2ServiceImpl实例,就不会出现上面的报错了。 ### 二.注入源码详解 代码定位到 AbstractAutowireCapableBeanFactory类的populateBean方法 ```java //是否有实例化相关的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注解 ```java if(field.isAnnotationPresent(Resource.class)) ``` 如果条件成立,就会构建一个ResourceElement出来。我们来看下ResourceElement类 ```java 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方法,对属性进行赋值。我们看下代码片段: ```java if (this.isField) { Field field = (Field) this.member; ReflectionUtils.makeAccessible(field); field.set(target, getResourceToInject(target, requestingBeanName)); } ```
    发布于 2022-11-20 08:52 •IP属地 江西
    举报
    评论
    收藏1