@Autowied和@Resource的区别——Autowired根据泛型类型注入

注入方式的区别

@Autowired:默认使用byType的方式注入spring bean
@Qualifier:默认使用byName的方式注入spring bean
@Resource:默认使用byName的方式注入bean,如果没有找到,再去byType。

@Resource看起来功能完全覆盖了Spring提供的注解,而且不局限于spring的容器,更易于扩展(虽然现在基本都是spring的容器),这看起来比spring提供的那两个货要强,所以为什么还要使用上边那两个货呢?
byName还不知道,但是byType可以给出个答案了。

答案是:@Autowired在byType的时候会连同泛型一起解析,而@Resource不支持解析泛型类型。

这个答案是在项目中开发遇到的,下面会举个简化了代码的例子。

排错过程

代码示例

代码结构图

在这里插入图片描述
通过这个图可以看出,每一个BO的实体类都会在build层和query层有一个唯一的处理类,所以可以使用byType的方式,在AbstractMetaBuild中组合IQuery,然后根据AbstractMetaBuild子类实现的泛型获取IQuery的bean。
说起来比较抽象,看下代码。

BO
@Data
public class PetBO {
    private String name;

    private Integer age;
    public PetBO(String name) {
        this.name = name;
    }
}
@Data
public class UserBO {
    private String name;

    private Integer age;
    public UserBO(String name) {
        this.name = name;
    }
}
Query层
public interface IQuery<T> {
    T query();
}
@Component
public class PetQuery implements IQuery<PetBO> {
    @Override
    public PetBO query() {
        return new PetBO("小白");
    }
}
@Component
public class UserQuery implements IQuery<UserBO> {
    @Override
    public UserBO query() {
        return new UserBO("明明");
    }
}
Build层

第三行通过泛型的类型在父类里注入了IQuery,T由子类确认唯一的类型。所以在理想情况下,PetMetaBuild这个子类中会自动注入PetQuery这个类型的bean。

public abstract class AbstractMetaBuild<T> {
    @Resource
    private IQuery<T> iQuery;
    public T build(){
        T t = iQuery.query();
        buildOther(t);
        return null;
    }
    /**
     * 构建其他信息
     * @param t 模型BO
     */
    public abstract void buildOther(T t);
}

@Component
public class PetMetaBuild extends AbstractMetaBuild<PetBO> {
    @Override
    public void buildOther(PetBO t) {
        t.setAge(2);
    }
}
@Component
public class UserMetaBuild extends AbstractMetaBuild<UserBO> {
    @Override
    public void buildOther(UserBO userBO) {
        userBO.setAge(10);
    }
}

错误结果

在这里插入图片描述
重点错误信息

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'petMetaBuild": Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.chongsan.web.inject.query.IQuery<?>’ available: expected single matching bean but found 2: petQuery,userQuery

翻译翻译,什么** 叫**的惊喜
惊喜就是,在创建子类:PetMetaBuild的bean的时候,尝试注入com.chongsan.web.inject.query.IQuery<?>类型的bean,但是找到了俩,分别是petQueryuserQuery,懵逼了,不知道注哪个了。

但是,当把第二行的@Resource换成@Autowired

public abstract class AbstractMetaBuild<T> {
    @Autowired
    private IQuery<T> iQuery;
    public T build(){
        T t = iQuery.query();
        buildOther(t);
        return null;
    }
    /**
     * 构建其他信息
     * @param t 模型BO
     */
    public abstract void buildOther(T t);
}

在这里插入图片描述
这个惊喜有点**的难翻译了。

根据异常栈debug下

Resource注入分析

点击上一级的栈帧
抛出异常
在debug页面查看getBeanPostProcessorCache().instantiationAware,一共有四个子类,看名字应该分别是

  1. configuration注入
  2. AspectJ切面注入
  3. 通用注入
  4. autowired注入

由此可以推测,每次注入bean都会尝试用这四种解析器循环执行,直到有成功的,责任链模式。
在这里插入图片描述

栈帧的栈顶就是子类,所以知道是CommonAnnotationBeanPostProcessor这个类抛出的异常。

  • 可以用debug重新执行一下CommonAnnotationBeanPostProcessor这个类的栈帧,看看究竟是哪里的原因抛出了异常。
  • 也可以直接看throw的异常栈信息,由于外层有捕获,所以和在控制台看到的异常信息不一样。

来到真正抛异常的上层DefaultListableBeanFactory,可以发现注入bean的过程是

  1. 发现了两个IQuery的子类
  2. 判断注入其中的哪一个,return的是null

问题就出在了第二步,没有判断出来应该注入哪个bean
在这里插入图片描述
判断注入也分三步

  1. 基础数据类型(我想注入的是PetQuery,所以肯定不是)
  2. 根据优先级注入(没有设置bean的优先级)
  3. 先判断缓存中有没有这个类 || byName(前边的条件可以根据下方debug的数据判断出缓存没有PetQuery。byName也是失败的,变量名为iQuery,而自动解析出来的是petQuery)
    在这里插入图片描述
    最后返回了空
    在这里插入图片描述
    看了上边查找唯一bean的所有操作,发现都没有对泛型类型的解析,所以可以得出结论:

抛异常的原因是因为@Resource使用的是CommonAnnotationBeanPostProcessor获取bean,其中的核心代码在DefaultListableBeanFactory中,在有多个bean的情况下需要决定唯一的bean并返回,但是DefaultListableBeanFactory并不支持泛型类型的解析,所以无法获取到唯一bean,故抛出异常。

Autowired注入分析

知道了@Resource不能注入的原因,也看下@Autowired是怎么处理这种情况的

先更换注解
在这里插入图片描述

再从责任链的源头打个条件注解,只看petMetaBuild这个bean的注入
在这里插入图片描述
然后直接进入AutowiredAnnotationBeanPostProcessor一步步查看注入的方式
此处省略n多步,关键代码为org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver#checkGenericTypeMatch
最后的return判断的是IQuery<PetBO>能否转换成PetQuery
在这里插入图片描述

执行发现是true
在这里插入图片描述
到此已经找到了PetQuery,由此得出结论

@Autowired使用的是AutowiredAnnotationBeanPostProcessor获取bean,其中的核心代码在GenericTypeAwareAutowireCandidateResolver中,这个类可以根据泛型的信息判断需要注入的是具体是哪个bean,在byType上比@Resource更强大

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Resource和@Autowired是用来实现依赖注入的注解,在Spring/Spring Boot项目中使用。它们有以下几点区别: 1. 来源不同:@Autowired来自Spring框架,而@Resource来自Java的JSR-250标准。 2. 依赖查找的顺序不同:@Autowired先根据类再根据名称查询,而@Resource先根据名称再根据类查询。 3. 支持的参数不同:@Autowired只支持设置1个参数,而@Resource支持设置7个参数。 4. 依赖注入的用法支持不同:@Autowired既支持构造方法注入,又支持属性注入和Setter注入,而@Resource只支持属性注入和Setter注入。 5. 编译器IDEA的提示不同:当注入Mapper对象时,使用@Autowired注解编译器会提示错误,而使用@Resource注解则不会提示错误。\[1\] 总结来说,@Autowired和@Resource在依赖注入的实现方式、参数设置和编译器提示等方面有所不同。具体使用哪个注解取决于项目需求和个人偏好。 #### 引用[.reference_title] - *1* *2* [面试突击:@Autowired 和 @Resource 有什么区别?你学会了吗?](https://blog.csdn.net/Candyz7/article/details/126578224)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [[spring学习]8、@Autowired和@Resource区别](https://blog.csdn.net/m0_51545690/article/details/125244127)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值