【原理篇】二、Bean加载控制

news2024/12/23 15:19:59

文章目录

  • 1、编程式Bean加载控制
  • 2、注解式Bean加载控制
  • 3、@Conditional派生注解
  • 4、Bean依赖的属性配置

Bean的加载控制指根据特定情况对bean进行选择性加载以达到适用于项目的目标

上篇Bean声明的方式中,后4种可以实现对Bean加载的控制,分别是:

  • AnnotationConfigApplicationContext调用register方法
  • @Import导入ImportSelector接口
  • @Import导入ImportBeanDefinitionRegistrar接口
  • @Import导入BeanDefinitionRegistryPostProcessor接口

且上篇中已演示了部分对Bean加载的控制,这里演示编程式和注解式两种。

1、编程式Bean加载控制

下面实现:当A类被加载了,我才将B类的Bean加载。

public class MyImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
	
		try {
			Class<?> clazz = Class.forName("com.plat.bean.Mouse");
			
			if(clazz != null) {  //如果老鼠的类被加载了(注意不关心是不是Bean,只看是不是类加载了)
			
				return new String[]{"com.itheima.bean.Cat"};  //那就把猫这个类注册成Bean
				
			}
			
		} catch (ClassNotFoundException e) {
			//e.printStackException();
			return new String[0];    //条件类老鼠不存在时,返回一个空数组
		}
		
		return null;
	}
}

2、注解式Bean加载控制

上面代码里的判断的逻辑对所有类都大同小异,因此考虑抽成一组注解,公共逻辑部分用AOP去实现就好。Spring提供了@Conditional注解:

在这里插入图片描述

注解的属性类型是一个Condition接口或者其子类,且Condition接口有抽象方法需要实现,这样写还是很繁琐。因此常使用@Conditional注解的派生注解。注意很多派生注解都是SpringBoot的。

<!--非Boot项目记得导入SpringBoot的起步依赖才能找到这些派生注解-->
<dependency>    
	<groupId>org.springframework.boot</groupId>    
	<artifactId>spring-boot-starter</artifactId>    
	<version>2.5.4</version>
</dependency>
<!--注意boot的starter包含Spring的一些依赖,比如spring-context,若旧项目是Spring项目,可直接注释掉相关依赖,省的依赖冲突,版本混乱-->

在这里插入图片描述

以下分别整理这些派生注解。

3、@Conditional派生注解

@ConditionalOnClass:有这个类了(是不是Bean无所谓),就加载下面这个Bean

@Component
@ConditionalOnClass(Mouse.class)
public class Cat{

}
//有老鼠这个类被加载了(注意强调的是类,不管你是不是Bean),就加载Cat这个Bean

使用注解的value属性,这么写有点此地无银三百两了,编译器报错的时候,即导包失败,就说明没这个类,那还判断个啥,所以常用name属性,而不是直接class

@Component
@ConditionalOnClass(name = "com.plat.po.Mouse")
public class Cat{

}

@ConditionalOnMissingClass:没这个时(是不是Bean无所谓),就加载下面这个Bean

@Component
@ConditionalOnMissingClass("com.plat.po.Wolf")   //没有狼来时,加载猫成为一个Bean
public class Cat{

}

这样控制Bean加载的一个很经典的例子就是DataSource,如下:即只有用到了MySQL的驱动类时,再加载dataSource的Bean

public class SpringConfig {
	@Bean
	@ConditionalOnClass(name = "com.mysql.jdbc.Driver")
	public DruidDataSource dataSource(){
		DruidDataSource ds = new DruidDataSource();
		return ds;
	}
}

再往大了想,有一个技术框架的jar包,当你用到某个标志类,那就加载该技术所有的Bean,否则就不加载,此时,使用者只需做一些简单的配置即可。

@ConditionalOnBean : 有这个Bean时,才加载下面的这个Bean

public class SpringConfig {

	@Bean
	@ConditionalOnBean(name="jerry")
	@ConditionalOnMissingBean(name = "com.plat.po.Dog")
	public Cat tom(){
		return new Cat();
	}
}

