Spring依赖注入之@Resource注解

@Resource注解

类似spring中的@Autowired注解,这里也会有一个后置处理器。

对于CommonAnnotationBeanPostProcessor来说,首先在类加载的时候

private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = new LinkedHashSet<>(4);

static {
    webServiceRefClass = loadAnnotationType("javax.xml.ws.WebServiceRef");
    ejbClass = loadAnnotationType("javax.ejb.EJB");

    resourceAnnotationTypes.add(Resource.class);
    if (webServiceRefClass != null) {
        resourceAnnotationTypes.add(webServiceRefClass);
    }
    if (ejbClass != null) {
        resourceAnnotationTypes.add(ejbClass);
    }
}

就已经开始往里面来添加一个注解@Resource。

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;
}

寻找注入点

看看spring是如何来处理@Resource的。

metadata = buildResourceMetadata(clazz);

@Autowired和@Resource区别

和@Autowired逻辑类似,但是有一个区别:

if (Modifier.isStatic(field.getModifiers())) {
    throw new IllegalStateException("@Resource annotation is not supported on static fields");
}

if (Modifier.isStatic(method.getModifiers())) {
    throw new IllegalStateException("@EJB annotation is not supported on static methods");
}

如果类中的属性是static关键字并且添加了@Resource注解,那么这里直接报错。

而对于@Autowired来说,是直接返回null的。

ResourceElement构造函数

最终发现,找到了在方法上和属性上添加了@Resource注解了之后,形成了一个对象ResourceElement。

看看构造方法:

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();
    
    // 是否是使用默认的名字
    this.isDefaultName = !StringUtils.hasLength(resourceName);
    if (this.isDefaultName) {
        resourceName = this.member.getName();
        // set方法来做处理
        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());
}

构造中主要是用@Resource中的name和type属性来做一个判断,以及是否有@Lazy注解去生成代理对象等等。

注入

那么直接看下怎么进行注入的即可。

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

那么直接正常不会加@Lazy,那么直接看下getResource方法:

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
    throws NoSuchBeanDefinitionException {
    ...........
        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;
	
    // 来到这里
    if (factory instanceof AutowireCapableBeanFactory) {
        AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
        DependencyDescriptor descriptor = element.getDependencyDescriptor();
        if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
            autowiredBeanNames = new LinkedHashSet<>();
            resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
            if (resource == null) {
                throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
            }
        }
        else {
            resource = beanFactory.resolveBeanByName(name, descriptor);
            autowiredBeanNames = Collections.singleton(name);
        }
    }
    else {
        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)) {
                beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
            }
        }
    }

    return resource;
}

那么直接看核心方法:

if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
    autowiredBeanNames = new LinkedHashSet<>();
    resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
    if (resource == null) {
        throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
    }
}
else {
    resource = beanFactory.resolveBeanByName(name, descriptor);
    autowiredBeanNames = Collections.singleton(name);
}

这里就是@Resource注解的核心:先根据名字,然后根据类型。

那么原理是什么呢?

重点是:fallbackToDefaultTypeMatch=TRUE,表示的是如果根据名字从factory中查找得到bean没有找到的时候,需要类型来查找。

因为在判断factory.containsBean(name),是否有这个名字的bean的时候,如果没有找到,下面会根据类型查找。

如果包含了,那么直接根据名字找到对应的bean来进行赋值。通俗易懂

posted @ 2022-08-27 13:32  写的代码很烂  阅读(127)  评论(0编辑  收藏  举报