Spring Framework @Autowired与@Resource注解

Spring 注入的那些事儿:@Autowired 与 @Resource 的爱恨情仇

各位观众,各位听众,欢迎来到“Java那些事儿”特别节目!我是你们的老朋友,人送外号“代码诗人”的程序猿老王。今天,我们要聊聊Spring框架中两个非常重要的注解:@Autowired@Resource

这两位可是Spring IoC容器中的“流量明星”,负责依赖注入的大梁。但是啊,明星也有明星的烦恼,总是免不了被拿来比较,被问“你俩到底谁更强?”。今天,老王就化身情感导师,来给大家八卦八卦这两位“明星”的恩怨情仇,顺便帮大家理清思路,看看在实际开发中,到底该如何选择它们。

一、开场白:依赖注入,一场命中注定的邂逅

首先,让我们简单回顾一下什么是依赖注入(Dependency Injection,简称DI)。 想象一下,你开了一家咖啡馆,需要用到咖啡豆、牛奶、糖等各种原材料。 你可以自己去采购,也可以让供应商直接把东西送到你手上。

自己采购的方式,就相当于你在代码中直接new一个对象,这叫做“硬编码”,耦合度很高。 如果你想换个牌子的咖啡豆,就得修改代码,重新编译部署,麻烦!

而让供应商送货上门,就相当于依赖注入。 你只需要告诉Spring容器你需要什么(通过注解或者XML配置),Spring就会帮你找到合适的“供应商”(bean),然后把“原材料”(依赖的对象)注入到你的咖啡馆(类)中。

这样一来,你的代码就变得更加灵活、可维护、可测试。 你想换个咖啡豆供应商? 没问题,修改一下Spring配置就行了,代码根本不用动! 这就是依赖注入的魅力,它让你的代码不再“硬邦邦”,而是像咖啡一样丝滑柔顺。

二、@Autowired:Spring 的亲儿子,按类型注入

@Autowired 注解是Spring框架自家提供的,它按照类型(by type)进行依赖查找和注入。 简单来说,就是Spring容器会在上下文中寻找与属性类型匹配的bean,然后自动注入。

我们来看一个例子:

@Component
public class CoffeeMaker {

    @Autowired
    private CoffeeBean coffeeBean;

    public void makeCoffee() {
        System.out.println("Using " + coffeeBean.getName() + " to make coffee.");
    }
}

@Component
public class CoffeeBean {

    private String name = "Arabica";

    public String getName() {
        return name;
    }
}

在这个例子中,CoffeeMaker 类需要用到 CoffeeBean 对象。 我们使用 @Autowired 注解在 coffeeBean 属性上,Spring容器就会自动找到 CoffeeBean 类型的bean,然后注入到 CoffeeMaker 中。

优点:

  • 简单易用: 只需要一个注解,Spring就能自动完成依赖注入。
  • 类型安全: 编译器会检查类型是否匹配,避免运行时错误。
  • 与Spring框架深度集成: 是Spring框架的核心特性之一,使用广泛。

缺点:

  • 当存在多个相同类型的bean时,会报错: 如果Spring容器中存在多个 CoffeeBean 类型的bean,Spring就不知道该注入哪一个,会抛出 NoUniqueBeanDefinitionException 异常。
  • 只能按类型注入: 不能指定bean的名称,如果需要按名称注入,需要配合 @Qualifier 注解。

解决多个相同类型bean的问题:

为了解决上述问题,我们可以使用 @Qualifier 注解来指定要注入的bean的名称。 比如:

@Component
public class CoffeeMaker {

    @Autowired
    @Qualifier("arabicaBean")
    private CoffeeBean coffeeBean;

    public void makeCoffee() {
        System.out.println("Using " + coffeeBean.getName() + " to make coffee.");
    }
}

@Component("arabicaBean")
public class ArabicaCoffeeBean extends CoffeeBean {
    // ...
}

@Component("robustaBean")
public class RobustaCoffeeBean extends CoffeeBean {
    // ...
}

在这个例子中,我们定义了两个 CoffeeBean 类型的bean:arabicaBeanrobustaBean。 使用 @Qualifier("arabicaBean") 注解,告诉Spring容器注入名为 arabicaBeanCoffeeBean 对象。

一个小总结:

特性 @Autowired @Qualifier
注入方式 按类型注入 配合 @Autowired,按名称注入
作用 自动查找并注入匹配类型的bean 指定要注入的bean的名称,解决多个相同类型bean的问题
使用场景 大部分情况下,只有一个匹配类型的bean存在 存在多个相同类型的bean,需要指定注入哪个bean

三、@Resource:JSR-250 规范,按名称注入为主

@Resource 注解来自于JSR-250规范(Java Specification Requests),它默认按照名称(by name)进行依赖查找和注入,如果没有指定名称,则按照类型(by type)进行查找。

我们来看一个例子:

