1. 前言

在开发Spring Boot应用时会用到根据条件来向Spring IoC容器注入Bean。比如配置文件存在了某个配置属性才注入Bean

图中红色的部分是说,只有ali.pay.v1.app-id存在于Spring的环境配置中时这个@Configuration标记的类才能注入Spring IoC

这里面的@ConditionalOnProperty就是条件注解系列的一种。它还有很多种来满足各种场景的条件注解:

其实数量远不止截图中这几个,在Spring 家族的其它框架中也有实现。

这里扯得有点远了,今天不是来讲这些条件控制注解的用法的,只是我发现了一个使用条件注解@ConditionalOnProperty无法解决的问题。

条件注入参考往期:Spring Boot 2 实战:使用 @Condition 注解来根据条件注入 Bean

2. 配置文件存在Map结构的场景

下面是一段配置文件:

app:
v1:
foo:
name: felord.cn
description: 码农小胖哥
bar:
name: ooxx.cn
description: xxxxxx

对应配置类:

@Data
@ConfigurationProperties("app")
public class AppProperties {
/**
*
*/
private Map<String, V1> v1 = new HashMap<>(); /**
*
*
* @author felord.cn
* @since 1.0.0.RELEASE
*/
@Data
public static class V1 {
/**
* name
*/
private String name;
/**
* description
*/
private String description; }
}

特殊之处来了,yml配置里的 foobar其实是作为Map中的key来标识V1的,和其它配置参数不同,这个key用户可以随意定义一个String来标识,可能是foo,可能是bar,完全根据开发者的喜好进行主观定义。这个时候你想根据app.v1.*.name(暂时用通配符*)来进行@ConditionalOnProperty判断是行不通的,因为你不确定*的值,该怎么办呢?

3. 解决方案

这里我花了一天的时间去摸索,最开始我认为Spring提供通配符(app.v1.*.name)甚至是SpringEL表达式可以拿到,但是搞了半天无功而返。

突然我想到之前看Spring Security OAuth2源码中有类似的逻辑。用过Spring Security OAuth2相关的都知道Spring Security OAuth2也要求用户自定义一个key来标识自己的OAuth2客户端。比如我用Gitee的:

spring:
security:
oauth2:
client:
registration:
gitee:
client-id: xxxxxx
client-secret: xxxxx

这里的key就是gitee,当然这根据开发者心情决定,甚至你用zhangshan作为key都可以。

Spring Security OAuth2 提供了相关的条件注入思路,下面是其条件注入判断的核心类:

public class ClientsConfiguredCondition extends SpringBootCondition {

   private static final Bindable<Map<String, OAuth2ClientProperties.Registration>> STRING_REGISTRATION_MAP = Bindable
.mapOf(String.class, OAuth2ClientProperties.Registration.class); @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("OAuth2 Clients Configured Condition");
Map<String, OAuth2ClientProperties.Registration> registrations = getRegistrations(context.getEnvironment());
if (!registrations.isEmpty()) {
return ConditionOutcome.match(message.foundExactly("registered clients " + registrations.values().stream() .map(OAuth2ClientProperties.Registration::getClientId).collect(Collectors.joining(", "))));
}
return ConditionOutcome.noMatch(message.notAvailable("registered clients"));
} private Map<String, OAuth2ClientProperties.Registration> getRegistrations(Environment environment) {
return Binder.get(environment).bind("spring.security.oauth2.client.registration", STRING_REGISTRATION_MAP)
.orElse(Collections.emptyMap());
} }

显然OAuth2ClientProperties的结构和我们要验证的AppProperties结构是一样的。所以上面的逻辑是可以抄过来的,它可以将环境配置中的带有不确定key的配置绑定到我们的配置类AppProperties中。核心的绑定逻辑是这一段:

Binder.get(environment).bind("spring.security.oauth2.client.registration", STRING_REGISTRATION_MAP)

首先通过Bindable来声明一个可绑定的数据结构,这里调用了mapOf方法声明了一个Map的数据绑定结构。然后通过绑定的具体操作对象Binder从配置环境接口Environment中提取了spring.security.oauth2.client.registration开头的配置属性并注入到Map中去。既然我们能够获取到了Map,根据什么策略判断就完全掌握在我们手中了。

Bindable为Spring Boot 2.0提供的数据绑定新特性,有兴趣可从spring.io获取更多信息。

接下来不用我说了吧,照葫芦画瓢还有谁不会呢?配合@Conditional注解就能实现根据app.v1下参数的实际情况来动态的进行Bean注入。

4. 总结

今天利用Spring Boot 2.0的数据绑定特性解决了一个实际需求,花了不少时间。当我们解决问题陷入困境时,首先要去想想有没有类似场景以及对应的解决方案。这同样说明平时的积累很重要,很多粉丝的问题其实公众号都有讲过,所以处处留心解释学问。多多留意:码农小胖哥,共同学习,共同进步。

关注公众号:Felordcn 获取更多资讯

个人博客:https://felord.cn

