最新要闻
- 【全球新视野】问界M5智驾版发布全新OTA升级 加入全新智驾领航辅助等功能
- 真世界霸主!任天堂第一方收入远超索尼、微软
- 6月北京口岸出入境人员突破88万,再创今年出入境流量新高 全球观点
- 特斯拉将采用线控转向技术 Yoke方向盘能否全面普及? 环球要闻
- 20万买“小奔驰”图什么?
- 卷疯了!全新马自达3昂克赛拉正式上市 9.99万到底值不值?_观点
- 中非经贸博览会指定用车,岚图梦想家到底哪里好? 焦点热议
- AMD食言了!说好的显卡性能神技HYPR-RX跳票
- 至高优惠200元 华为MatePad Air 原野绿新色上市开启预售 天天快资讯
- 新机蓄势待发!Switch后续机型开发套件或已抵达西班牙-环球观热点
- 汪小菲“趁火打劫”成功,将接子女回北京,大s妥协原因引热议_全球热头条
- 大V吐槽极氪倒车影像“骗人”bug:让他撞了两次车
- 马斯克缺钱?推特限制用户浏览量:不开会员每天只能看1000条_今日视点
- 动态:硕士被曝制作全校学生颜值打分网站供人查看 人大回应
- 当前关注:excel图表制作折线图数据选择_excel图表制作折线图
- 华为智选车将建汽车独立门店 知情人士:经销商加盟 华为话语权更大
手机
光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯
- 搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注
- 视点!美国首位女总统即将诞生?拜登恐怕要提前下岗,美政坛迎来变局?
- 当前速递!用理想仪器实现更好的颗粒 德国新帕泰克亮相CPHI & PMEC China获好评
- 微粒贷怎么申请开通 开通方法如下
- 焦点简讯:心疼!这位40岁的云南缉毒警,已是满头白发
家电
Jar包开发之【有之则用,无之则禁】|世界新要闻
最近在开发一个热部署平台,应用接入平台需要依赖我们提供一个代理包,为应用提供,订阅热补命令、往注册中心写应用地址信息,解析命令进行热部署的能力。
应用需要在平台配置该应用的发布订阅的组件信息。然后应用在启动的时候取注册这个监听。当平台发布热补命令的时候,所有监听到的应用就能接收到命令,进而进行热补处理。
发布订阅
作为平台开发,必须兼容更多组件,才能够吸收更多的项目应用接入平台。那就意味着该代理包必须尽可能多的支持具有发布订阅功能的组件。常见的具有发布订阅的组件有
(资料图片仅供参考)
- zookeeper
- nacos
- redis
- mq
等等,一般的mq都具有发布订阅的功能,这里就不展开细分的mq。
考虑到公司现有项目的组件使用情况, 当前支持了zookeeper、nacos、redis这三种。
有之则取
因为应用是无法决定或感知接入的应用用了什么发布订阅组件的,因此应用接入平台,除了依赖代理包提供热部署能力外,还需要到平台配置自己的组件信息。代理包通过调用平台接口而获取到配置。
spring redis注册监听
redis注册监听,是在项目启动时候完成。需要创建一个RedisMessageListenerContainer。而RedisMessageListenerContainer的创建,依赖一个RedisConnectionFactory实例,当项目本是就有使用spring redis的情况下,会自动配置一个RedisConnectionFactory bean
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
@AutoConfiguration@ConditionalOnClass({RedisOperations.class})@EnableConfigurationProperties({RedisProperties.class})@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})public class RedisAutoConfiguration { public RedisAutoConfiguration() { } ...}
其中的
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
扫描了两个配置类LettuceConnectionConfiguration和JedisConnectionConfiguration
通过查看他们的源码:
LettuceConnectionConfiguration.java
@Configuration( proxyBeanMethods = false)@ConditionalOnClass({RedisClient.class})@ConditionalOnProperty( name = {"spring.redis.client-type"}, havingValue = "lettuce", matchIfMissing = true)class LettuceConnectionConfiguration extends RedisConnectionConfiguration { LettuceConnectionConfiguration(RedisProperties properties, ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfigurationProvider, ObjectProvider clusterConfigurationProvider) { super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider); } @Bean( destroyMethod = "shutdown" ) @ConditionalOnMissingBean({ClientResources.class}) DefaultClientResources lettuceClientResources(ObjectProvider customizers) { Builder builder = DefaultClientResources.builder(); customizers.orderedStream().forEach((customizer) -> { customizer.customize(builder); }); return builder.build(); } @Bean @ConditionalOnMissingBean({RedisConnectionFactory.class}) LettuceConnectionFactory redisConnectionFactory(ObjectProvider builderCustomizers, ClientResources clientResources) { LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool()); return this.createLettuceConnectionFactory(clientConfig); } ... }
注册了一个LettuceConnectionFactory bean,其是RedisConnectionFactory 的实现类。
JedisConnectionConfiguration.java
@Configuration( proxyBeanMethods = false)@ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class})@ConditionalOnMissingBean({RedisConnectionFactory.class})@ConditionalOnProperty( name = {"spring.redis.client-type"}, havingValue = "jedis", matchIfMissing = true)class JedisConnectionConfiguration extends RedisConnectionConfiguration { JedisConnectionConfiguration(RedisProperties properties, ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfiguration, ObjectProvider clusterConfiguration) { super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration); } @Bean JedisConnectionFactory redisConnectionFactory(ObjectProvider builderCustomizers) { return this.createJedisConnectionFactory(builderCustomizers); } ... }
注册了一个JedisConnectionFactory bean,其也是RedisConnectionFactory 的实现类。
这里先了解一下应用本身如果用了spring redis,必定会有一个RedisConnectionFactory 的spring bean,RedisConnectionFactory Redis连接的线程安全工厂,维护了客户与服务器的连接。至于那个才是生效配置,我们下面【无之禁用】再介绍,因为其息息相关。
项目本身RedisConnectionFactory有了,我们就尽可能去复用,而不是自己去创建多一个连接工程,这样再微服务场景下,如果项目的实例部署很多,会给redis带来极大的负担。
如何判断拿项目侧是否激活了spring redis自动配置呢?
private static final String REDIS_STANDALONE = "spring.redis.host"; private static final String REDIS_SENTINEL = "spring.redis.sentinel.nodes"; private static final String REDIS_CLUSTER = "spring.redis.cluster.nodes";/** * 在的环境加载末期判断当前应用是否使用了redis自动配置 * @param environment * @return */ private boolean hasRedisAutoConfigure(ConfigurableEnvironment environment) { boolean standalone = !Objects.isNull(environment.getProperty(REDIS_STANDALONE)); boolean sentinel = !Objects.isNull(environment.getProperty(REDIS_SENTINEL)); boolean cluster = !Objects.isNull(environment.getProperty(REDIS_CLUSTER)); return standalone || sentinel || cluster; }
当springboot项目启用了spring redis的自动配置,环境中肯定会有关于spring redis的相关配置。在判断的时候,要注意redis常见的三种模式进行判断:Standalone\ sentinel\cluster
有些小伙伴可能会想,单单判断节点信息,可以确认是否启用了吗?如果的我只配置了节点信息,其他没有怎么办?
首先,如果只配置了节点,的确会让我们该处理器认为他启用了redis的自动配置,尽管他会因为缺了其他配置导致redis没有被自动配置。但可以被确定的一点是:当你缺了配置,导致redis没有被自动配置,甚至项目根本就启动失败,这种情况其实不是我们要去适配的情况,我们的处理都是默认认为你项目被正确启动,正常运行的情况。
无之则禁
当能够判断redis没有被自动配置后,我们就可以处理由于的接入我们的jar包导致spring redis 被无意触发的情况,进而将自动配置禁用掉。
springboot提供了禁用自动配置的口子,那就是在配置文件中,在spring.autoconfigure.exclude中配置自动配置类的类路径。在项目启动时,读取环境变量的时候,会读取改配置,然后在处理自动配置阶段,将这些类排除不处理。
那有人就会说,那在项目的配置文件中,在spring.autoconfigure.exclude中添加spring redis 自动配置类不就好了吗?正常情况下的确是的。但不要忘了,我们这个是jar包的处理,要尽可能做到对应用的无侵入,能给做的要自己做了,让用户开箱即用。
因此我们需要自己处理。
从上面的阐述也可以看的出来。排除自动配置的配置,本质上是在项目的prepareEnvironment,也就是准备环境阶段中,读取了配置用于后续处理。你可以简单的理解,springboot需要你配置的东西,都将作为项目的一部分“环境”,这不过绝大部分的环境配置都是springboot 约定好了,你按他的要求配置,他到约定的地方去读取。
spring boot提供了一个的用于自定义环境变量的钩子。那就是EnvironmentPostProcessor
。
@FunctionalInterfacepublic interface EnvironmentPostProcessor { /** * Post-process the given {@code environment}. * @param environment the environment to post-process * @param application the application to which the environment belongs */ void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);}
EnvironmentPostProcessor是一个FunctionalInterface,允许在刷新应用程序上下文之前自定义应用程序的环境。 EnvironmentPostProcessor的实现必须在META-INF/spring中注册。使用该类的完全限定名作为键。如果实现希望以特定顺序调用,它们可以实现Ordered接口或使用@Order注释。
使用上来说,只需要实现这个接口,然后将实现类注册给spring容器即可。
有了这个钩子,不意外着万事大吉了。还有很多事情需要考虑:
- 确定好自定义配置失效的优先级
- 不影响应用原有的配置。
springboot 的配置来自于很多地方,也就是说它会总各个地方搜集到应用环境的配置。称之为PropertiesSource。这么多个PropertiesSource,springboot只会为每一个key值处理一次,后续再有相关配置就不处理,因此配置的优先级至关重要。
由于每个PropertiesSource里面的配置其实没有规律的,取决于实际的配置情况,而且每个数组配置的配置项,最终都会解析成key[n]的字符串形式,你无法直接获取到PropertiesSource里面是否的有的spring.autoconfigure.exclude的配置,有的话到底配置了几个。
比如某一个PropertiesSource里面有spring.autoconfigure.exclude配置,配置了3个,那将会被解析成spring.autoconfigure.exclude[0]、spring.autoconfigure.exclude[1]、spring.autoconfigure.exclude[3],这些键值不能重复,重复会报错。因此在拥有spring.autoconfigure.exclude配置的PropertiesSource里面塞入我们的的自定义配置,不是一个很好的办法。
我们只能自己创建一个PropertiesSource。
创建一个新的PropertiesSource放到环境中去,我们要确保两点。
- 搜集项目原有的完整的spring.autoconfigure.exclude配置,放入我们新建的PropertiesSource,然后将我们自定义配置放在最后。
- 将新建的PropertiesSource放置于PropertiesSource列表的最前面,确保最先处理。
要确保能够搜集到全量的配置,就必须使得我们EnvironmentPostProcessor处理器最后处理。order必须最大,优先级最低。
因此最终的实现如下:
@Configuration(proxyBeanMethods = false)public class RedisCheckProcessor implements EnvironmentPostProcessor, Ordered { private static final String REDIS_STANDALONE = "spring.redis.host"; private static final String REDIS_SENTINEL = "spring.redis.sentinel.nodes"; private static final String REDIS_CLUSTER = "spring.redis.cluster.nodes"; public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap"; private static final String SPRING_AUTOCONFIGURE_EXCLUDE_KEY = "spring.autoconfigure.exclude"; private static final String EXCLUDE_AUTO_CONFIG_SOURCE_NAME = "excludeAutoConfig"; private static final String EXCLUDE_AUTO_CONFIG = "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration"; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 排除springCloud场景 if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { return; } // 禁用redis自动配置 if (!hasRedisAutoConfigure(environment)) { // 覆写应用spring.autoconfigure.exclude配置 List excludes = new ArrayList<>(); MutablePropertySources mutablePropertySources = environment.getPropertySources(); mutablePropertySources.forEach(propertySource -> { for (int i = 0; i < Integer.MAX_VALUE; i++) { String key = SPRING_AUTOCONFIGURE_EXCLUDE_KEY + "[" + i + "]"; Object value = propertySource.getProperty(key); if (Objects.isNull(value)) { break; } if (value instanceof String) { String exclude = (String) value; if (StringUtils.isNotBlank(exclude)) { excludes.add(exclude); } } } }); // 添加新增排除配置,并将PropertySources激活时机置于application.yml激活时机之前以生效设置 excludes.add(EXCLUDE_AUTO_CONFIG); Properties properties = new Properties(); for (int i = 0; i < excludes.size(); i++) { properties.setProperty( SPRING_AUTOCONFIGURE_EXCLUDE_KEY + "[" + i + "]", excludes.get(i)); } PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(EXCLUDE_AUTO_CONFIG_SOURCE_NAME, properties); environment.getPropertySources() .addFirst(propertiesPropertySource); } } @Override public int getOrder() { // 最后加载,确保application.yml配置读取完成 return Ordered.LOWEST_PRECEDENCE; } /** * 在的环境加载末期判断当前应用是否使用了redis自动配置 * @param environment * @return */ private boolean hasRedisAutoConfigure(ConfigurableEnvironment environment) { boolean standalone = !Objects.isNull(environment.getProperty(REDIS_STANDALONE)); boolean sentinel = !Objects.isNull(environment.getProperty(REDIS_SENTINEL)); boolean cluster = !Objects.isNull(environment.getProperty(REDIS_CLUSTER)); return standalone || sentinel || cluster; }}
关键词:
Jar包开发之【有之则用,无之则禁】|世界新要闻
CentOS 9 x64 使用 Nginx、Supervisor 部署 Go/Golang 服务
spring启动流程 (2) Bean实例化流程 世界短讯
【全球新视野】问界M5智驾版发布全新OTA升级 加入全新智驾领航辅助等功能
真世界霸主!任天堂第一方收入远超索尼、微软
6月北京口岸出入境人员突破88万,再创今年出入境流量新高 全球观点
特斯拉将采用线控转向技术 Yoke方向盘能否全面普及? 环球要闻
20万买“小奔驰”图什么?
卷疯了!全新马自达3昂克赛拉正式上市 9.99万到底值不值?_观点
中非经贸博览会指定用车,岚图梦想家到底哪里好? 焦点热议
AMD食言了!说好的显卡性能神技HYPR-RX跳票
至高优惠200元 华为MatePad Air 原野绿新色上市开启预售 天天快资讯
新机蓄势待发!Switch后续机型开发套件或已抵达西班牙-环球观热点
汪小菲“趁火打劫”成功,将接子女回北京,大s妥协原因引热议_全球热头条
大V吐槽极氪倒车影像“骗人”bug:让他撞了两次车
马斯克缺钱?推特限制用户浏览量:不开会员每天只能看1000条_今日视点
动态:硕士被曝制作全校学生颜值打分网站供人查看 人大回应
Python 使用 NetworkX
当前关注:excel图表制作折线图数据选择_excel图表制作折线图
华为智选车将建汽车独立门店 知情人士:经销商加盟 华为话语权更大
20万内仅有的换电SUV来了!吉利睿蓝7即将上市
切比雪夫距离
全球微动态丨日方首度确认!中国两栖攻击舰路过鹿儿岛
俩南方小伙山东旅游点6个菜吃不完 被北方菜“上了一课”-世界短讯
广东一电摩自燃 30辆车被连累烧成空壳!千万别在室内充电
俄罗斯恢复飞往古巴的定期航班
人大金仓学习之一_kwr的简单学习_环球看点
前端Vue基于腾讯地图Api实现的选择位置组件 返回地址名称详细地址经纬度信息|世界焦点
今日要闻!民主测评软件需求分析与实现
6大人争接萌娃放学 最后姥爷赢麻了:画面让网友笑翻-环球快资讯
美国一女子去世7只猫继承217万遗产引热议:上百人抢着领养
公共气象服务中心:预警“叫应”联动服务信息化建设_每日热门
每日看点!选读SQL经典实例笔记01_检索和排序
学霸、体育才女!谷爱凌大一课程全A 曾自称成绩100%自己努力得到的
华硕全球首发RTX 4060 Ti神卡:能扩展M.2 SSD
当前短讯!威马疑又停薪,仅发中高层工资!“讨债大军”攻陷官方微博
国科大校长谈龙芯之母说着说着就哭了:别让“卡脖子”遏制我们发展 当前报道
【WALT】update_window_start() 代码详解
国外RTX 4090销售陷入困境 愁坏了很多零售商
孙女给生活不能自理爷爷洗澡引争议 当事人:孝心不分男女 天天头条
升级iOS 17还要交钱?跟iOS 16对比后拔草了!立省688元
今日播报!Apple Watch新款感知不强!500块的S4最香
做“有温度”的就业指导服务|世界新视野
每日热文:大姨妈不正常怎么办吃什么好呢_大姨妈不正常怎么办
【世界速看料】茅坑里的石头是哪种人_茅坑里的石头是什么意思
【聚看点】go src - sync.Map
环球观天下!Web安全-渗透测试-权限提升01
焦点快报!116-104!山东高速力克北京首钢,山东试训外援首秀轻取19分
63岁男子向熊猫投掷苹果惹众怒 后果来了:被终身禁入
宝镇
天玑9200+性价王者预定!Redmi K60 Ultra第三方保护壳现身|全球新视野
支持正版:英国为采购Office刚花了71亿|天天快看
全球热资讯!7月新农合再涨40元,一市明确每人590元,今年你还交吗?
长城站起来!哈弗枭龙系列第1万台下线:6月热销6098台暴增97%
复兴号正式在青藏铁路投入运行:海拔3000米 零下40度也能跑
曼高德谈《夺宝奇兵5》
天天快讯:传奇设计大师去世:“我从不设计丑陋东西”
焦点精选!第一部8.0分不值一提!美国年度科幻大片续集来了
协鑫宣布年产36万吨储能正极材料项目今日投产-世界热讯
张雪峰老师推荐的十大高薪专业引发热议:计算机类霸榜 文科中仅法语入围 天天速读
丰田章男工资曝光:9.99亿日元丰田汽车史上最高! 环球微头条
RPM安装的Oracle19c 修改init.ora进行修复以及最简单开机启动Oracle的方法 世界百事通
markdown终极指南 观点
100%纯棉、亲肤舒适:布静观纯棉短袖13元、长袖16元
环球视讯!多地高温难耐 黑龙江却遭遇特大冰雹:砸碎几乎整栋楼玻璃
环球新消息丨安卓性能标杆!一加Ace 2 Pro来了:5200mAh+骁龙8 Gen2
米体:汉达可能降薪续约留在国米,下赛季担任二号门将 全球微动态
mosquitto的基本参数使用
全球热议:探究直播app源码技术:视频上传功能
当前资讯!02修剪标准&&方法
环球滚动:ThinkPHP6.0 链式SQL语句
【世界速看料】见证历史!苹果成全球首家市值3万亿美元公司 意味着什么?
男生旅行3年收集50余个城市水土:覆盖30个省份 比打卡拍照更有意义
云南男子外出干活遇超大菌子 网友羡慕:菌之大一锅炖不下_当前聚焦
网友吐槽一条街三四十个井盖 官方回应:雨污分流、将铺上沥青
当前热议!五年中考三年模拟八上物理答案
全球观热点:MinIO-对象存储简单使用
JS必学的11个工具方法(避免重复造轮子) 世界快看
IDA的使用2_全球新视野
荷兰DUV光刻机一律管制出口?一文看懂|环球快播
埃安6月销量45013辆创历史新高 今年已卖出比亚迪一个月销量
特斯拉加入价格乱战!买这两款车最高优惠4.5万元
国内高端手机市场除了苹果iPhone 还能有谁?! 焦点要闻
2023年全国铁路暑期运输启动:满血复活 暴涨超70%
焦点热议:使用Gitee或GitHub托管Maven仓库JAR包的便捷方法
灭霸演员是谁_ 芝士回答
世界短讯!心爱的小乌龟死了:主人把它做成《七龙珠》龟仙人手办!
担心的事情发生了!泰国女游客卷入电动步道腿被夹断
火车站保洁阿姨不慎弄脏乘客衣服跪地道歉 公司回应:已和解 世界观焦点
蔚来智能系统Banyan 2.0正式发布:超120项功能 号称全场景领先
天天快播:一键打开动态日历 锁定2023下半年这些大事!
本田飞度彻底无语!新款马自达昂克赛拉官降3万:8.99万起_今头条
小米下一代旗舰还用1英寸主摄!1.33英寸还得等|快报
天天头条:今天起全国汽车实施国6排放标准6b阶段!对老车年检有何影响?官方解答
Web安全-渗透测试-waf绕过02
每日快讯!微服务设计:集成
vue中封装服务器地址/接口与设置请求头
天津关于调整2023年住房公积金缴存额的通知政策解读
环球讯息:你敢吃吗?知了肉初上市400元一斤:号称高蛋白
不用求助“拍瓜师”!西瓜甜不甜:看这里就知道