@Component
public class CoffeeMaker {

    @Resource(name = "arabicaBean")
    private CoffeeBean coffeeBean;

    public void makeCoffee() {
        System.out.println("Using " + coffeeBean.getName() + " to make coffee.");
    }
}

@Component("arabicaBean")
public class ArabicaCoffeeBean extends CoffeeBean {
    // ...
}

在这个例子中,我们使用 @Resource(name = "arabicaBean") 注解,告诉Spring容器注入名为 arabicaBeanCoffeeBean 对象。

优点:

  • 默认按名称注入: 更符合我们的直觉,更容易理解和维护。
  • JSR-250规范: 具有更好的通用性和可移植性。
  • 可以指定注入名称: 可以直接通过 name 属性指定要注入的bean的名称,更加灵活。

缺点:

  • 不是Spring框架原生注解: 需要引入额外的依赖(javax.annotation-api)。
  • 类型安全性稍弱: 如果指定的bean名称不存在,运行时才会报错。
  • 按类型查找的优先级较低: 只有在没有指定名称,或者指定的名称找不到对应的bean时,才会按类型查找。

四、@Autowired vs @Resource:一场“三国演义”般的较量

现在,让我们把 @Autowired@Resource 放在一起比较一下,看看它们各自的优缺点,以及适用场景。

特性 @Autowired @Resource
来源 Spring框架 JSR-250规范
注入方式 默认按类型注入 默认按名称注入,如果未指定名称,则按类型注入
是否需要依赖 无需额外依赖 需要引入 javax.annotation-api 依赖
类型安全性 较高,编译时检查类型是否匹配 稍弱,运行时才报错
灵活性 需要配合 @Qualifier 注解才能按名称注入 可以直接通过 name 属性指定要注入的bean的名称
适用场景 大部分情况下,只有一个匹配类型的bean存在 优先按名称注入,或者需要与其他JSR-250规范兼容时

形象比喻:

  • @Autowired 就像一个“耿直的理工男”,只认类型,只要类型匹配,就毫不犹豫地注入。
  • @Resource 就像一个“成熟的职场老手”,优先看名字,名字对得上就注入,对不上再看类型。

到底该如何选择?

这个问题没有绝对的答案,关键在于你的具体需求和个人偏好。 老王给大家一些建议:

  • 如果你是Spring框架的忠实粉丝,并且喜欢简洁的代码风格,那么 @Autowired 可能是你的首选。
  • 如果你更倾向于按照名称注入,或者需要与其他JSR-250规范兼容,那么 @Resource 可能会更适合你。
  • 在团队开发中,最好统一使用一种注解,避免混用,保持代码风格的一致性。

五、高级技巧:构造器注入和Setter注入

除了字段注入之外,我们还可以使用构造器注入和Setter注入。

  • 构造器注入: 通过构造器来注入依赖。 优点是:依赖关系更加明确,可以确保对象在创建时就拥有所有必要的依赖。 缺点是:如果依赖过多,构造器会变得很长。
@Component
public class CoffeeMaker {

    private final CoffeeBean coffeeBean;

    @Autowired
    public CoffeeMaker(CoffeeBean coffeeBean) {
        this.coffeeBean = coffeeBean;
    }

    public void makeCoffee() {
        System.out.println("Using " + coffeeBean.getName() + " to make coffee.");
    }
}
  • Setter注入: 通过Setter方法来注入依赖。 优点是:更加灵活,可以在对象创建之后再注入依赖。 缺点是:依赖关系可能不那么明确,对象可能处于不完整的状态。
@Component
public class CoffeeMaker {

    private CoffeeBean coffeeBean;

    @Autowired
    public void setCoffeeBean(CoffeeBean coffeeBean) {
        this.coffeeBean = coffeeBean;
    }

    public void makeCoffee() {
        System.out.println("Using " + coffeeBean.getName() + " to make coffee.");
    }
}

选择哪种注入方式?

  • 如果你的对象需要强制依赖某个对象才能正常工作,那么构造器注入是更好的选择。
  • 如果你的对象可以容忍没有某个依赖,或者需要在对象创建之后再注入依赖,那么Setter注入可能更适合你。

六、总结:注入的艺术,平衡的智慧

好了,各位观众,今天的“Java那些事儿”就到这里了。 我们一起探讨了Spring框架中 @Autowired@Resource 注解的恩怨情仇,以及构造器注入和Setter注入的优缺点。

依赖注入是一门艺术,它需要我们根据实际情况,选择合适的注入方式,才能写出优雅、可维护、可测试的代码。

记住,没有最好的注入方式,只有最适合你的方式。 关键在于理解每种方式的优缺点,然后根据你的具体需求做出明智的选择。

希望今天的节目能帮助大家更好地理解Spring的依赖注入机制,在实际开发中更加得心应手。

最后,老王祝大家代码无bug,天天开心! 我们下期再见! 😊

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注