Spring Boot 2.0 的配置绑定类Bindable居然如此强大的更多相关文章

  1. Spring Boot 2.0 教程 | 配置 Undertow 容器

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 文章首发于个人网站 https://ww ...

  2. Spring Boot 2.0的属性绑定

    Spring Boot2.0的属性绑定 原文从Spring boot第一个版本以来,我们可以使用@ConfigurationProperties注解将属性绑定到对象.也可以指定属性的各种不同格式.比如 ...

  3. Spring Boot 2.0 教程 - 配置详解

    Spring Boot 可以通过properties文件,YAML文件,环境变量和命令行参数进行配置.属性值可以通过,@Value注解,Environment或者ConfigurationProper ...

  4. Spring Boot 2.0 配置图文教程

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 ...

  5. Spring Boot 2.0 整合携程Apollo配置中心

    原文:https://www.jianshu.com/p/23d695af7e80 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够 ...

  6. Spring Boot 2.0系列文章(七):SpringApplication 深入探索

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/30/springboot_SpringApplication/ 前言 在 Spring B ...

  7. 详细介绍Spring Boot 2.0的那些新特性与增强

    以Java 8 为基准 Spring Boot 2.0 要求Java 版本必须8以上, Java 6 和 7 不再支持. 内嵌容器包结构调整 为了支持reactive使用场景,内嵌的容器包结构被重构了 ...

  8. Spring Boot 2.0 升级指南

    Spring Boot 2.0 升级指南 前言 Spring Boot已经发布2.0有5个月多,多了很多新特性,一些坑也慢慢被填上,最近有空,就把项目中Spring Boot 版本做了升级,顺便整理下 ...

  9. spring boot 2.0.3+spring cloud (Finchley)3、声明式调用Feign

    Feign受Retrofix.JAXRS-2.0和WebSocket影响,采用了声明式API接口的风格,将Java Http客户端绑定到他的内部.Feign的首要目标是将Java Http客户端调用过 ...

随机推荐

  1. pytorch 损失函数(nn.BCELoss 和 nn.CrossEntropyLoss)(思考多标签分类问题)

    一.BCELoss 二分类损失函数 输入维度为(n, ), 输出维度为(n, ) 如果说要预测二分类值为1的概率,则建议用该函数! 输入比如是3维,则每一个应该是在0--1区间内(随意通常配合sigm ...

  2. Java获取不到请求的真实IP

    问题 最近在写博客浏览量的时候,设计了这么一个逻辑:同一个IP浏览一遍文章,5分钟内不刷新次数.就需要在服务器端得到用户的真实IP,我代码是这样写的(从网上找的方法): public static S ...

  3. 没有它你的DevOps是玩不转的,你信不?

    摘要:架构的选择对于DevOps的实践是至关重要的,从某种程度上来说,架构就是DevOps这场战役的粮草,它是支撑着DevOps成功落地的重要前提. 善用兵者,役不再籍,粮不三载.取用于国,因粮于敌, ...

  4. Panda Global 要点聚焦,区块链在数字医疗的落地应

    据Panda Global,随着区块链技术影响力的不断扩大,其应用性已涉及更加广泛的领域,不断更新着人们的认知.在区块链技术未介入之前,关于医疗行业和数字经济结合早已不是什么新鲜话题,相关研究不少 但 ...

  5. APIO2012 苦无 Kunai

    这题网上貌似还没有完整的题解呢,我来口胡一下~ Description \(W \times H\) 的二维坐标系,\(W, H \le 10^9\) 给 \(n (n \le 10^5)\) 个点 ...

  6. 【题解】「SP34013」SEUG - Seetha’s Unique Game

    这道题一看就是 贪心 . 使放的石头少,就需要石头大. 那么就可以将石头重量排序,从大到小. 这道题里面看似东西很多,但是很多东西都是没有用的.比如说:箱子的长和宽,因为题目中说「每加一个石头,水的高 ...

  7. rsync全网备份low方法

    要求: 1.基本备份要求已知3 台服务器主机名分别为web01.backup .nfs01,主机信息见下表:服务器说明外网IP(NAT) 内网IP(NAT) 主机名称nginx web 服务器10.0 ...

  8. Linux下基于.NET5开发CAX应用

    <<.NET5下的三维应用程序开发>>一文中介绍了如何在.NET5下使用AnyCAD开发应用程序.相比.NET4.x,.NET5一大进步便是可以跨平台,即可以在Linux.Ma ...

  9. 优化Windows电脑常见方法,提高速度,释放硬盘C盘

    开始,我们首先让电脑变得易于使用一,提高开机速度常见的使电脑变卡的原因是:一台电脑同时安装了多个杀毒软件.一台电脑安装多个杀毒软件不仅占用你电脑大量内存.有时甚至会产生冲突,这会导致电脑运行非常缓慢, ...

  10. 解决Linux所有命令不能使用的问题

    解决Linux所有命令不能使用的问题 出现这个问题说明你的 /etc/profile 配置出现了问题,一般是因为path配置出现了问题.排除添加内容中的错误,然后重启一个新窗口执行执行 source ...