Spring源码分析(十三)依赖注入源码解析5:@Resource

本文主要解析@Resource注解的注入原理。其实现类是CommonAnnotationBeanPostProcessor,分解析注入点和注入点注入两步。注入时先验证type类型与注入点类型是否匹配,再获取注入对象,遵循先byName再byType原则。@Resource由java规范提供,Spring支持它可实现框架无缝替换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
@Autowired的实现类是AutowiredAnnotationBeanPostProcessor
@Resource的实现类是CommonAnnotationBeanPostProcessor,也是分两个步骤

  • 解析注入点
  • 注入点注入

CommonAnnotationBeanPostProcessor

找注入点

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
    //找注入点
    InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
    metadata.checkConfigMembers(beanDefinition);
}

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#findResourceMetadata

private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // Quick check on the concurrent map first, with minimal locking.
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                //构建注入点
                metadata = buildResourceMetadata(clazz);
                this.injectionMetadataCache.put(cacheKey, metadata);
            }
        }
    }
    return metadata;
}
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
    if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
        return InjectionMetadata.EMPTY;
    }

    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    Class<?> targetClass = clazz;

    do {
        final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
    	//遍历字段
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            //@WebServiceRef
            if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {...}
            //@EJB
            else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {...}
            //@Resource
            else if (field.isAnnotationPresent(Resource.class)) {
                //@Autowired遇到静态字段不会抛异常,但是@Resource会
                if (Modifier.isStatic(field.getModifiers())) {
                    throw new IllegalStateException("@Resource annotation is not supported on static fields");
                }
                if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
                    currElements.add(new ResourceElement(field, field, null));
                }
            }
        });
    	//遍历方法
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
            if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                return;
            }
            if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                //@WebServiceRef
                if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {...}
                //@EJB
                else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {...}
                //@Resource
                else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
                    //@Autowired遇到静态方法不会抛异常,但是@Resource会
                    if (Modifier.isStatic(method.getModifiers())) {
                        throw new IllegalStateException("@Resource annotation is not supported on static methods");
                    }
                    //@Resource标记的方法,要求方法参数必须有且只有1个
                    Class<?>[] paramTypes = method.getParameterTypes();
                    if (paramTypes.length != 1) {
                        throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
                    }
                    if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new ResourceElement(method, bridgedMethod, pd));
                    }
                }
            }
        });

        elements.addAll(0, currElements);
        targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);

    return InjectionMetadata.forElements(elements, clazz);
}

@Resource注解,标记的字段、方法注入点类型都是:ResourceElement(其中方法要求入参必须有且只有一个)
且相比于@Autowired,@Resource遇到静态字段、方法会抛异常。

注入点注入

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
    }
    return pvs;
}

org.springframework.beans.factory.annotation.InjectionMetadata#inject

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate =
            (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
        //遍历每个注入点进行依赖注入
        for (InjectedElement element : elementsToIterate) {
            //此时InjectedElement就是ResourceElement
            element.inject(target, beanName, pvs);
        }
    }
}

ResourceElement的inject方法,并没有自己实现,用的是其父类的
org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject

    protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
            throws Throwable {

        if (this.isField) {
            Field field = (Field) this.member;
            ReflectionUtils.makeAccessible(field);
            //反射赋值
            //getResourceToInject找到注入的值
            field.set(target, getResourceToInject(target, requestingBeanName));
        }
        else {
            if (checkPropertySkipping(pvs)) {
                return;
            }
            try {
                Method method = (Method) this.member;
                ReflectionUtils.makeAccessible(method);
                //反射赋值
                //getResourceToInject找到注入的值
                method.invoke(target, getResourceToInject(target, requestingBeanName));
            }
            catch (InvocationTargetException ex) {...}
        }
    }

getResourceToInject实现在ResourceElement自己
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject

@Resource可以指定name和type属性
在这里插入图片描述

private class ResourceElement extends LookupElement {

    private final boolean lazyLookup;

    //看getResourceToInject方法之前,先看下构造做了什么:
    //member:字段或方法
    public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
        super(member, pd);
        Resource resource = ae.getAnnotation(Resource.class);
        //@Resource注解上的name和type属性
        String resourceName = resource.name();
        Class<?> resourceType = resource.type();

