概述

众所周知,SpringBoot最腻害的地方就是容器,开发人员的日常工作就是编写bean,并由框架扫描存到容器里面,当程序跑起来的时候,各种bean协同工作完成了软件功能。

那么容器是什么呢?

从概念层面来讲,容器是一个池子;从物理层面来讲,容器是一个内存块。

SpringBoot中默认是以单例形式装载bean的,所以大多数情况下,我们创建的bean对象在程序启动的时候都会被装载到org.springframework.beans.factory.support.DefaultSingletonBeanRegistry-singletonObjects 中,这是一个ConcurrentHashMap

一方面我们需要关注容器中的bean能够提供哪些功能,这是程序工作的细粒度单元,是提供软件功能的基石;另外一方面我们也需要关注bean的装配,处理好它们的依赖关系才能让它们协同工作,共同完成造物主(码农)安排的任务。

本文总结了在SpringBoot中常用的bean装配方法:

  • profile
  • conditional
  • ConditionalOn

Profile

profile 顾名思义,就是环境相关的装配条件。常见的如静态资源的存储,开发环境我们期望存储到硬盘,生产环境可能会存到MinIO中,那么此时可以通过profile根据环境的不同装配不同的文件存储处理bean到容器中,消费者无需关心当前什么环境,直接从容器中获取文件存储处理bean并使用即可。

如下示例代码:


import com.ramble.springbootzgnetsdk.service.DiskResourceServiceImpl;
import com.ramble.springbootzgnetsdk.service.MinIoResourceServiceImpl;
import com.ramble.springbootzgnetsdk.service.ResourceService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile; /**
* Project springboot-zgnetsdk
* Package com.ramble.springbootzgnetsdk.config
* Class ResourceServiceConfig
* date 2024/1/26 10:49
* author cml
* Email liangchen_beijing@163.com
* Description
*/ @Configuration
public class ResourceServiceConfig { @Profile("dev")
@Bean
public ResourceService initDiskResourceService() {
return new DiskResourceServiceImpl();
} @Profile("prod")
@Bean
public ResourceService initMinIoResourceService() {
return new MinIoResourceServiceImpl();
}
}

当前所属环境通过配置文件中的 spring.profiles.active 配置项约束

  • @Profile("dev"):当active的值为dev的时候,此注解注释的方法才会生效,结合@bean注解,方法的返回对象将被注入到容器中。
  • @Profile("!dev"):也可使用! 来表示取反的操作,即不是dev的环境此注解注释的方法才生效

Conditional

Conditional 位于org.springframework.context.annotation中,常常会结合Condition这个接口来完成条件装配,具体来说,Condition的match方法负责编写装配条件,返回true则表示允许装载,否则就不会装载。

假设我们有这样一个需求,程序需要和海康网络设备SDK做集成,那么我们可以在配置文件中通过一个配置项来做开关,hikvision.enable,若此开关打开则装配海康网络设备SDK到容器中,方便其它开发人员使用,否则就不装配。

示例代码如下:


import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Objects; /**
* Project springboot-hcnetsdk
* Package com.ramble.springboothcnetsdk.condition
* Class HikvisionSdkInitCondition
*
* @author
* Email
* Description 海康sdk初始化条件装配
* @date 2024/1/10 13:19
*/
public class HikvisionSdkInitCondition implements Condition { /**
* 装配规则,根据配置文件中hikvision.enable的值,为true或者1则装配
*
* @param context the condition context
* @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return 返回true,允许初始化;否则不允许
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String property = context.getEnvironment().getProperty("hikvision.enable");
if (Objects.nonNull(property)) {
return property.contains("true") || property.contains("1");
} else {
return false;
}
}
}

HikvisionSdkInitCondition ,首先定义一个条件类,此类继承Condition,通过重写matches方法来处理装配条件。

  • context:通过context对象的getEnvironment获取配置文件中的hikvision.enable

    配置项
  • 若hikvision.enable值为true或者1,表示允许初始化,即允许装配到容器中

import com.ramble.springboothcnetsdk.condition.HikvisionSdkInitCondition;
import com.ramble.springboothcnetsdk.support.HikvisionSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; /**
* Project springboot-hcnetsdk
* Package com.ramble.springboothcnetsdk.config
* Class SdkInitConfig
*
* @author
* Email liangchen_beijing@163.com
* Description
* @date 2024/1/10 13:27
*/ @Configuration
public class SdkInitConfig { /**
* 初始化海康sdk。
* 若满足Conditional注解,则向容器中注入 HikvisionSupport
* @return
*/
@Conditional(HikvisionSdkInitCondition.class)
@Bean
HikvisionSupport initHikvisionSdk() {
return new HikvisionSupport();
}
}

SdkInitConfig,定义一个sdk初始化配置类,通过此类将sdk装入容器中。

