0


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

在这里插入图片描述

** 🏡浩泽学编程:个人主页
** 🔥 推荐专栏
:《深入浅出SpringBoot》《java项目分享》
《RabbitMQ》《Spring》《SpringMVC》🛸学无止境,不骄不躁,知行合一

文章目录


前言

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


一、🌕依赖注入

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

//定义人类接口publicinterfacePerson{voidservice();voidsetAnimal(Animal animal);}//定义动物接口publicinterfaceAnimal{voiduser();}//定义狗@ComponentpublicclassDogimplementsAnimal{@Overridepublicvoiduser(){System.out.println("狗【"+Dog.class.getSimpleName()+"】是用来看门的");}}//定义年轻人@ComponentpublicclassYoungPersonimplementsPerson{@AutowiredprivateAnimal animal =null;@Overridepublicvoidservice(){this.animal.user();}@OverridepublicvoidsetAnimal(Animal animal){this.animal = animal;}}//定义配置类@Configuration@ComponentScan("com.dragon.restart")//所有的包和类都在restart下publicclassAppConfig{}

测试类:

ApplicationContext ctx =newAnnotationConfigApplicationContext(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),猫可以为我们抓老鼠, 于是我们又创建了一个猫的类。

@ComponentpublicclassCatimplementsAnimal{@Overridepublicvoiduser(){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;
@ComponentpublicclassYoungPersonimplementsPerson{@AutowiredprivateAnimal dog =null;@Overridepublicvoidservice(){this.dog.user();}@OverridepublicvoidsetAnimal(Animal animal){this.dog = animal;}}

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

@Autowired(required = false)

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

@Override@Autowiredpublicvoid setAnimal (Animal animal){this.animal = animal;

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

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

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

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

@Component@PrimarypublicclassDogimplementsAnimal{@Overridepublicvoiduser(){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;

代码:

@ComponentpublicclassYoungPersonimplementsPerson{@Autowired@Qualifier("dog")privateAnimal animal =null;@Overridepublicvoidservice(){this.animal.user();}@OverridepublicvoidsetAnimal(Animal animal){this.animal = animal;}}

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

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

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

@ComponentpublicclassYoungPersonimplementsPerson{privateAnimal animal =null;publicYoungPerson(@Autowired@Qualifier("dog")Animal animal){this.animal = animal;}@Overridepublicvoidservice(){this.animal.user();}@OverridepublicvoidsetAnimal(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
 */@ComponentpublicclassDataBaseProperties{@Value("${database.driverName}")privateString driverName =null;@Value("${database.url}")privateString url =null;privateString username =null;privateString password =null;publicvoidsetDriverName(String driverName){System.out.println(driverName);this.driverName = driverName;}publicvoidsetUrl(String url){System.out.println(url);this.url = url;}@Value("${database.username}")publicvoidsetUsername(String username){System.out.println(username);this.username = username;}@Value("${database.password}")publicvoidsetPassword(String password){System.out.println(password);this.password = password;}publicStringgetDriverName(){return driverName;}publicStringgetUrl(){return url;}publicStringgetUsername(){return username;}publicStringgetPassword(){return password;}}

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

/**
 * @Version: 1.0.0
 * @Author: Dragon_王
 * @ClassName: DataBaseProperties
 * @Description: TODO描述
 * @Date: 2024/1/16 16:07
 */@Component@ConfigurationProperties("database")publicclassDataBaseProperties{privateString driverName =null;privateString url =null;privateString username =null;privateString password =null;publicvoidsetDriverName(String driverName){System.out.println(driverName);this.driverName = driverName;}publicvoidsetUrl(String url){System.out.println(url);this.url = url;}publicvoidsetUsername(String username){System.out.println(username);this.username = username;}publicvoidsetPassword(String password){System.out.println(password);this.password = password;}publicStringgetDriverName(){return driverName;}publicStringgetUrl(){return url;}publicStringgetUsername(){return username;}publicStringgetPassword(){return password;}}

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

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

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

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


总结

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

标签: spring boot 后端 java

本文转载自: https://blog.csdn.net/weixin_62951900/article/details/135626611
版权归原作者 浩泽学编程 所有, 如有侵权,请联系我们删除。

“SpringBoot:详解依赖注入和使用配置文件”的评论:

还没有评论