当前位置: 首页 > news >正文

SpringBoot:详解依赖注入和使用配置文件

在这里插入图片描述

🏡浩泽学编程:个人主页

 🔥 推荐专栏:《深入浅出SpringBoot》《java项目分享》
              《RabbitMQ》《Spring》《SpringMVC》

🛸学无止境,不骄不躁,知行合一

文章目录

  • 前言
  • 一、🌕依赖注入
    • 🌖注解@Autowired
    • 🌗消除歧义性——@Quelifier和@Primary
    • 🌑带有参数的构造方法类装配
  • 二、📝使用属性文件
  • 总结


前言

在上一篇文章中,讲诉了SpringIoC的Bean装配,但是对于如何进行获取,也就是Bean之间的依赖还未讲诉,下面开始讲诉依赖注入(Dependency Injection,DI)以及如何使用属性文件。涉及主要注解@Autowired、@Primary、@Quelifier、@PropertySource和@ConfigurationProperties。


一、🌕依赖注入

例:人类(Person)有时候利用一些动物(Animal)去完成一些事情,比方说狗(Dog)是用来看门的,猫(Cat)是用来抓老鼠的, 鹦鹉(Paηot)是用来迎客的……于是做一些事情就依赖于那些可爱的动物。假设现在需要用狗狗来看门。

//定义人类接口
public interface Person {void service();void setAnimal(Animal animal);
}
//定义动物接口
public interface Animal {void user();
}
//定义狗
@Component
public class Dog implements Animal {@Overridepublic void user() {System.out.println("狗【" + Dog.class.getSimpleName() + "】是用来看门的");}
}
//定义年轻人
@Component
public class YoungPerson implements Person {@Autowiredprivate Animal animal = null;@Overridepublic void service() {this.animal.user();}@Overridepublic void setAnimal(Animal animal) {this.animal = animal;}
}
//定义配置类
@Configuration
@ComponentScan("com.dragon.restart")//所有的包和类都在restart下
public class AppConfig {
}

测试类:

ApplicationContext ctx =new AnnotationConfigApplicationContext(AppConfig.class) ;Person person= ctx.getBean(YoungPerson.class) ;person.service() ;

在这里插入图片描述
测试是成功的,这个时候SpringIoC 容器己经通过注解@Autowired成功地将Dog注入到了 YoungPerson 实例中。 但是这只是一个比较简单的例子,我们有必要继续探讨@Autowired。

🌖注解@Autowired

@Autowired 是我们使用得最多的注解之一, 因此在这里需要进一步地探讨它。 它注入的机制最基本的一条是根据类型 (bytype), 我们回顾IoC 容器的顶级接口 BeanFactory,就可以知道 IoC 容器是通过getBean 方法获取对应Bean 的,而 getBean 又支持根据类型(by type)或者根据名称(by name)。这里明显根据类型注入,将狗狗实例注入Animal对象。
再回到上面的例子,我们只是创建了一个动物一一狗,而实际上动物还可以有猫 (Cat),猫可以为我们抓老鼠, 于是我们又创建了一个猫的类。

@Component
public class Cat implements Animal{@Overridepublic void user() {System.out.println("猫【" + Cat.class.getSimpleName() + "】是抓老鼠的");}
}

好了,如果我们还使用着YoungPerson类,那么麻烦来了,因为这个类只是定义了一个动物属性(Animal),而我们却有两个动物,一个狗, 一个猫, SpringIoC 如何注入呢?
运行测试,可以看到IoC容器抛出异常,如下:

Description:
Field animal in com.dragon.restart.pojo.impl.YoungPerson required a single bean, but 2 were found:- cat: defined in file [E:\IDEA_projects\restart\target\classes\com\dragon\restart\pojo\Cat.class]- dog: defined in file [E:\IDEA_projects\restart\target\classes\com\dragon\restart\pojo\impl\Dog.class]

那么使用@Autowired 能处理这个问题吗?答案是肯定的。假设我们目前需要的是狗提供服务,那么可以把属性名称转化为dog,也就是原来YoungPerson的private Animal animal = null; 改为private An工mal dog = null;

@Component
public class YoungPerson implements Person {@Autowiredprivate Animal dog = null;@Overridepublic void service() {this.dog.user();}@Overridepublic void setAnimal(Animal animal) {this.dog = animal;}
}

这里, 我们只是将属性的名称从animal 修改为了 dog,那么我们再测试的时候,你可以看到是采用狗来提供服务的。那是因为@Autowired提供这样的规则:首先它会根据类型找到对应的Bean,如果对应类型的 Bean 不是唯一的,那么它会根据其属性名称和 Bean 的名称进行匹配。如果匹配得上,就会使用该Bean:如果还无法匹配,就会抛出异常。这里还要注意的是@Autowired 是一个默认必须找到对应 Bean 的注解,如果不能确定其标注属性一定会存在并且允许这个被标注的属性为null, 那么你可以配置@Autowired属性 required 为 false@Autowired(required = false)

