@Autowired三种注入方式的区别以及@Inject注解的基本使用

2023-11-08

@Autowired三种注入方式的区别

在Spring中使用@Autowired注解进行依赖注入时,一般有三种注入方式:

  1. Autowired Constructors (构造器注入)
  2. Autowired Methods (setter注入)
  3. Autowired Fields (属性注入)

@Autowired三种注入方式

1、构造器注入

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

​ 由于Bean的声明周期大致有以下阶段:实例化 -> 属性赋值 -> 初始化 -> 销毁。也就是说需要先执行构造方法然后再进行属性依赖注入,在进行实例化过程中,如果在ioc容器中找不到构造参数对应的实例对象,那就不会执行构造函数,这就保障了只要实例化了某对象,那么他的所有依赖都不会为null(前提是所有成员变量都作为构造参数进行注入或者本身附有初始值)。于是在这种使用方式下,在对象实例化阶段就能发现问题并抛出异常,区别于下面说到的属性注入。

​ 从Spring Framework 4.3开始,如果目标bean一开始只定义一个构造函数,那么就不需要在这样的构造函数上使用@Autowired注释。但是,如果有几个构造函数可用,并且没有默认构造函数,则必须至少用@Autowired注释其中一个构造函数,以便指示容器使用哪一个。

lombok注解实现构造器注入

如果觉得构造器注入写起来比较麻烦,可以使用lombok的@RequiredArgsConstructor注解来自动生成带@Autowired的构造器。

注意:使用lombok实现构造器注入的依赖必须是final或者@NonNull修饰,否则lombok不会注入这些依赖。

@RequiredArgsConstructor(onConstructor=@_(@Autowired))
@Controller
public class BasicController {

    private final UserService userService;
    
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(@RequestParam(name = "name", defaultValue = "unknown user") String name) {
        return "Hello " + name;
    }
	// ...
}

2、setter注入

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

​ setter注入会在实例化过程之后进行,虽然理论上这样可能会导致依赖为null,但实际上Spring不会把空值赋值给依赖,如果依赖在ioc容器中不存在,spring会抛出异常,所以setter注入和构造器注入在使用效果上是相同的,都会及时抛出异常,区别在于依赖注入的时间不同,一个是在实例化阶段,一个是在属性赋值阶段。

​ setter注入对方法名称和方法参数的个数没有特定要求,比如:

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

​ 上边的例子依然属于一个setter注入。

3、属性注入

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

​ 属性注入和构造器注入可以同时使用。属性注入会被idea提醒 Field injection is not recommended

​ @Autowired执行时刻:属性注入本质上是通过反射的方式直接注入到field,因为只有对象创建完成之后才能调用反射方法进行注射,所以执行时刻是对象创建完成之后。

​ 这种方式可能会存在一些隐患:

问题一
@Autowired
private User user;

private String company;

public UserDaoImpl(){
    this.company = user.getCompany();
}

编译过程不会报错,但是运行之后报NullPointerException。

Java 在初始化一个类时,是按照静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 -> @Autowired 的顺序。所以在执行这个类的构造方法时,user对象尚未被注入,它的值还是 null。

问题二

当我们Autowired属性注入时,如果写错了依赖类型,那只有在使用这个依赖时才能发现空指针异常,而不是在项目运行后就及时报出来,这对我们及时排查问题造成了阻碍。

总结

  1. 对于必需的依赖项,建议使用构造器注入,且一般配合final使用,这样保证依赖注入后不可变。使用构造函数注入可以非常明确地表达一个类的依赖关系,使得代码依赖性更加明显和易于维护。此外,由于所有必须的依赖项都必须在构造函数中声明,因此这种方式可以提高代码的可测试性。
  2. 对于可选的依赖,推荐setter注入。setter注入可以避免直接访问类的属性,从而实现更好的封装性。
  3. 属性注入能够减少类的代码,使其更加简洁易懂,但属性注入有可能注入为null,这些错误只有在使用时才会爆出来,另外属性注入也存在一些隐患。
  4. 构造器和setter注入是推荐的,而属性注入并不推荐,除非依赖关系非常简单明了。

使用 @Inject 代替 @Autowired

从Spring 3.0开始,Spring提供了对JSR-330标准注释(依赖注入)的支持。这些注释的扫描方式与Spring注释相同。要使用它们,在pom文件中引入相关jar包。

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

与 @Autowired一样,您可以在属性、setter方法和构造函数参数上使用@Inject。

import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.findMovies(...);
        // ...
    }
}

另外,可以通过Provider.get()方法懒加载方式获得依赖并使用它:

import javax.inject.Inject;
import javax.inject.Provider;

public class SimpleMovieLister {

    private Provider<MovieFinder> movieFinder;

    @Inject
    public void setMovieFinder(Provider<MovieFinder> movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.get().findMovies(...);
        // ...
    }
}

还可以配合@Named使用来指定特定实现类:

import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

注入的优先级:

  1. Match by Type
  2. Match by Qualifier
  3. Match by Name

由于@Inject注解没有属性,在加载所需bean失败时,会报错,这是区别于@Autowired的。

参考

  • https://docs.spring.io/spring-framework/docs/5.3.27/reference/html/core.html#beans-autowired-annotation
  • https://juejin.cn/post/7023618746501562399
  • https://blog.csdn.net/qq_39249094/article/details/121028234
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@Autowired三种注入方式的区别以及@Inject注解的基本使用 的相关文章

