springboot原理实战(7)--使用Conditional实现按条件注入bean

简介: springboot原理实战(7)--使用Conditional实现按条件注入bean

目录


本系列博客第一篇就是如何利用spring4+注解方式注入bean,在springboot中提供了按照条件方式注入,我们可以按照条件选择注入或者不注入bean。

本篇博客的内容概要:

1dc618a0ed9580ce8bfa6facb208c08f.png


一、Conditional按照条件注入bean原理


看下Conditional注解源码,它有个value属性类型就是condition的。

5d4c6812c8535adbb050f4ddf2e1bce8.png

看下Condition接口源码:

46a9d80a6e05e4e3b19d57a0ee70bcdf.png

这个接口只有一个方法,是否匹配的matches方法,返回boolean类型。


所以Conditional注解 能够提供基于条件的自动配置,一般配合Condition接口(1个或多个)一起使用,只有接口的实现类都返回true,才装配,否则不装配.。

现在我们自定义一个condition实现类来,结合conditional注解,来切换是否能注入bean。


二、自定义condition接口注入bean


案例:根据代码的字符集。 判断是否注入我们自己定义的bean。

字符集转换接口:


public interface EncodingConvert {
}


有2个实现类:


public class GbkEncodinConvert implements EncodingConvert{
}


public class Utf8EncodingConvert implements EncodingConvert {
}


然后分别写2个condition条件,来切换是否能注入bean的实现类:


public class GbkCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String encoding = System.getProperty("file.encoding");
        if (encoding != null){
            return "gbk".equals(encoding.toLowerCase());
        }
        return false;
    }
}


public class Utf8Condition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String encoding = System.getProperty("file.encoding");
        if (encoding != null){
            return "utf-8".equals(encoding.toLowerCase());
        }
        return false;
    }
}


写个配置类,在配置类里装载bean:


@SpringBootConfiguration
public class EncodingConvertConfiguration {
    @Bean
    @Conditional(Utf8Condition.class)
    public EncodingConvert createUtf8EncodingConvert(){
        return new Utf8EncodingConvert();
    }
    @Bean
    @Conditional(GbkCondition.class)
    public EncodingConvert createGbkEncodinConvert(){
        return new GbkEncodinConvert();
    }
}


可以看到上面的装载方式都加了个条件,@Conditional,参数内容为

根据System.getProperty("file.encoding")的值来判断,是否装载,如果是utf-8就装载createUtf8EncodingConvert类,如果是gbk就装载createGbkEncodinConvert这个类。


我们进入入口函数测试下:


@SpringBootApplication
public class Demo4Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Demo4Application.class, args);
        System.out.println(System.getProperty("file.encoding"));
        System.out.println(context.getBeansOfType(EncodingConvert.class));
        context.close();
    }
}


打印结果:

1dc618a0ed9580ce8bfa6facb208c08f.png

显示注入了bean,因为编码字符集是UTF-8。

我们切换下参数:再试下:

5d4c6812c8535adbb050f4ddf2e1bce8.png

打印结果:

46a9d80a6e05e4e3b19d57a0ee70bcdf.png


同时我们也可以将@Conditional条件放在类上,因为@Conditional的value是个集合,所以可以指定多个,这些条件都满足才能装载类里面的bean:


@SpringBootConfiguration
@Conditional({Utf8Condition.class,GbkCondition.class})
public class EncodingConvertConfiguration {
    @Bean
    public EncodingConvert createUtf8EncodingConvert(){
        return new Utf8EncodingConvert();
    }
    @Bean
    public EncodingConvert createGbkEncodinConvert(){
        return new GbkEncodinConvert();
    }
}


打印结果,因为类的条件为false,所以没有装载bean。

1dc618a0ed9580ce8bfa6facb208c08f.png

我们改成类的条件为true的情况:


@SpringBootConfiguration
@Conditional({Utf8Condition.class})
public class EncodingConvertConfiguration {
    @Bean
    public EncodingConvert createUtf8EncodingConvert(){
        return new Utf8EncodingConvert();
    }
    @Bean
    public EncodingConvert createGbkEncodinConvert(){
        return new GbkEncodinConvert();
    }
}


再次运行入口函数,发现类下的2个bean都被注入了:

5d4c6812c8535adbb050f4ddf2e1bce8.png


三、springboot提供的@Conditional工具类


上面是我们自己定义的@Conditional参数中的condition实现类,其实springboot也提供了很多的实用的@Conditional工具类,我们可以直接去使用就可以。

在里面有很多的condition实现类:

46a9d80a6e05e4e3b19d57a0ee70bcdf.png

注解 说明

@ConditionalOnSingleCandidate 当给定类型的bean存在并且指定为Primary的给定类型存在时,返回true

@ConditionalOnMissingBean 当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true.各类型间是or的关系

@ConditionalOnBean 与上面相反,要求bean存在

@ConditionalOnMissingClass 当给定的类名在类路径上不存在时返回true,各类型间是and的关系

@ConditionalOnClass 与上面相反,要求类存在

@ConditionalOnCloudPlatform 当所配置的CloudPlatform为激活时返回true