  • @Configuration:添加此注解,让容器可以扫描到此配置类
  • @Conditional(HikvisionSdkInitCondition.class):Conditional注解需要一个装配条件,当条件允许的时候就执行此方法,而条件具体逻辑已经在HikvisionSdkInitCondition中编写了
  • @Bean:将方法返回值注入容器

@Autowired(required = false)
private HikvisionSupport hikvisionSupport;

在消费的地方注入的时候,必须添加 required = false,否则编译无法通过。

ConditionalOn

ConditionalOn是一个总称,其中包含了很多具体的注解,常用的如下:

  • @ConditionalOnBean:当容器中有指定Bean的条件下进行实例化。
  • @ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。
  • @ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。
  • @ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。
  • @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
  • @ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
  • @ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
  • @ConditionalOnExpression:基于SpEL表达式的条件判断。
  • @ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
  • @ConditionalOnResource:当类路径下有指定的资源时触发实例化。
  • @ConditionalOnJndi:在JNDI存在的条件下触发实例化。
  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。

ConditionalOnProperty

ConditionalOnProperty位于org.springframework.boot.autoconfigure.condition中,表示当配置文件中存在某配置项,并且该项值为具体某值的时候才装配bean。

还是以程序需要和第三方网络设备SDK做集成的需求举例说明。

示例代码如下:


import com.ramble.springbootzgnetsdk.support.ZgSupport;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile; /**
* Project springboot-zgnetsdk
* Package com.ramble.springbootzgnetsdk.config
* Class SdkInitConfig
* date 2024/1/24 10:17
* author
* Email liangchen_beijing@163.com
* Description
*/ @Configuration
@ConditionalOnProperty(value = "sdk.enable", havingValue = "true")
public class SdkInitConfig { @Bean
@ConditionalOnMissingBean
ZgSupport initZgSdk() {
return new ZgSupport();
} }
  • @Configuration:添加此注解,让容器可以扫描到此配置类
  • @ConditionalOnProperty(value = "sdk.enable", havingValue = "true"):当配置文件中存在sdk.enable配置项,并且配置项值为true的时候,才会执行此配置类
  • @Bean:将方法返回值注入容器
  • @ConditionalOnMissingBean:确保此bean不会重复注入

SpringBoot中Bean的条件装配的更多相关文章

  1. Spring生态研习【五】:Springboot中bean的条件注入

    在springboot中,开发的确变的简单了很多,但是,开发者现在希望开发傻瓜式的方便搞定项目中的各种奇怪的需求最好了,不用烧脑,本来程序猿的生活就是枯燥的,不要再给自己添加更多的烦恼. 今天,就为了 ...

  2. 如果你还不知道如何控制springboot中bean的加载顺序,那你一定要看此篇

    1.为什么需要控制加载顺序 springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题.在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功 ...

  3. Spring学习--xml 中 Bean 的自动装配

    Spring IOC 容器可以自动装配 Bean. 只要在 <bean> 的 autowire 属性里指定自动装配的模式. byName(根据名称自动装配):必须将目标 Bean 的名称和 ...

  4. springboot中bean的重定义

    需求描述: 项目中应用其他项目的jar包,然后有些controller中的方法有缺陷需要修改. 1.配置添加 spring.main.allow-bean-definition-overriding= ...

  5. Spring中Beans的自动装配概述

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/beans-autowiring.html: 在之前的做法上会参照这样的顺序:1.使用<bea ...

  6. SpringBoot拦截器中Bean无法注入(转)