//不关心有没有老鼠的类被加载,而是要求有叫杰瑞的Bean,且没有狗的Bean,Tom才加载到Ioc中成为Bean

@ConditionalOnNotWebApplication和@ConditionalOnWebApplication :非Web或者Web环境下再加载

注意,这些派生注解同时出现在同一个类上方时,它们之间是且的关系。

4、Bean依赖的属性配置

结论:当业务Bean运行需要一些属性时,抽取一个独立的属性类xxProperties,读取配置文件信息

用一个循序渐进的示例来体验属性配置,先准备几个demo类:

@Data
public class Cat{

	private String name;
	
	private Integer age;
}
@Data
public class Mouse{

	private String name;
	
	private Integer age;
}
//猫和老鼠卡通类
@Component
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	//提供个构造方法给两个属性赋值,不然默认null,下面会空指针
	public CartoonCatAndMouse(){
		cat = new Cat();
		cat.setName("tom");
		cat.setAge(3);
		mouse= new Mouse();
		mouse.setName("jerry");
		mouse.setAge(3);
	}

	public void play(){
		System.out.println(cat.getAge()+"岁的"+cat.getName()+"与"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");
	}
}
//启动类中调用下play方法
@SpringBootApplication
public class App{

	public static void main(String[] args){
		
		ConfigurableApplicationContext ctx = pringApplication.run(app.class);
		
		CartoonCatAndMouse bean = ctx.getBean(CartoonCatAndMouse.class);
		
		bean.play();
	}
}

此时,配置都硬编码了,显然不合理。

优化第一步:引入yaml配置并@ConfigurationProperties读取

cartoon:
  cat:
    name: tom
    age: 3
  mouse:
    name: jerry
    age: 4
@Component
@ConfigurationProperties(prefix = "cartoon")
//加个set,不然cat属性和mouse属性为null
@Data  
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	public void play(){
		System.out.println(cat.getAge()+"岁的"+cat.getName()+"与"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");
	}
}

此时,如果yaml配置中没有相关配置,则对应的对象为null,进而空指针。也就是说,一加@ConfigurationProperties(prefix = “cartoon”),我的类和yaml配置绑死了,没配置,类都受影响。

优化第二步:引入独立配置类CartoonProperties

@ConfigurationProperties(prefix = "cartoon")
@Data
//旧知识点,读取yaml的类的对象必须受Spring容器管控,否则,即使拿到yaml值也无法set给你
@Component
public class CartoonProperties {
	private Cat cat;
	private Mouse mouse;
}

@Component
@Data  
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;
	
	//构造器注入了,用@Autowired也行
	private CartoonProperties cartoonProperties;

	public void play(CartoonProperties cartoonProperties){
		
		this.cartoonProperties = cartoonProperties;
		cat = new Cat();
		cat.setName(cartoonProperties.getCat()!=null && StringUtils.hasText(cartoonProperties.getCat().getName())
					?cartoonProperties.getCat().getName():"tom");  //有则用,无则用默认值
		//Mouse对象同样写法,略
		System.out.println(cat.getAge()+"岁的"+cat.getName()+"与"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了");
	}
}

此时,有个缺点,CartoonProperties类不管用不用,都被强制加载成一个Bean了,但去掉@Component,只留@ConfigurationProperties语法错误 ⇒ 用@EnableConfigurationProperties

优化第三步:@EnableConfigurationProperties,改掉强制加载Bean

@ConfigurationProperties(prefix = "cartoon")
@Data
public class CartoonProperties {
	private Cat cat;
	private Mouse mouse;
}

@ConfigurationProperties和@EnableConfigurationProperties,前者是做属性绑定的,后者是开启属性绑定,并设定对应的目标是谁

@Component  //这个Component也可以去掉,用的时候@Import(CartoonCatAndMouse.class)
@Data  
//即当我加载CartoonCatAndMouse时,就用CartoonProperties.class,并把CartoonProperties类加载成Bean
@EnableConfigurationPropertiesCartoonProperties.class)   
public class CartoonCatAndMouse{

