正确使用@Autowired

目录

  • 一、前言
  • 二、跟着官方文档,学习正确使用@Autowired
    • 0、实验环境
    • 1、通过构造方法进行注入
      • 1.1 问题1:那万一没有这个CustomerPreferenceDao对象,会报错吗?
    • 2、通过setter方法注入
    • 3、通过方法注入(这个方法可以是任意名称,有任意参数)
    • 4、通过字段注入 【非常常见的做法,简洁】
    • 5、字段注入和构造方法注入可以混用。
    • 6、解释
    • 7、注入一组依赖(通过:字段 或 方法)
      • 7.1 通过字段
      • 7.2 通过方法
    • 8、按顺序注入一组依赖【以字段注入为例】
    • 9、还能注入Map<String, xxx>

一、前言

  • Spring框架两大核心特性:IoC容器(对开发者来说,就是希望Spring帮助注入依赖) + AOP
  • 因此,熟练使用Spring框架之一便是熟练使用依赖注入。
  • 之前介绍了“正确使用@Resource”,本文重点介绍“正确使用@Autowired”

二、跟着官方文档,学习正确使用@Autowired

0、实验环境

@ComponentScan
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
        Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
        System.out.println("---------------------------------------------------------------");
        MovieRecommender movieRecommender = applicationContext.getBean(MovieRecommender.class);
        movieRecommender.sayHello();
    }
}
public class CustomerPreferenceDao {
    public void sayHello() {
        System.out.println("Hello, I am " + this.getClass().getSimpleName());
    }
}

@Configuration
public class LearnAutowiredConfig {
    @Bean
    public CustomerPreferenceDao customerPreferenceDao() {
        return new CustomerPreferenceDao();
    }
}

1、通过构造方法进行注入

@Component
public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;

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

    public void sayHello() {
        customerPreferenceDao.sayHello();
        System.out.println("Hello, I am " + this.getClass().getSimpleName());
    }
}
  • 从Spring 4.3开始,如果组件只有一个构造方法,那么没必要给这个构造方法带上@Autowired。
    • 道理也很简单,Spring为MovieRecommender类创建对象时,少不了调用构造方法。它发现需要一个CustomerPreferenceDao对象,然后在IoC容器里面确实有这个对象,那就自动帮咱注入了。

1.1 问题1:那万一没有这个CustomerPreferenceDao对象,会报错吗?

  • 会报错!

个人觉得,注解是一个非常好的标识,即使能省略,也别省略。

  • 为啥会报错呢?
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

	/**
	 * Declares whether the annotated dependency is required.
	 * <p>Defaults to {@code true}.
	 */
	boolean required() default true;

}
  • 表面上省略了@Autowired,但实际上仍然和带上@Autowired的效果一致。默认要求必须注入依赖,如果待注入的依赖不存在,则报错。
  • 那我改成@Autowired(required = false)可以吗?–> 依然报错
Inconsistent constructor declaration on bean with name 'movieRecommender': single autowire-marked constructor flagged as optional - this constructor is effectively required since there is no default constructor to fall back to: public com.forrest.learnspring.autowired.example1.bean.MovieRecommender(com.forrest.learnspring.autowired.example1.dao.CustomerPreferenceDao)
  • 大意:Spring认为提供的构造方法不符合需求(因为找不到可用的CustomerPreferenceDao的bean),然后发现这个构造方法是可选的,那我就选其他的吧。结果没其他可选了,那只能报错了。
  • 补一个空参构造方法就行:
public MovieRecommender() {
    this.customerPreferenceDao = null;
}

@Autowired(required = false)
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
    this.customerPreferenceDao = customerPreferenceDao;
}
  • 如果bean具有多个构造方法,那么不能省略@Autowired,否则Spring在创建bean的时候,不知道用哪个构造方法。

2、通过setter方法注入

@Component
public class MovieRecommender {
    private CustomerPreferenceDao customerPreferenceDao;
    
    @Autowired
    public void setCustomerPreferenceDao(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
    
    ...
}

3、通过方法注入(这个方法可以是任意名称,有任意参数)

@Component
public class MovieRecommender {
    private CustomerPreferenceDao customerPreferenceDao;
    
