聊聊@Autowired与@Resource的区别

1. 前言

从事过很多家公司,见过很多项目,发现@Autowired@Resource的使用都是一样的乱,

一个项目中有使用@Autowired的,有使用@Resource的,

甚至有的类中一会儿使用@Autowired,一会儿使用@Resource,虽然不影响业务功能的实现,但看起来真的是杂乱无章。

本篇博客主要讲解这2个注解之间的区别。

2. 来源不同

@Autowired是Spring框架的注解。

@Resource是Java的注解(来自于JSR-250),由Spring框架兼容支持。

说明:JSR是Java Specification Requests的缩写,意思是Java规范提案。

3. 依赖查找顺序不同

@Autowired先根据类型查找,如果存在多个Bean,再根据名称查找。

@Resource先根据名称查找,如果查找不到,再根据类型查找。

3.1 验证@Autowired先根据类型查找,再根据名称查找

首先,新建接口:

java 复制代码
public interface NotificationService {
    void send();
}

然后新建第一个实现类:

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class EmailService implements NotificationService {
    @Override
    public void send() {
        System.out.println("发送邮件通知");
    }
}

接着新建第二个实现类:

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class SmsService implements NotificationService {
    @Override
    public void send() {
        System.out.println("发送短信通知");
    }
}

最后新建Controller,并使用@Autowired来注入NotificationService:

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

@RestController("/dependency/injection/test")
public class NotificationController {
    @Autowired
    private NotificationService notificationService;
}

此时启动项目,会抛出org.springframework.beans.factory.NoUniqueBeanDefinitionException异常,

原因是因为有2个NotificationService类型的Bean,Spring不确定注入哪一个Bean,这也证明@Autowired默认是先根据类型查找。

有三种解决方案可以解决该问题,

第一种解决方案是使用@Primary注解:

java 复制代码
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Service
@Primary
public class EmailService implements NotificationService {
    @Override
    public void send() {
        System.out.println("发送邮件通知");
    }
}

说明:如果有多个同类型的Bean,Spring会优先使用@Primary注解标记的Bean。

第二种解决方案是修改字段名称:

java 复制代码
@Autowired
private NotificationService emailService;

第三种解决方案是使用@Qualifier注解:

java 复制代码
@Autowired
@Qualifier("emailService")
private NotificationService notificationService;

第二种解决方案和第三种解决方案证明@Autowired是根据名称查找的,

两者的区别是第二种解决方案是按字段名称查找的(隐式),第三种解决方案是按指定的名称查找的(显式)。

3.2 验证@Resource先根据名称查找,再根据类型查找

首先,新建接口:

java 复制代码
public interface NotificationService {
    void send();
}

然后新建第一个类(注意事项:不是实现类):

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class EmailService {
    public void send() {
        System.out.println("发送邮件通知");
    }
}

接着新建第二个类(注意事项:是实现类):

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class SmsService implements NotificationService {
    @Override
    public void send() {
        System.out.println("发送短信通知");
    }
}

最后新建Controller,并使用@Resource来注入NotificationService:

java 复制代码
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController("/dependency/injection/test")
public class NotificationController {
    @Resource
    private NotificationService emailService;
}

此时启动项目,会抛出org.springframework.beans.factory.BeanNotOfRequiredTypeException异常,

原因是因为按字段名称查找到的EmailService Bean,不是NotificationService类型,这也证明@Resource默认是先根据名称查找。

有两种解决方案可以解决该问题,

第一种解决方案是显式指定Bean名称:

java 复制代码
@Resource(name = "smsService")
private NotificationService emailService;

第二种解决方案是修改字段名称:

java 复制代码
@Resource
private NotificationService notificationService;

第二种解决方案能注入成功,也证明@Resource是根据类型查找的,

此时因为NotificationService只有一个实现类SmsService,所以直接注入成功,

如果将EmailService也改为NotificationService的实现类:

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class EmailService implements NotificationService {
    @Override
    public void send() {
        System.out.println("发送邮件通知");
    }
}

那么启动项目,会抛出org.springframework.beans.factory.NoUniqueBeanDefinitionException异常。

4. 参数不同