随机推荐

  • NCC申请授权

    1 进入home路径下的bin文件夹 打开sysconfig配置文件 2 在sysconfig配置界面 点击license 生成硬件锁 在弹框界面输入产品号 产品号可在点击 读取授权 按钮后 进行查看 后 点击确定 自动生成一个hardke
  • CryptoPP使用介绍

    CryptoPP使用介绍 发表时间 2012年06月15 分类 编程开发 作者 天缘 Crypto 是个免费的C 加解密类库 由于资格太老 持续更新 最新版本到了CryptoPP 5 6 对天缘而言 第一眼看到CryptoPP就感觉头大 根
  • 文本语言模型的参数估计-最大似然估计、MAP及贝叶斯估计

    以PLSA和LDA为代表的文本语言模型是当今统计自然语言处理研究的热点问题 这类语言模型一般都是对文本的生成过程提出自己的概率图模型 然后利用观察到的语料数据对模型参数做估计 有了语言模型和相应的模型参数 我们可以有很多重要的应用 比如文本
  • JavaWeb - Servlet:重定向和转发,状态管理

    Servlet JDBC 应用 在 Servlet 中可以使用 JDBC 技术访问数据库 常见功能如下 查询 DB 数据 然后生成显示页面 例如 列表显示功能 接收请求参数 然后对 DB 操作 例如 注册 登录 修改密码等功能 为了方便重用
  • 代理重加密(Proxy Re-Encryption)技术原理和Java代码实现

    欢迎关注公众号 区块链之美 致力于区块链技术研究 传播区块链技术和解决方案 区块链应用落地 区块链行业动态等 1 代理重加秘的应用介绍 由于大部分的云服务供应商并不能完全值得信任 云服务供应商可能会在未经用户允许的情况下 擅自泄露用户的隐私
  • 【node】10、express模块搭建服务

    express模块是一个外部引入模块 不是node内部自身的模块 所以需要下载express模块才能引入 下载express之前需要初始化项目文件 npm init y 初始化后安装express npm install express 安
  • org.springframework.aop.AopInvocationException: Null return value from advice does not match primiti

    private static Object processReturnType Object proxy Object target Method method Object retVal Massage return value if n
  • Python3 TypeError: Required argument ‘outImg‘ (pos 3) not found

    问题 在用python3使用img3 cv2 drawMatchesKnn img1 kp1 img2 kp2 good flags 2 的时候 可能会产生错误 TypeError Required argument outImg pos
  • solidity 安全 合约的短地址攻击——这个锅谁来背

    前一段时间 有个用户用说发交易的时候提示地址错误 后来发现发送的地址少了一字节 所以钱包检测发送地址时 会提示错误 当时也没当回事 以为是用户自己搞错了 最近研究solidity的时候 才明白了当时是怎么回事 原来这个用户遇到了短地址攻击
  • 超详细解释MyBatis与Spring的集成原理

    前言 最原始的MyBatis的使用 通常有如下几个步骤 读取配置文件mybatis config xml构建SqlSessionFactory 通过SqlSessionFactory拿到SqlSession 通过SqlSession拿到Ma
  • SpringCloud

    文章目录 微服务架构 SpringCloud 二 上篇SpringCloud本Cloud 1 SpringCloud的命名规则及版本关系 1 1 springboot与springcloud的版本依赖 1 2 本次博文使用的环境及版本 2
  • 浅谈NB-IOT模块调试

    背景 在物联网的口号下 我们公司也有幸踏足NB物联这块 当然也只是二次应用开发 NB核心开发技术都掌握在几个大公司大佬手里 例如 华为海思 高通 intel 当然模块 厂商又例如 移远 ublox等 芯片的资料和技术不像Lora这样开源 所
  • python实现字符串匹配算法BF,BF改,KMP

    包含 BF BF改进版本 KMP BF 暴力搜索 BF改 当判断匹配失败的字符串是不是与首字母相同 若不同 继续BF算法 若相同 直接将首字母移到当前位置 KMP 通过前缀与后缀发现待匹配字符串本身的特性 匹配失败时一次性移动多个字符以减少
  • python三维数组切片

    使用np random randint创建一个 3 4 5 的三维随机数组 利用切片返回 如下图位置的数
  • 文件上传封装与使用

    组件封装
  • Linux下Fork与Exec使用

    老邮局 琼楼挂月钓流云 梦里瑶台暂借春 Linux下Fork与Exec使用 一 引言 对于没有接触过Unix Linux操作系统的人来说 fork是最难理解的概念之一 它执行一次却返回两个值 fork函数是Unix系统最杰出的成就之一 它是
  • 白帽,黑帽,灰帽,绿帽!一文了解黑客的所有信息

    前言 您是否想过黑客有许多不同的类型 是什么因素促使他们学习黑客技能 当我想到黑客时 我都会想到下面这张图片 那就是黑客的形象 那你呢 文末有彩蛋 网络可以说是有史以来最重要的战场 这里没有国界 也没有有组织的军队 在线网络战场是善恶之间最
  • ucosII 下iic 的使用问题(含解决方式)

    今天在将SGP30气体传感器的代码移植到ucosii中使用时遇到了输出数据一直为65535的情况 分析现象 开始以为是硬件问题 元器件损坏等原因 使用了裸核代码进行测试 能够正常读取相应参数说明硬件正常 ucos跑死了 增加led显示任务
  • 机器学习笔试题一

    1 输入图片大小为200 200 依次经过一层卷积 kernel size 5 5 padding 1 stride 2 pooling kernel size 3 3 padding 0 stride 1 又一层卷积 kernel siz
  • @Autowired三种注入方式的区别以及@Inject注解的基本使用

    文章目录 Autowired三种注入方式的区别 Autowired三种注入方式 1 构造器注入 lombok注解实现构造器注入 2 setter注入 3 属性注入 问题一 问题二 总结 使用 Inject 代替 Autowired 参考 A