        //使用@Resource时没有指定具体的name,那么isDefaultName=true
        //代表使用默认的名字,默认的名字就是field的name,或setXxx()中的xxx
        this.isDefaultName = !StringUtils.hasLength(resourceName);
        if (this.isDefaultName) {
            //field的name
            resourceName = this.member.getName();
            if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
                //方法的话,取setXxx()中的xxx
                resourceName = Introspector.decapitalize(resourceName.substring(3));
            }
        }
        //指定了具体的name,则进行占位符填充
        else if (embeddedValueResolver != null) {
            resourceName = embeddedValueResolver.resolveStringValue(resourceName);
        }
    	//@Resource除开可以指定name,还可以指定type,type默认为Object
        //!=Object.class 相当于自己定义了type
        if (Object.class != resourceType) {
            //1.如果指定了type,则验证一下和field的类型 或 set方法的第一个参数类型是否和所指定的resourceType匹配
            checkResourceType(resourceType);
        }
        else {
            // No resource type specified... check field/method.
            // 没有指定的话自动获取类型
            resourceType = getResourceType();
        }
        this.name = (resourceName != null ? resourceName : "");
        //lookupType、mappedName和ejb有关
        this.lookupType = resourceType;
        String lookupValue = resource.lookup();
        this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
        //也支持懒加载,判断字段或方法上有没有@Lazy注解
        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) :
                //2.获取需要注入的对象
                getResource(this, requestingBeanName));
    }
}

创建一个代理对象注入,和@Autowired差不多
在这里插入图片描述

1. 验证type指定的类型和注入点的类型是否匹配

org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#checkResourceType

protected final void checkResourceType(Class<?> resourceType) {
    if (this.isField) {
        Class<?> fieldType = ((Field) this.member).getType();
        if (!(resourceType.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(resourceType))) {
            throw new IllegalStateException("Specified field type [" + fieldType +
                    "] is incompatible with resource type [" + resourceType.getName() + "]");
        }
    }
    else {
        //注意,方法的时候,只会和方法第一个入参类型进行判断(事先校验过了,方法只允许有且只有1个参数)
        Class<?> paramType =
                (this.pd != null ? this.pd.getPropertyType() : ((Method) this.member).getParameterTypes()[0]);
        if (!(resourceType.isAssignableFrom(paramType) || paramType.isAssignableFrom(resourceType))) {
            throw new IllegalStateException("Specified parameter type [" + paramType +
                    "] is incompatible with resource type [" + resourceType.getName() + "]");
        }
    }
}

2. 获取需要注入的对象

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
        throws NoSuchBeanDefinitionException {

    // JNDI lookup to perform?
    String jndiName = null;
    if (StringUtils.hasLength(element.mappedName)) {...}
    else if (this.alwaysUseJndiLookup) {...}
    if (jndiName != null) {...}

    // Regular resource autowiring
    if (this.resourceFactory == null) {...}
    //根据LookupElement从BeanFactory找到合适的Bean对象
    return autowireResource(this.resourceFactory, element, requestingBeanName);
}
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
        throws NoSuchBeanDefinitionException {

    Object resource;
    Set<String> autowiredBeanNames;
    String name = element.name;

    //判断工厂是不是支持自动注入的工厂,默认Bean工厂就是实现这个接口的
    if (factory instanceof AutowireCapableBeanFactory) {
        AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
        DependencyDescriptor descriptor = element.getDependencyDescriptor();

        //假设@Resource中没有指定name,并且field的name 或 setXxx()的xxx不存在对应的Bean
        //那么则根据field类型或方法参数类型从BeanFactory中查找
        
        //fallbackToDefaultTypeMatch:回退到默认类型匹配,默认true
        //element.isDefaultName:是否是默认的名字,@Resource中没有指定name,则是true
        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 {
            //2.1 根据名字解析Bean,底层也是getBean
            resource = beanFactory.resolveBeanByName(name, descriptor);
            autowiredBeanNames = Collections.singleton(name);
        }
    }
    else {
        //直接根据名字获取Bean
        resource = factory.getBean(name, element.lookupType);
        autowiredBeanNames = Collections.singleton(name);
    }

    if (factory instanceof ConfigurableBeanFactory) {
        ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
        for (String autowiredBeanName : autowiredBeanNames) {
            if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
                //记录requestingBeanName依赖autowiredBeanName
                //为给定bean注册一个依赖bean,以便在销毁给定bean之前销毁。
                beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
            }
        }
    }

    return resource;
}
  • @Resource中没有指定name并且默认的name(字段名 或 setXxx的xxx名)工厂中没有对应的Bean,才会根据类型找Bean
  • @Resource如果指定了name,只会根据name找Bean

所以@Resource注解是先byName再byType

2.1 根据名字找Bean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName

public Object resolveBeanByName(String name, DependencyDescriptor descriptor) {
    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        //getBean
        return getBean(name, descriptor.getDependencyType());
    }
    finally {
        ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
}

总结

在这里插入图片描述

  • 如果没有指定name,先根据属性、方法的名字找Bean,找不到,再根据类型去找
  • 如果指定了name,只会根据名字找Bean
  • @Resouce注解不是Spring提供的,java规范提供的。
    Spring支持@Resouce的意义:能让任何支持@Resouce注解的框架,都能无缝替换Spring,源码不用改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

犬豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值