@Autowired只有1个required参数,@Resource有name、type等7个参数。

4.1 @Autowired的参数

@Autowired只有1个参数,如下所示:

java 复制代码
public @interface Autowired {
    boolean required() default true;
}

默认情况下,@Autowired要求依赖必须存在,可以通过required = false设置为可选。

java 复制代码
@Autowired(required = false)
private NotificationService notificationService;

4.2 @Resource的参数

@Resource有7个参数,如下所示:

java 复制代码
public @interface Resource {
    String name() default "";

    String lookup() default "";

    Class<?> type() default java.lang.Object.class;

    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }

    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    boolean shareable() default true;

    String mappedName() default "";

    String description() default "";
}

默认情况下,@Resource先根据字段名查找Bean,可以通过name参数显式指定名称,通过type参数显式指定类型。

java 复制代码
@Resource(name = "emailService", type = NotificationService.class)
private NotificationService notificationService;

5. 支持的依赖注入方式不同

@Autowired支持字段注入、Setter方法注入和构造函数注入。

@Resource支持字段注入、Setter方法注入,不支持构造函数注入。

5.1 @Autowired支持的依赖注入方式

1)字段注入:

java 复制代码
@RestController("/dependency/injection/test")
public class NotificationController {
    @Autowired
    private NotificationService notificationService;
}

这种方式不推荐使用,但在实际项目中使用的最多。

2)Setter方法注入:

java 复制代码
@RestController("/dependency/injection/test")
public class NotificationController {
    private NotificationService notificationService;

    @Autowired
    private void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }
}

3)构造函数注入

java 复制代码
@RestController("/dependency/injection/test")
public class NotificationController {
    private final NotificationService notificationService;

    public NotificationController(NotificationService notificationService) {
        this.notificationService = notificationService;
    }
}

这种方式是Spring官方推荐的首选方式。

5.2 @Resource支持的依赖注入方式

1)字段注入:

java 复制代码
@RestController("/dependency/injection/test")
public class NotificationController {
	@Resource
    private NotificationService notificationService;
}

2)Setter方法注入:

java 复制代码
@RestController("/dependency/injection/test")
public class NotificationController {
    private NotificationService notificationService;

	@Resource
    private void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }
}

@Resource不支持构造函数注入,如果在构造函数上使用@Resource注解,IDEA会提示:

'@Resource' not applicable to constructor。

6. 总结

@Autowired@Resource都是用来实现依赖注入的注解,但两者之间是有区别的,主要有以下4点:

  1. 来源不同

    @Autowired是Spring框架的注解。

    @Resource是Java的注解(来自于JSR-250),由Spring框架兼容支持。

  2. 依赖查找顺序不同

    @Autowired先根据类型查找,如果存在多个Bean,再根据名称查找。

    @Resource先根据名称查找,如果查找不到,再根据类型查找。

  3. 参数不同

    @Autowired只有1个required参数,@Resource有name、type等7个参数。

  4. 支持的依赖注入方式不同

    @Autowired支持字段注入、Setter方法注入和构造函数注入。

    @Resource支持字段注入、Setter方法注入,不支持构造函数注入。

相关推荐
开源之眼2 小时前
《github star 加星 Taimili.com 艾米莉 》为什么Java里面,Service 层不直接返回 Result 对象?
java·后端·github
Maori3163 小时前
放弃 SDKMAN!在 Garuda Linux + Fish 环境下的优雅 Java 管理指南
java
用户908324602733 小时前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
小王和八蛋3 小时前
DecimalFormat 与 BigDecimal
java·后端
beata4 小时前
Java基础-16:Java内置锁的四种状态及其转换机制详解-从无锁到重量级锁的进化与优化指南
java·后端
IT探险家4 小时前
你的第一个 Java 程序就翻车?HelloWorld 的 8 个隐藏陷阱
java
随风飘的云4 小时前
SpringBoot 的自动配置原理
java
SimonKing4 小时前
觅得又一款轻量级数据库管理工具:GoNavi
java·后端·程序员
Seven975 小时前
BIO详解:解锁阻塞IO的使用方式
java
oak隔壁找我15 小时前
JVM常用调优参数
java·后端