@ConditionalOnExpression spel表达式执行为true

@ConditionalOnJava 运行时的java版本号是否包含给定的版本号.如果包含,返回匹配,否则,返回不匹配

@ConditionalOnProperty 要求配置属性匹配条件

@ConditionalOnJndi 给定的jndi的Location 必须存在一个.否则,返回不匹配

@ConditionalOnNotWebApplication web环境不存在时

@ConditionalOnWebApplication web环境存在时

@ConditionalOnResource 要求制定的资源存在

现在我们使用几个测试下


1.@ConditionalOnProperty


某个属性等于某个值的时候才装配。


@SpringBootConfiguration
public class UserConfiguration {
    //某个属性等于某个值的时候才装配
    @Bean
    @ConditionalOnProperty(name="runable.enabled",havingValue = "true")
    public Runnable createRunable(){
        return  () -> {};
    }
 }


这个例子是 如果有runable.enabled这个属性,并且值为true就注入,我们测试下:


@SpringBootApplication
public class Demo4Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Demo4Application.class, args);
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}


运行结果:显示没有装载。


我们在application.properties添加runable.enabled=true


runable.enabled=true

再次测试,显示已经装载进来了:


@ConditionalOnProperty还有个属性matchIfMissing,没有这个属性的时候,让条件变为true或false。


我们做个测试,让其他变为true,同时删除配置文件中的runable.enabled=false这行代码:


@SpringBootConfiguration
public class UserConfiguration {
    //某个属性等于某个值得时候才装配
    @Bean
    @ConditionalOnProperty(name="runable.enabled",havingValue = "true",matchIfMissing = true)
    public Runnable createRunable(){
        return  () -> {};
    }
 }


测试下,在缺少配置文件属性的情况下也注入进来了:

1dc618a0ed9580ce8bfa6facb208c08f.png


2.@ConditionalOnClass


@SpringBootConfiguration
public class UserConfiguration {
    //表示 classpath中存在某个class的时候才装配
    @Bean
    @ConditionalOnClass(name="com.google.gson.Gson")
    public Runnable createGsonRunable(){
        return  () -> {};
    }
 }


我们判断如果项目有这个Gson类就加载否则不加载,现在项目jar里面没有的,应该不注入,我们看下效果:


@SpringBootApplication
public class Demo4Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Demo4Application.class, args);
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}


1dc618a0ed9580ce8bfa6facb208c08f.png运行结果:的确没注入进来。


现在我们让他注入进来,在pom.xml中引入gson包

<dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>


再次运行,显示已经注入进来了:


1dc618a0ed9580ce8bfa6facb208c08f.png


3.@ConditionalOnBean


根据容器中是否存在某个bean来进行装配


@SpringBootConfiguration
public class UserConfiguration {
    //根据容器中是否存在某个bean来进行装配
    @Bean
    @ConditionalOnBean(name="user")
    public Runnable createUserRunable(){
        return  () -> {};
    }
 }


目前项目中没有名字为user的bean,我们打印测试下:

1dc618a0ed9580ce8bfa6facb208c08f.png

的确没注入。

我们写个user的bean:


@Component
public class User {
}


再次测试,显示注入了。

5d4c6812c8535adbb050f4ddf2e1bce8.png

本文的所有内容已经完毕,主要就是按照条件转载bean,可以自定义和使用springboot提供的一些实用的工具类来为我所用。在以后的工作中,具体的类再具体了解吧。


相关文章
|
11天前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
49 6
|
14天前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
35 4
|
17天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
2月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot
|
29天前
|
Java Spring 容器
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
这篇文章讨论了在Spring Boot 3.2.1版本中,同名同类型的bean和@Service注解类之间冲突的问题得到了解决,之前版本中同名bean会相互覆盖,但不会在启动时报错,而在配置文件中设置`spring.main.allow-bean-definition-overriding=true`可以解决这个问题。
64 0
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
|
2月前
|
Java 开发者 数据格式
【Java笔记+踩坑】SpringBoot基础4——原理篇
bean的8种加载方式,自动配置原理、自定义starter开发、SpringBoot程序启动流程解析
【Java笔记+踩坑】SpringBoot基础4——原理篇
|
2月前
|
缓存 NoSQL Java
Springboot实战——黑马点评之秒杀优化
【9月更文挑战第27天】在黑马点评项目中,秒杀功能的优化对提升系统性能和用户体验至关重要。本文提出了多项Spring Boot项目的秒杀优化策略,包括数据库优化(如索引和分库分表)、缓存优化(如Redis缓存和缓存预热)、并发控制(如乐观锁、悲观锁和分布式锁)以及异步处理(如消息队列和异步任务执行)。这些策略能有效提高秒杀功能的性能和稳定性,为用户提供更佳体验。
117 6
|
2月前
|
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‘问题的解决
|
24天前
|
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。
|
Java 应用服务中间件 Maven
传统maven项目和现在spring boot项目的区别
Spring Boot:传统 Web 项目与采用 Spring Boot 项目区别
483 0
传统maven项目和现在spring boot项目的区别