    @Autowired
    public void prepare(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    public void sayHello() {
        customerPreferenceDao.sayHello();
        System.out.println("Hello, I am " + this.getClass().getSimpleName());
    }
}
  • 这个prepare方法会被Spring调用,咱不用管。咱需要注意的是:
    • 入参必须是能够在IoC容器中找得到的bean。(String s, CustomerPreferenceDao customerPreferenceDao)这是不可以的。因此,IoC容器中,没有s这个bean。
    • 对访问修饰符没有要求。
      • 我猜测是,Spring创建好MovieRecommender的bean后,由这个bean去调用自己的方法。因此,哪怕方法是private的都没问题。

4、通过字段注入 【非常常见的做法,简洁】

@Component
public class MovieRecommender {
    @Autowired
    private CustomerPreferenceDao customerPreferenceDao;
	...
}
  • 但IDEA不建议字段注入。【但写的代码少了,一般都采用这种,哈哈】

5、字段注入和构造方法注入可以混用。

@Component
public class MovieRecommender {
    @Autowired
    private CustomerPreferenceDao customerPreferenceDao;

    private final UserProcessor userProcessor;

    @Autowired
    public MovieRecommender(UserProcessor userProcessor) {
        this.userProcessor = userProcessor;
    }
	
	...
}

6、解释

官方文档:
Make sure that your target components (for example, MovieCatalog or CustomerPreferenceDao) are consistently declared by the type that you use for your @Autowired-annotated injection points. Otherwise, injection may fail due to a “no type match found” error at runtime.
For XML-defined beans or component classes found via classpath scanning, the container usually knows the concrete type up front. However, for @Bean factory methods, you need to make sure that the declared return type is sufficiently expressive. For components that implement several interfaces or for components potentially referred to by their implementation type, consider declaring the most specific return type on your factory method (at least as specific as required by the injection points referring to your bean).

咋一看,这都是啥啊… 但还是要理解下啊

  • 解释:
    • 从上面的例子可知,不管@Autowired用在构造方法、setter方法、还是字段上,我们都能知道需要的依赖是什么类型的。
    • Spring就会根据这个类型,去IoC容器中找:
      • 因此,我们要保证类型别写错了,否则Spring会找不到。Make sure that your target components are consistently declared by the type (类型兼容)
  • 正例(可以向上转型):
@Controller
public class UserController {
    @Autowired
    private UserService userService;
	...
}
@Configuration
public class LearnAutowiredConfig {
	// 很显然,这么写相当于:UserService userServie = new UserServiceImpl();
	// UserController的userService = userServie = new UserServiceImpl(); 也是成立的。
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }
}

// 同理这么写也行
@Configuration
public class LearnAutowiredConfig {
    @Bean
    public UserServiceImpl userService() {
        return new UserServiceImpl();
    }
}
  • 反例(不能向下转型):
@Controller
public class UserController {
    @Autowired
    private UserServiceImpl userService;
    ...
}

@Configuration
public class LearnAutowiredConfig {
	// UserService userService = new UserServiceImpl();
	// UserController的userService = userService; 这是不行的。属于向下转型。
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }
}
  • 报错:No qualifying bean of type ‘com.forrest.learnspring.autowired.example2.service.impl.UserServiceImpl’ available
  • 向下转型需要强转,否则会报错,等同于:
    在这里插入图片描述
  • 推荐】注入依赖,依赖的类型尽可能抽象(有接口用接口!)。

7、注入一组依赖(通过:字段 或 方法)

7.1 通过字段

@Controller
public class UserController {
    @Autowired
    private List<UserService> userServices;
	...
}

@Configuration
public class LearnAutowiredConfig {
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }

    @Bean
    public UserService userProxyService() {
        return new UserProxyServiceImpl();
    }
}

7.2 通过方法

@Controller
public class UserController {
    private List<UserService> userServices;

    @Autowired
    public void prepare(List<UserService> userServices) {
        this.userServices = userServices;
    }
	
	...
}

8、按顺序注入一组依赖【以字段注入为例】

  • 办法:
  • (1) implement the org.springframework.core.Ordered interface
  • (2) @Order
  • (3) @Priority
  • 以@Order为例子
@Configuration
public class LearnAutowiredConfig {
    @Bean
    @Order(2)
    public UserService userService() {
        return new UserServiceImpl();
    }

    @Bean
    @Order(1)
    public UserService userProxyService() {
        return new UserProxyServiceImpl();
    }
}

