聊聊@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 分钟前
ElasticSearch从入门到精通-覆盖DSL操作和Java实战
java·大数据·elasticsearch·搜索引擎·全文检索
极客智谷26 分钟前
深入理解Java线程池:从原理到实战的完整指南
java·后端
程序员小陈在成都34 分钟前
Spring Ioc源码引入:什么是IoC,IoC解决了什么问题
spring
代码不行的搬运工34 分钟前
HTML快速入门-4:HTML <meta> 标签属性详解
java·前端·html
bug菌1 小时前
面十年开发候选人被反问:当类被标注为@Service后,会有什么好处?我...🫨
spring boot·后端·spring
mask哥1 小时前
详解最新链路追踪skywalking框架介绍、架构、环境本地部署&配置、整合微服务springcloudalibaba 、日志收集、自定义链路追踪、告警等
java·spring cloud·架构·gateway·springboot·skywalking·链路追踪
XU磊2601 小时前
javaWeb开发---前后端开发全景图解(基础梳理 + 技术体系)
java·idea
学也不会1 小时前
雪花算法
java·数据库·oracle
晓华-warm2 小时前
国产免费工作流引擎star 5.9k,Warm-Flow版本升级1.7.0(新增大量好用功能)
java·中间件·流程图·开源软件·flowable·工作流·activities
凭君语未可2 小时前
介绍 IntelliJ IDEA 快捷键操作
java·ide·intellij-idea