SpringBoot中Bean的条件装配

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 本文总结了在SpringBoot中常用的bean装配方法:* profile* conditional* ConditionalOn

[toc]

概述

众所周知,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不会重复注入
目录
相关文章
|
20天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
26 2
|
1月前
|
Java
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
40 2
|
19天前
|
前端开发 Java 数据格式
SpringBoot中定义Bean的几种方式
本文介绍了Spring Boot中定义Bean的多种方式,包括使用@Component、@Bean、@Configuration、@Import等注解及Java配置类。每种方式适用于不同的场景,帮助开发者高效管理和组织应用组件。
|
2月前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
2月前
|
Java Spring 容器
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
这篇文章讨论了在Spring Boot 3.2.1版本中,同名同类型的bean和@Service注解类之间冲突的问题得到了解决,之前版本中同名bean会相互覆盖,但不会在启动时报错,而在配置文件中设置`spring.main.allow-bean-definition-overriding=true`可以解决这个问题。
100 0
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
|
3月前
|
Java Spring
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
本文介绍了如何在Spring Boot项目中集成Swagger 2.x和3.0版本,并提供了解决Swagger在Spring Boot中启动失败问题“Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerEx”的方法,包括配置yml文件和Spring Boot版本的降级。
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
|
2月前
|
Java Shell C++
Springboot加载注入bean的方式
本文详细介绍了Spring Boot中Bean的装配方法。首先讲解了使用@Component、@Service、@Controller、@Repository等注解声明Bean的方式,并解释了这些注解之间的关系及各自适用的层次。接着介绍了通过@Configuration和@Bean注解定义Bean的方法,展示了其灵活性和定制能力。最后讨论了@Component与@Bean的区别,并提供了在Spring Boot应用中装配依赖包中Bean的三种方法:使用@ComponentScan注解扫描指定包、使用@Import注解导入特定Bean以及在spring.factories文件中配置Bean。
|
5月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
111 11
|
5月前
|
消息中间件 Java Kafka
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
208 5
|
4月前
|
Java Spring 容器
Java SpringBoot 中,动态执行 bean 对象中的方法
Java SpringBoot 中,动态执行 bean 对象中的方法
42 0