    问题 这两天遇到SpringBoot拦截器中Bean无法注入问题.下面介绍我的思考过程和解决过程: 1.由于其他bean在service,controller层注入一点问题也没有,开始根本没意识到Be ...

  7. springBoot按条件装配:Condition

    编码格式转换器接口 package com.boot.condition.bootconditionconfig.converter; /** * 编码格式转换器接口 */ public interf ...

  8. Spring-Boot基于配置按条件装Bean

    背景 同一个接口有多种实现,项目启动时按某种规则来选择性的启用其中一种实现,再具体一点,比如Controller初始化的时候,根据配置文件的指定的实现类前缀,来记载具体Service,不同Servic ...

  9. spring的条件装配bean

    1 应用程序环境的迁移 问题: 开发软件时,有一个很大的挑战,就是将应用程序从一个环境迁移到另一个环境. 例如,开发环境中很多方式的处理并不适合生产环境,迁移后需要修改,这个过程可能会莫名的出现很多b ...

  10. 【归纳】springboot中的IOC注解:注册bean和使用bean

    目前了解的springboot中IOC注解主要分为两类: 1. 注册bean:@Component和@Repository.@Service.@Controller .@Configuration 共 ...

随机推荐

  1. 快来一起玩转LiteOS组件:RHas

    摘要:RHash是一个C语言编写的哈希函数库,用于计算和验证磁力链接和各种消息摘要的控制台实用程序. 本文分享自华为云社区<LiteOS组件尝鲜-玩转RHas>,作者:Lionlace . ...

  2. PPT 动画-树叶摆动

    插入树叶 插入矩形,长宽放大1倍 树叶和矩形组合

  3. python中的代码运行时间获取方式

    python中的代码运行时间获取方式 ​ 我们知道为了提高代码的运行速度,我们需要对书写的python代码进行性能测试,而代码性能的高低的直接反馈就是电脑运行代码所需要的时间. 使用time模块对代码 ...

  4. SE11/SE16N修改表数据

    1.SE11修改方法 首先修改显示格式 选择SE16标准列表 双击这条数据 输入/H,回车,再回车 修改CODE为EDIT,F8 此时,数据已经可以修改了 2.SE16N修改方法 2.1断点修改 输入 ...

  5. Vue | vuex安装失败解决的方法实例

    Vuex是一个专为Vue.js应用程序开发的状态管理模式 下面这篇文章主要给大家介绍了关于vuex安装失败解决的方法,文中通过图文介绍的非常详细,需要的朋友可以参考下 1.报错信息: npm ERR! ...

  6. MyBatis(2):深入学习

    请注意,本文学习自 @我没有三颗心脏 编写日志输出环境配置文件 在开发过程中,最重要的就是在控制台查看程序输出的日志信息,在这里我们选择使用 log4j 工具来输出: 准备工作: 将[MyBatis] ...

  7. 复旦大学2020考研机试题-编程能力摸底试题(A-E)

    A.斗牛 给定五个0~9范围内的整数a1,a2,a3,a4,a5.如果能从五个整数中选出三个并且这三个整数的和为10的倍数(包括0),那么这五个整数的权值即为剩下两个没被选出来的整数的和对10取余的结 ...

  8. PTA 天梯赛 L3-003 社交集群(并查集)

    当你在社交网络平台注册时,一般总是被要求填写你的个人兴趣爱好,以便找到具有相同兴趣爱好的潜在的朋友.一个"社交集群"是指部分兴趣爱好相同的人的集合.你需要找出所有的社交集群. 输入 ...

  9. freeswitch带媒体压力测试方案

    概述 原本的计划是使用sipp完成带媒体压力测试,但是实际测试过程中发现sipp的媒体处理功能有问题(也有可能是我使用的姿势不对). sipp在带媒体的情况下(600路并发开始),出现大量的不响应和响 ...

  10. 每天学五分钟 Liunx 011 | sudo

    回顾前两节,在 001 中介绍了怎么添加用户及用户组,在 010 中介绍了从 client 端 ssh 到 server 的详细过程,那么接下来要介绍的就是是登陆到 server 之后如何切换用户了. ...