聊聊@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方法注入,不支持构造函数注入。

相关推荐
松☆9 分钟前
Dart 核心语法精讲:从空安全到流程控制(3)
android·java·开发语言
编码者卢布30 分钟前
【App Service】Java应用上传文件功能部署在App Service Windows上报错 413 Payload Too Large
java·开发语言·windows
q行1 小时前
Spring概述(含单例设计模式和工厂设计模式)
java·spring
好好研究2 小时前
SpringBoot扩展SpringMVC
java·spring boot·spring·servlet·filter·listener
毕设源码-郭学长2 小时前
【开题答辩全过程】以 高校项目团队管理网站为例,包含答辩的问题和答案
java
NE_STOP2 小时前
spring6-工厂设计模式与bean的实例化方式
spring
玄〤2 小时前
Java 大数据量输入输出优化方案详解:从 Scanner 到手写快读(含漫画解析)
java·开发语言·笔记·算法
tb_first2 小时前
SSM速通3
java·jvm·spring boot·mybatis
独自破碎E2 小时前
总持续时间可被 60 整除的歌曲
java·开发语言