	private Cat cat;

	private Mouse mouse;

	//重复代码,略....

这套模式的亮点有以下几个:

  • 合理的加载配置文件,即你配置了就用你的,没配就用默认值来工作
  • 对于属性类xxxProperties不用强制配置成Bean,使用@EnableConfigurationProperties
  • 对于业务功能的Bean,通常使用@Import将一个类加载成Bean,来解耦强制加载Bean,以降低Spring容器管理Bean的工作量以及强度

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

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

相关文章

0.1+0.2为什么不等于0.3

目录 一、0.10.2是如何执行的&#xff1f;1、转成浮点数2、浮点数相加3、浮点数转成十进制 二、BigDecimal 大家好&#xff0c;我是哪吒。 最近碰到一个问题&#xff0c;很有趣&#xff0c;我就不贴代码了&#xff0c;要不你们会以为我在无中生有。 我现在的心情很复杂&#x…

无人机航迹规划:五种最新智能优化算法(KOA、COA、LSO、GRO、LO)求解无人机路径规划MATLAB

一、五种算法&#xff08;KOA、COA、LSO、GRO、LO&#xff09;简介 1、开普勒优化算法KOA 开普勒优化算法&#xff08;Kepler optimization algorithm&#xff0c;KOA&#xff09;由Mohamed Abdel-Basset等人于2023年提出。五种最新优化算法&#xff08;SWO、ZOA、EVO、KOA、…

如何在Visual Studio上创建项目并运行【超级详细】

工欲善其事&#xff0c;必先利其器。想要学好编程&#xff0c;首先要把手中的工具利用好&#xff0c;今天小编教一下大家如何在史上最强大的编译器--Visual Studio上创建项目。&#x1f357; 一.打开编译器&#x1f357; 双击你电脑上的vs&#xff0c;(2012,2019,2022)都行。&…

【原理篇】四、自定义starter

文章目录 1、案例分析2、业务功能的实现3、中途调试4、开启定时任务打印报表5、引入属性配置类&#xff0c;写活业务参数配置6、拦截器7、开启yml提示功能 做一个记录系统访客独立IP访问次数的功能&#xff0c;并把它自定义成一个starter&#xff0c;实现&#xff1a;在现有项目…

systemctl enable docker.service报错“Failed to execute operation: Bad message“

将docker加入到开机自启&#xff0c;报错&#xff1a; 解决&#xff1a; 重新粘贴复制&#xff1a; [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service Wantsnetwork-online.target…

【基带开发】AD9361通信基础:复数乘法 除法

复数 是实数和虚数的组合 例子&#xff1a;3.6 4i, −0.02 1.2i, 25 − 0.3i, 0 2i 乘法 除法

apple mobile device ethernet

莫名其妙使用了一次apple mobile device ethernet&#xff0c;原本正常的网络突然之间抽筋了&#xff0c;在网卡界面看到有两个&#xff0c;以太网3原本启用状态&#xff0c;禁用恢复。 通过搜索apple mobile device ethernet&#xff0c;在网上看到该答案&#xff0c;原来是接…

DDD技术方案落地实践

1. 引言 从接触领域驱动设计的初学阶段&#xff0c;到实现一个旧系统改造到DDD模型&#xff0c;再到按DDD规范落地的3个的项目。对于领域驱动模型设计研发&#xff0c;从开始的各种疑惑到吸收各种先进的理念&#xff0c;目前在技术实施这一块已经基本比较成熟。在既往经验中总结…

PPT NO.3 如何设置日期自动更新

打开PPT,停留在你想放入时间的那页上&#xff1a; ​ 点击“插入”-->"时间和日期"&#xff1a; 然后会弹出一个窗口&#xff0c; 选择“日期和时间”&#xff0c;自动更新里面可以选择自己喜欢的日期格式&#xff0c;左下角“应用”就是当前的这张ppt&#xff0…

Workbench环境中常见问题

问题描述&#xff1a;1 解决方案&#xff1a;2 问题描述&#xff1a;在WB中启动Fluent&#xff0c;报错&#xff0c;提示 “The requested operatjon requires elevation” 解决方案&#xff1a;这个问题是因为WB主程序没有管理员权限导致。使用管理员权限启动WB后&#xff0c…

机器学习练习1

线性回归 数据集模型,第一列代表人口,第二列代表利润 此处的线性回归 与 常规的 y wx b 不同的是 将b换成了w的一部分 故需要在数据集x前面加个1, 求出b . 可以简化计算,只需要一个乘法就可以

音频恢复怎么做?这3招很管用!

“我是一名电台主播&#xff0c;所以经常需要用电脑录制并保存一些音频&#xff0c;今天想对某期节目进行加工时&#xff0c;突然有一部分音频丢失了&#xff0c;有什么方法能找回这些音频吗&#xff1f;” 在日常工作中&#xff0c;或许我们也会将很多重要的音频文件保存在电脑…

论文速览 | TRS 2023: 使用合成微多普勒频谱进行城市鸟类和无人机分类

注1:本文系“最新论文速览”系列之一,致力于简洁清晰地介绍、解读最新的顶会/顶刊论文 论文速览 | TRS 2023: Urban Bird-Drone Classification with Synthetic Micro-Doppler Spectrograms 原始论文:D. White, M. Jahangir, C. J. Baker and M. Antoniou, “Urban Bird-Drone…

【CSP认证考试】202309-1:坐标变换(其一)100分解题思路+代码

解题思路 暴力解决&#xff0c;不考虑时空开销就一直用for循环也可以做出来。按照题目意思输入两个数组&#xff0c;然后将第一个输入的数组的x部分累加起来记作x&#xff0c;再将y部分累加起来记作y。再将第二个数组的x部分都加上x&#xff0c;y部分加上y。最后输出第二个数组…

Git的入门详细教程

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《git》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这…

网络原理---拿捏TCP机制原理

文章目录 确认应答机制超时重传机制连接管理机制三次握手&#xff08;建立连接&#xff09;三次握手的流程三次握手的状态转换 四次挥手&#xff08;断开连接&#xff09;四次挥手的流程四次挥手的状态转换 滑动窗口机制流量控制机制拥塞控制机制延迟应答机制捎带应答机制粘包问…

网络工程师回顾学习(第一部分)

根据书本目录&#xff0c;写下需要记忆的地方&#xff1a; 参考之前的笔记&#xff1a; 网络工程师回答问题_one day321的博客-CSDN博客 重构第一部分需要记忆的&#xff1a; 第一章&#xff1a;计算机网络概论 计算机网络的定义和分类&#xff1a;计算机网络是指将地理位…

【Linux】:git基本操作_添加文件_两种场景_查看.git文件 || git修改文件 || 版本回退

&#x1f3af;添加⽂件–场景⼀ &#x1f3af;在包含.git的⽬录下新建⼀个ReadMe⽂件&#xff0c;我们可以使⽤ git add 命令可以将⽂件添加到暂存区&#xff1a; • 添加⼀个或多个⽂件到暂存区&#xff1a; git add [file1] [file2] … • 添加指定⽬录到暂存区&#xff0c;…

【项目总结】基于SpringBoot+Ansj分词+正倒排索引的Java文档搜索引擎项目总结

文章目录 项目介绍&#xff08;开发背景&#xff09;主要用到的技术点前端后端Ansj分词实现索引模块实现Parser类实现Index类完善Parser类优化制作索引速度 实现搜索模块实现DocSearcher类处理暂停词 项目编写过程中遇到的困难点上传部署总结 项目介绍&#xff08;开发背景&…

【紫光同创国产FPGA教程】【PGC1/2KG第六章】密码锁实验例程

本原创教程由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处 适用于板卡型号&#xff1a; 紫光同创PGC1/2KG开发平台&#xff08;盘古1K/2K&#xff09; 一&#xff1a;盘古1K/2K开发板&#xff08;紫光同创PGC…