正确使用@Autowired

news2024/10/7 13:20:09

目录

  • 一、前言
  • 二、跟着官方文档,学习正确使用@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
*/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝桥杯每日一题:斐波那契(矩阵乘法)

在斐波那契数列中&#xff0c;Fib00,Fib11,FibnFibn−1Fibn−2(n>1) 给定整数 n&#xff0c;求 Fibnmod10000。 输入格式 输入包含不超过 100100 组测试用例。 每个测试用例占一行&#xff0c;包含一个整数 当输入用例 n−1时&#xff0c;表示输入终止&#xff0c;且该…

实现Hello Qt 程序

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、使用 "按钮" 实现 1、纯代码方式实现 2、可视化操作实现 &#xff08;1&#xff09…

【Easy云盘 | 第三篇】登录注册模块上篇(获取验证码、发送邮箱验证码、登录、注册、重置密码)基于AOP实现参数校验

文章目录 4.2登录注册模块设计4.2.1获取验证码&#xff08;1&#xff09;思路&#xff08;2&#xff09;接口&#xff08;3&#xff09;controller层&#xff08;4&#xff09;CreateImageCodeUtils工具类&#xff08;5&#xff09;测试结果 4.2.2发送邮箱验证码&#xff08;1&…

C++初阶---vector(STL)

1、vector的介绍和使用 1.1、vector的介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是…

Pagerank学习笔记

前言 通过b站视频学习以及网上资料整理后进行学习笔记的撰写&#xff0c;只包含pr的一些简单原理及其应用。不包括pr的自定义实现。 一、pagerank简介 背景简介 PageRank算法 由Google创始人Larry Page 在斯坦福读大学时提出&#xff0c;又称PR,佩奇排名。主要针对网页进行…

Docker容器(五)Docker Compose

一、概述 1.1介绍 Docker Compose是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。Compose 是 Docker 公司推出的一个工具软件&#xff0c;可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml&#xff0c;…

bootstrap+thymeleaf 页面多选回显时莫名其妙多了

bootstrapthymeleaf 页面多选回显时莫名其妙多了 问题现象问题分析问题处理总结 问题现象 今天遇到的问题的描述正如标题中的一样&#xff0c;就是后台管理系统在配置完内容后点击保存&#xff0c;回显时发现页面竟然莫名其妙多了一些数据。项目整体后台管理系统采用的是boots…

分类预测 | Matlab实现DRN深度残差网络数据分类预测

分类预测 | Matlab实现DRN深度残差网络数据分类预测 目录 分类预测 | Matlab实现DRN深度残差网络数据分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现DRN深度残差网络数据分类预测&#xff08;完整源码和数据&#xff09;&#xff0c;运行环境为Matl…

usb_camera传输视频流编码的问题记录!

前言&#xff1a; 大家好&#xff0c;今天给大家分享的内容是&#xff0c;一个vip课程付费的朋友&#xff0c;在学习过程中遇到了一个usb采集的视频数据流&#xff0c;经过ffmpeg编码&#xff0c;出现了问题&#xff1a; 问题分析&#xff1a; 其实这个问题不难&#xff0c;关键…

wordpress子比主题打开文章详情页一直出现在首页的问题

遇到过几次这种情况了&#xff0c;不知是不是中了木马&#xff0c;无从下手&#xff0c;试了很多方法都不行快要疯了&#xff0c;之前试过解决不了只能重新安装&#xff0c;现在又出现了&#xff0c;第二次了&#xff0c;太麻烦了&#xff0c;突然无意中打开index.php文件发现被…

Android与RN远程过程调用的原理

Android与RN远程过程调用的原理是通过通信协议进行远程过程调用。RPC(Remote Procedure Call)是分布式系统常见的一种通信方式&#xff0c;从跨进程到跨物理机已经有几十年历史。 在React Native中&#xff0c;通信机制是一个C实现的桥&#xff0c;打通了Java和JS,实现了两者的…

数据被halo勒索病毒锁定?这里有恢复秘笈!

在当今数字化的社会&#xff0c;数据的重要性不言而喻。然而&#xff0c;随着网络技术的发展&#xff0c;数据安全问题也日益凸显。近年来&#xff0c;勒索病毒成为网络安全的一大威胁&#xff0c;其中halo勒索病毒更是让人闻风丧胆。一旦数据被这种病毒锁定&#xff0c;用户将…

42. 接雨水(Java)

目录 题目描述:输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 输入&#xff1a; height [0,1,0,2,1,0,1,3,2,1,2,1]输出&#xff1…

SpringCloud学习(11)-SpringCloudAlibaba-Nacos数据模型

一、Nacos数据模型 1.1、数据模型 对于Nacos配置管理&#xff0c;通过Namespace、Group、Date ID能够定位到一个配置集。Nacos数据模型如下所示&#xff1a; 1.2、命名空间(Namespace) 可用于进行不同环境的配置隔离。例如&#xff1a; 1)、可以隔离开发环境——测试环境和…

数据可视化高级技术Echarts(快速上手柱状图进阶操作)

目录 1.Echarts的配置 2.程序的编码 3.柱状图的实现&#xff08;入门实现&#xff09; 相关属性介绍&#xff08;进阶&#xff09;&#xff1a; 1.标记最大值/最小值 2.标记平均值 3.柱的宽度 4. 横向柱状图 5.colorBy series系列&#xff08;需要构造多组数据才能实现…

基于java实现的二手车交易网站

开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea…

C++初级----string类(STL)

1、标准库中的string 1.1、sring介绍 字符串是表示字符序列的类&#xff0c;标准的字符串类提供了对此类对象的支&#xff0c;其接口类似于标准字符容器的接口&#xff0c;但是添加了专门用于操作的单字节字符字符串的设计特性。 string类是使用char&#xff0c;即作为他的字符…

蓝桥杯 【日期统计】【01串的熵】

日期统计 第一遍写的时候会错了题目的意思&#xff0c;我以为是一定要八个整数连在一起构成正确日期&#xff0c;后面发现逻辑明明没有问题但是答案怎么都是错的才发现理解错了题目的意思&#xff0c;题目的意思是按下标顺序组成&#xff0c;意思就是可以不连续&#xff0c;我…

vue快速入门(八)绑定方法

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-if与button响应回顾事件方法写法 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, …

《QT实用小工具·十七》密钥生成工具

1、概述 源码放在文章末尾 该项目主要用于生成密钥&#xff0c;下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #pragma execution_character_set("utf-8")#include "frmmain.h" #include "ui_frmmain.h" #include "qmessag…