/*
1 : UserProxyServiceImpl
2 : UserServiceImpl
*/
@Order(1)
@Service
public class UserProxyServiceImpl implements UserService {
	...
}

@Order(2)
@Service
public class UserServiceImpl implements UserService {
	...
}

9、还能注入Map<String, xxx>

  • 以字段注入为例:
@Controller
public class UserController {
    @Autowired
    private Map<String, UserService> userServiceMap;
	
	...
}

/*
userProxyServiceImpl: UserProxyServiceImpl
userServiceImpl: UserServiceImpl
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/526727.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑

引言 在 iOS 开发中&#xff0c;将 IPA 文件上传到苹果开发者中心是一个重要的步骤。通常情况下&#xff0c;我们需要使用 Mac 电脑上的 Xcode 或 Application Loader 工具来完成这个任务。然而&#xff0c;如果你没有 Mac 电脑&#xff0c;也没有关系&#xff0c;本文将介绍一…

【安全工具】信息收集——ENScan_GO

1.初次使用 运行命令生成配置文件 ./enscan -v 2.配置cookie 参考介绍 wgpsec/ENScan_GO: 一款基于各大企业信息API的工具&#xff0c;解决在遇到的各种针对国内企业信息收集难题。一键收集控股公司ICP备案、APP、小程序、微信公众号等信息聚合导出。 (github.com) 3.友情提…

element-ui 在Popover弹框中使用Select选择器,Vue3

bug描述&#xff1a; 当选择完select的时候,popover也会退出。 解决&#xff1a; popover组件的的关闭是当点击组件外的元素时会关闭&#xff0c;select虽然是写在组件内的&#xff0c;但是select有一个默认属性teleported“true” 会把它默认插到 body 元素&#xff0c;我…

JavaScript模块化开发的前世今生

一个兜兜转转&#xff0c;从“北深”回到三线城市的小码农&#xff0c;热爱生活&#xff0c;热爱技术&#xff0c;在这里和大家分享一个技术人员的点点滴滴。欢迎大家关注我的微信公众号&#xff1a;果冻想 前言 现代化的编程语言&#xff0c;基本都支持模块化的开发&#xff…

嵌入式会越来越卷吗?

当谈及嵌入式系统时&#xff0c;我们探究的不仅是一种科技&#xff0c;更是一个日益多元与普及的趋势。嵌入式系统&#xff0c;作为一种融入更大系统中的计算机硬件和软件&#xff0c;旨在执行特定功能或任务。但这个看似特定的系统概念&#xff0c;却在发展中展现出了惊人的灵…

C++设计模式:观察者模式(三)

1、定义与动机 观察者模式定义&#xff1a;定义对象间的一种1对多&#xff08;变化&#xff09;的依赖关系&#xff0c;以便当一个对象&#xff08;Subject&#xff09;的状态发生比改变时&#xff0c;所有依赖于它的对象都得到通知并且自动更新 再软件构建过程中&#xff0c…

(文章复现)基于改进秃鹰算法的微电网群经济优化调度研究

参考文献&#xff1a; [1]周辉,张玉,肖烈禧,等.基于改进秃鹰算法的微电网群经济优化调度研究[J].太阳能学报,2024,45(02):328-335. 1.基本原理 微电网群由3个独立的微电网(microgrid , MG)组成,各微电网内部包含光伏(photovoltaic , PV)、风力发电机(windturbine,WT)、电动汽…

h5 笔记3 多媒体素材运用

关于电影编码 我们经常用扩展名来判断文件的类型&#xff0c;但是对于影音文件未必适用&#xff0c;影音文件的文件格式(container)和编码(codec)之间并非绝对相关。决定影音文件播放的关键在于浏览器是否含有适合的影音编解码技术。 笔记来源&#xff1a; ©《HTML5CSS3J…

机器学习笔记 - 深度学习遇到超大图像怎么办?使用 xT 对极大图像进行建模论文简读

作为计算机视觉研究人员,在处理大图像时,避免不了受到硬件的限制,毕竟大图像已经不再罕见,手机的相机和绕地球运行的卫星上的相机可以拍摄如此超大的照片,遇到超大图像的时候,我们当前最好的模型和硬件都会达到极限。 所以通常我们在处理大图像时会做出两个次优选择之一:…

Centos 7 安装通过yum安装google浏览器

在CentOS 7上使用yum安装Google Chrome浏览器稍微复杂一些&#xff0c;因为Chrome并不直接包含在默认的Yum仓库中。按照以下步骤来操作&#xff1a; 1、添加Google Chrome仓库 首先&#xff0c;您需要手动添加Google Chrome的Yum仓库。打开终端&#xff0c;并使用文本编辑器&a…

物联网实战--驱动篇之(四)LoRa应用(modbus)

目录 一、前言 二、板级收发 三、主机请求 四、从机接收及回复 五、主机接收 一、前言 之前两篇分别介绍了modbus和sx1278的驱动&#xff0c;但是都并未具体讲解如何应用&#xff0c;那么这一篇就把两者结合起来&#xff0c;做个小demo&#xff0c;便于理解这两个驱动的使…

2005-2023年各省国内生产总值指数分季度数据

2005-2023年各省国内生产总值指数分季度数据 1、时间&#xff1a;2005-2023年 2、来源&#xff1a;国家统计局、各省统计局 3、指标&#xff1a;地区生产总值指数(上年同期100)_累计值(%) 4、范围&#xff1a;31省 5、时间跨度&#xff1a;季度 6、缺失情况&#xff1a;无…

20240325-1-HMM

HMM 直观理解 马尔可夫链&#xff08;英语&#xff1a;Markov chain&#xff09;&#xff0c;又称离散时间马尔可夫链&#xff08;discrete-time Markov chain&#xff0c;缩写为DTMC&#xff09;&#xff0c;因俄国数学家安德烈马尔可夫&#xff08;俄语&#xff1a;Андре…

Ubuntu 20.04.06 PCL C++学习记录(十六)

[TOC]PCL中点云分割模块的学习 学习背景 参考书籍&#xff1a;《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,&#xff0c;PCL版本为1.10.0&#xff0c;CMake版本为3.16 学习内容 用一组点云数据做简单的平面的分割 源代码及所用函数 源代码 #include<iostr…

Linux 内核:线程的实现

在linux中的线程是轻量级线程&#xff08;Light-Weight-process&#xff0c;LWP&#xff09; 文章目录 线程概念线程实现 线程概念 线程分类 用户级线程内核级线程&#xff0c;没有用户空间&#xff0c;完全工作在内核中&#xff08;下图中没有[]的就是用户级线程&#xff09…

跨服务器迁移nextcloud数据

背景 阿里云最近做活动,99一年的2U2G的服务器,比我原来的1U1G的服务器不知道高到哪里去了,于是决定迁移服务器数据到另一台主机上。原先的计划是直接做一个自定义镜像,然后复制到另一台主机就行,结果发现旧主机是aliyunOS,新主机不想踩这个坑了,决定换成乌班图,因此决定重新搭…

大模型基础知识 - 语言模型及其演进

开场白 人工智能发展到现在&#xff0c;在2个重要领域取得了重大突破&#xff0c;有望达到人类水平&#xff1a; 计算机视觉 &#xff08;Computer Vision, CV&#xff09;-> 希望机器帮助人类处理图像数据自然语言处理&#xff08;Natural Language Processing, NLP&…

基于R语言lavaan结构方程模型(SEM)实践技术应用

原文链接&#xff1a;基于R语言lavaan结构方程模型&#xff08;SEM&#xff09;实践技术应用https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247596681&idx4&sn08753dd4d3e7bc492d750c0f06bba1b2&chksmfa823b6ecdf5b278ca0b94213391b5a222d1776743609cd…

文章自动生成器

1.前言 最近开始想写一些东西&#xff0c;脑子里也有不少想法&#xff0c;但是自己写又比较费时间&#xff0c;而且还要排版&#xff0c;精修&#xff0c;刚开始写的字数甚少&#xff0c;想法也是断断续续&#xff0c;不连贯&#xff0c;本想放弃&#xff0c;但是想到放弃就太…

HarmonyOS实战开发-如何实现跨应用数据共享实例。

介绍 本示例实现了一个跨应用数据共享实例&#xff0c;分为联系人&#xff08;数据提供方&#xff09;和联系人助手&#xff08;数据使用方&#xff09;两部分&#xff1a;联系人支持联系人数据的增、删、改、查等功能&#xff1b;联系人助手支持同步联系人数据&#xff0c;当…