@Autowired除了可以标注属性外,还可以标注方法, 如setAnimal方法,如下所示:

@Override
@Autowired 
public void setAnimal (Animal animal) { 
this.animal = animal;

这样它也会使用setAnimal 方法从 IoC 容器中找到对应的动物进行注入,甚至我们还可以使用在方法的参数上,后续文章会再谈到它。

🌗消除歧义性——@Quelifier和@Primary

在上面我们发现有猫有狗的时候, 为了使@Autowired 能够继续使用,我们做了一个决定,将YoungPerson 的属性名称从 animal 修改为 dog。显然这是一个憋屈的做法,好好的一个动物,却被我们定义为了狗,毕竟不能每次换个对象就改一次,这样太麻烦了。产生注入失败的问题根本是按类型(bytype) 查找, 正如动物可以有多种类型,这样会造成 Spring IoC 容器注入的困扰,我们把这样的一个问题称为歧义性。知道这个原因后, 那么这两个注解是从哪个角度去解决这些问题的呢?

首先是一个注解@Primary,它是一个修改优先权的注解,当我们有猫有狗的时候,假设这次需要使用猫, 那么只需要在猫类的定义上加入@Primarγ就可以了,如下:

@Component
@Primary
public class Dog implements Animal {@Overridepublic void user() {System.out.println("狗【" + Dog.class.getSimpleName() + "】是用来看门的");}
}

这里的@Primary 的含义告诉 Spring IoC 容器, 当发现有多个同样类型的 Bean 时,请优先使用我进行注入,于是再进行测试时会发现,系统将用狗狗为你提供服务。 因为当 Spring 进行注入的时候,虽然它发现存在多个动物, 但因为Dog被标注为了@Primarγ,所以优先采用Dog的实例进行了注入,这样就通过优先级的变换使得IoC容器知道注入哪个具体的实例来满足依赖注入。然后,有时候@Primary 也可以使用在多个类上,也许无论是猫还是狗狗都可能带上@Primary 注解, 其结果是IoC容器还是无法区分采用哪个Bean的实例进行注入, 又或者说我们需要更加灵活的机制来实现注入,那么**@Quelifier** 可以满足你的这个愿望。

它将与@Autowired 组合在一起,通过类型和名称一起找到Bean。我们知道Bean 名称在 Spring IoC 容器中是唯一的标识,通过这个就可以消除歧义性了。此时你是否想起了BeanFactory接口中的这个方法呢?<T> T getBean(String name, Class<T> requiredType) throws BeansException;

代码:

@Component
public class YoungPerson implements Person {@Autowired@Qualifier("dog")private Animal animal = null;@Overridepublic void service() {this.animal.user();}@Overridepublic void setAnimal(Animal animal) {this.animal = animal;}
}

一旦这样声明, Spring IoC 将会以类型和名称去寻找对应的Bean进行注入。根据类型和名称,显然也只能找到狗狗为我们服务了。

🌑带有参数的构造方法类装配

在上面,我们都基于一个默认的情况,那就是不带参数的构造方法下实现依赖注入。但事实上,有些类只有带有参数的构造方法,于是上述的方法都不能再使用了。为了满足这个功能,我们可以使用@Autowired 注解对构造方法的参数进行注入,例如,修改类YoungPerson来满足这个功能。

@Component
public class YoungPerson implements Person {private Animal animal = null;public YoungPerson(@Autowired @Qualifier("dog") Animal animal) {this.animal = animal;}@Overridepublic void service() {this.animal.user();}@Overridepublic void setAnimal(Animal animal) {this.animal = animal;}
}

@Autowired 和@Qualifier 注解,使其注入进来。这里使用@Qualifier 是为了避免歧义性。当然如果你的环境中不是有猫有狗,则可以完全不使用@Qualifier,而单单使用@Autowired就可以了。

二、📝使用属性文件

Java 开发使用属性文件已经十分普遍,所以这里谈谈这方面的内容。在Spring Boot 中使用属性文件,可以采用其默认为我们准备的application.properties,也可以使用自定义的配置文件。 应该说读取配置文件的方法很多, 这里没有必要面面俱到地介绍每一个细节,只是介绍那些最常用的方法。

在SpringBoot项目的application.properties:

database.driverName=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/my
database.username=root
database.password=root

创建个DataBaseProperties类:

/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: DataBaseProperties* @Description: TODO描述* @Date: 2024/1/16 16:07*/
@Component
public class DataBaseProperties {@Value("${database.driverName}")private String driverName = null;@Value("${database.url}")private String url = null;private String username = null;private String password = null;public void setDriverName(String driverName) {System.out.println(driverName);this.driverName = driverName;}public void setUrl(String url) {System.out.println(url);this.url = url;}@Value("${database.username}")public void setUsername(String username) {System.out.println(username);this.username = username;}@Value("${database.password}")public void setPassword(String password) {System.out.println(password);this.password = password;}public String getDriverName() {return driverName;}public String getUrl() {return url;}public String getUsername() {return username;}public String getPassword() {return password;}
}

通过@Value注解, 使用${… }这样的占位符读取配置在属性文件的内容。这里的@Value 注解,既可以加载属性, 也可以加在方法上。
这样就能成功将属性文件内容成功注入了。但是我们可以使用过注解@ConfigurationProperties来简化一下,同样能实现注入,如下:

/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: DataBaseProperties* @Description: TODO描述* @Date: 2024/1/16 16:07*/
@Component
@ConfigurationProperties("database")
public class DataBaseProperties {private String driverName = null;private String url = null;private String username = null;private String password = null;public void setDriverName(String driverName) {System.out.println(driverName);this.driverName = driverName;}public void setUrl(String url) {System.out.println(url);this.url = url;}public void setUsername(String username) {System.out.println(username);this.username = username;}public void setPassword(String password) {System.out.println(password);this.password = password;}public String getDriverName() {return driverName;}public String getUrl() {return url;}public String getUsername() {return username;}public String getPassword() {return password;}
}

这里@ConfigurationProperties注解内的database将会与属性名组成属性的全限定名去配置文件里查找,如属性driveName就会和database组成database.driverName去查找,这里注意一下不需要管字母的大小写,不影响。

如果我不用默认的application.properties文件怎么办呢?现在我们重新创建一个jdbc.properties文件,将原配置文件内容移入其中。
DataBaseProperties 类不变如上,启动类配置如下:

@SpringBootApplication
@PropertySource(value = {"classpath:jdbc.properties"},ignoreResourceNotFound = true)
public class RestartApplication {public static void main(String[] args) {SpringApplication.run(RestartApplication.class, args);}}

在这里插入图片描述
value 可以配置多个配置文件。使用 classpath 前缀, 意味着去类文件路径下找到属性文件;ignoreResourceNotFound 则是是否忽略配置文件找不到的问题。 ignoreResourceNotFound 的默认值为false,也就是没有找到属性文件, 就会报错;这里配置为true,也就是找不到就忽略掉,不会报错。通过运行日志,可以看出成功注入。


总结

以上就是依赖注入和使用配置配置文件的讲解,欢迎大家一起讨论。

相关文章:

  • 《Python 3 基础》- numpy的array,python的list、tuple的区别与联系再辨析
  • 【C++ 程序设计入门基础】- 第4节-函数
  • 分布式搜索引擎--认识
  • 【设计模式】01-前言
  • Spring集成
  • 【镜像制作】OS云主机镜像的制作——以H3C为例
  • SAP 中销售订单占用库存和交货单占用库存取值
  • PaddleSeg的训练与测试推理全流程(超级详细)
  • PHP项目如何自动化测试
  • 机器学习之集成学习概念介绍
  • 导航与定位技术已成为移动机器人的核心技术之一
  • java SSM物业管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计
  • MStart | MStart开发与学习
  • 系列六、Spring Security中的认证 授权 角色继承
  • 从0到1:如何建立一个大规模多语言代码生成预训练模型
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • CSS实用技巧干货
  • Python连接Oracle
  • 高度不固定时垂直居中
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 简析gRPC client 连接管理
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 试着探索高并发下的系统架构面貌
  • 物联网链路协议
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • #{}和${}的区别是什么 -- java面试
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (70min)字节暑假实习二面(已挂)
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (汇总)os模块以及shutil模块对文件的操作
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (南京观海微电子)——COF介绍
  • (正则)提取页面里的img标签
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .apk文件,IIS不支持下载解决
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET Core 中的路径问题
  • .stream().map与.stream().flatMap的使用
  • ??eclipse的安装配置问题!??
  • [ 攻防演练演示篇 ] 利用通达OA 文件上传漏洞上传webshell获取主机权限
  • []Telit UC864E 拨号上网
  • [16/N]论得趣
  • [20171101]rman to destination.txt
  • [AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]
  • [Asp.net mvc]国际化
  • [BUG] Hadoop-3.3.4集群yarn管理页面子队列不显示任务
  • [Bzoj4722]由乃(线段树好题)(倍增处理模数小快速幂)
  • [go 反射] 进阶
  • [hadoop读书笔记] 第十五章 sqoop1.4.6小实验 - 将mysq数据导入HBASE
  • [IE编程] WebBrowser控件中设置页面的缩放
  • [Java性能剖析]Sun JDK基本性能剖析工具介绍