日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

springboot项目接入配置中心,实现@ConfigurationProperties的bean属性刷新方案

發(fā)布時(shí)間:2025/3/8 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot项目接入配置中心,实现@ConfigurationProperties的bean属性刷新方案 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

  配置中心,通過(guò)key=value的形式存儲(chǔ)環(huán)境變量。配置中心的屬性做了修改,項(xiàng)目中可以通過(guò)配置中心的依賴(lài)(sdk)立即感知到。需要做的就是如何在屬性發(fā)生變化時(shí),改變帶有@ConfigurationProperties的bean的相關(guān)屬性。

配置中心

  在讀配置中心源碼的時(shí)候發(fā)現(xiàn),里面維護(hù)了一個(gè)Environment,以及ZookeeperPropertySource。當(dāng)配置中心屬性發(fā)生變化的時(shí)候,清空Z(yǔ)ookeeperPropertySource,并放入最新的屬性值。

  

public class ZookeeperPropertySource extends EnumerablePropertySource<Properties>

  

  ZookeeperPropertySource重寫(xiě)了equals和hahscode方法,根據(jù)這兩個(gè)方法可以判定配置中心是否修改了屬性。

  

配置中心定義的屬性變量

message.center.channels[0].type=HELIUYAN message.center.channels[0].desc=和留言系統(tǒng) message.center.channels[1].type=EC_BACKEND message.center.channels[1].desc=電商后臺(tái) message.center.channels[2].type=BILL_FLOW message.center.channels[2].desc=話(huà)費(fèi)和流量提醒 message.center.channels[3].type=INTEGRATED_CASHIER message.center.channels[3].desc=綜合收銀臺(tái)message.center.businesses[0].type=BIZ_EXP_REMINDER message.center.businesses[0].desc=業(yè)務(wù)到期提醒 message.center.businesses[0].topic=message-center-biz-expiration-reminder-topic message.center.businesses[1].type=RECHARGE_TRANSACTION_PUSH message.center.businesses[1].desc=充值交易實(shí)時(shí)推送 message.center.businesses[1].topic=message-center-recharge-transaction-push-topicmessage.center.businesses2Channels[BIZ_EXP_REMINDER]=EC_BACKEND message.center.businesses2Channels[RECHARGE_TRANSACTION_PUSH]=INTEGRATED_CASHIERmessage.center.bizTypeForMsgType[RECHARGE_TRANSACTION_PUSH]=data.type:pay-finish,data.type:rechr-finish,data.type:refund-finish

java屬性配置映射類(lèi)

import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.List; import java.util.Map; import java.util.Objects;/*** @author hujunzheng* @create 2018-06-28 11:37**/ @ConfigurationProperties(prefix = "message.center") public class MessageCenterConstants {private List<Business> businesses;private List<Channel> channels;private Map<String, String> businesses2Channels;private Map<String, String> bizTypeForMsgType;public void setBusinesses(List<Business> businesses) {this.businesses = businesses;}public void setChannels(List<Channel> channels) {this.channels = channels;}public List<Business> getBusinesses() {return businesses;}public List<Channel> getChannels() {return channels;}public Map<String, String> getBusinesses2Channels() {return businesses2Channels;}public void setBusinesses2Channels(Map<String, String> businesses2Channels) {this.businesses2Channels = businesses2Channels;}public Map<String, String> getBizTypeForMsgType() {return bizTypeForMsgType;}public void setBizTypeForMsgType(Map<String, String> bizTypeForMsgType) {this.bizTypeForMsgType = bizTypeForMsgType;}public static class Business implements Comparable<Business> {//業(yè)務(wù)類(lèi)型private String type;//業(yè)務(wù)描述private String desc;//對(duì)應(yīng) kafka 的 topicprivate String topic;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public String getTopic() {return topic;}public void setTopic(String topic) {this.topic = topic;}@Overridepublic int compareTo(Business o) {if (type.compareTo(o.type) == 0 || topic.compareTo(o.topic) == 0) {return 0;}return Objects.hash(type, topic);}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Business business = (Business) o;return Objects.equals(type, business.type) ||Objects.equals(topic, business.topic);}@Overridepublic int hashCode() {return Objects.hash(type, topic);}@Overridepublic String toString() {return "Business{" +"type='" + type + '\'' +", desc='" + desc + '\'' +", topic='" + topic + '\'' +'}';}}public static class Channel implements Comparable<Channel> {//渠道類(lèi)型private String type;//渠道描述private String desc;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic int compareTo(Channel o) {return this.type.compareTo(o.type);}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Channel channel = (Channel) o;return Objects.equals(type, channel.type);}@Overridepublic int hashCode() {return Objects.hash(type);}@Overridepublic String toString() {return "Channel{" +"type='" + type + '\'' +", desc='" + desc + '\'' +'}';}} }

屬性刷新方案

@Bean public MergedProperties kafkaMessageMergedProperties() {return ConfigCenterUtils.createToRefreshPropertiesBean(MergedProperties.class); }public static class MergedProperties {private Map<String, MessageCenterConstants.Business> businesses;private Map<String, MessageCenterConstants.Channel> channels;//業(yè)務(wù)映射渠道private Map<String, String> businesses2Channels;//消息類(lèi)型映射業(yè)務(wù)類(lèi)型private Map<String, String> msgType2BizType;public MergedProperties() throws GeneralException {this.refreshProperties();}private void refreshProperties() throws GeneralException {
//獲取到配置中心最新的propertySourceZookeeperPropertySource propertySource
= ConfigHelper.getZookeeperPropertySource();MessageCenterConstants messageCenterConstants = null;
     //判斷屬性是否刷新
if (ConfigCenterUtils.propertySourceRefresh(propertySource)) {
       //將屬性binding到帶有@ConfigurationProperties注解的類(lèi)中messageCenterConstants
=RelaxedConfigurationBinder.with(MessageCenterConstants.class).setPropertySources(propertySource).doBind();}
     //以下是自定義處理,可忽略
if (!Objects.isNull(messageCenterConstants)) {//Business.type <-> Businessthis.setBusinesses(Maps.newHashMap(Maps.uniqueIndex(Sets.newHashSet(messageCenterConstants.getBusinesses()), business -> business.getType())));//Channel.type <-> Channelthis.setChannels(Maps.newHashMap(Maps.uniqueIndex(Sets.newHashSet(messageCenterConstants.getChannels()), channel -> channel.getType())));//business <-> channelsthis.setBusinesses2Channels(messageCenterConstants.getBusinesses2Channels());//消息類(lèi)型映射業(yè)務(wù)類(lèi)型this.setMsgType2BizType(messageCenterConstants.getBizTypeForMsgType().entrySet().stream().map(entry -> {Map<String, String> tmpMap = Maps.newHashMap();if (StringUtils.isBlank(entry.getValue())) {return tmpMap;}Arrays.stream(entry.getValue().split(",")).forEach(value -> tmpMap.put(value, entry.getKey()));return tmpMap;}).flatMap(map -> map.entrySet().stream()).collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue())));}}
   //刷新方法
private void catchRefreshProperties() {try {this.refreshProperties();} catch (Exception e) {LOGGER.error("KafkaMessageConfig 配置中心屬性刷新失敗", e);}}
   //get方法上指定刷新屬性@ToRefresh(method
= "catchRefreshProperties")public Map<String, MessageCenterConstants.Business> getBusinesses() {return businesses;}public void setBusinesses(Map<String, MessageCenterConstants.Business> businesses) {this.businesses = businesses;}@ToRefresh(method = "catchRefreshProperties")public Map<String, MessageCenterConstants.Channel> getChannels() {return channels;}public void setChannels(Map<String, MessageCenterConstants.Channel> channels) {this.channels = channels;}@ToRefresh(method = "catchRefreshProperties")public Map<String, String> getBusinesses2Channels() {return businesses2Channels;}public void setBusinesses2Channels(Map<String, String> businesses2Channels) {this.businesses2Channels = businesses2Channels;}@ToRefresh(method = "catchRefreshProperties")public Map<String, String> getMsgType2BizType() {return msgType2BizType;}public void setMsgType2BizType(Map<String, String> msgType2BizType) {this.msgType2BizType = msgType2BizType;} }

工具類(lèi)

ConfigCenterUtils

import com.cmos.cfg.core.ConfigHelper; import com.cmos.cfg.zookeeper.ZookeeperPropertySource; import org.apache.commons.lang3.StringUtils; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ReflectionUtils;import java.lang.reflect.Method; import java.util.Objects;/*** @author hujunzheng* @create 2018-07-04 15:45**/ public class ConfigCenterUtils {private static ZookeeperPropertySource propertySource = ConfigHelper.getZookeeperPropertySource();
  //判斷配置中心屬性是否刷新
public synchronized static boolean propertySourceRefresh(ZookeeperPropertySource newPropertySource) {if (propertySource.equals(newPropertySource)) {return false;}if (propertySource.hashCode() == newPropertySource.hashCode()) {return false;}propertySource = newPropertySource;return true;}
   //創(chuàng)建代理類(lèi),代理@ToRefresh注解的方法,調(diào)用相應(yīng)的刷新方法
public static <T> T createToRefreshPropertiesBean(Class<T> clazz) {Enhancer enhancer = new Enhancer();// 設(shè)置代理對(duì)象父類(lèi) enhancer.setSuperclass(clazz);// 設(shè)置增強(qiáng)enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {ToRefresh toRefresh = AnnotationUtils.findAnnotation(method, ToRefresh.class);if (Objects.isNull(toRefresh) || StringUtils.isBlank(toRefresh.method())) {return methodProxy.invokeSuper(target, args);}Method refreshMethod = ReflectionUtils.findMethod(target.getClass(), toRefresh.method());if (Objects.isNull(refreshMethod)) {return methodProxy.invokeSuper(target, args);}refreshMethod = BridgeMethodResolver.findBridgedMethod(refreshMethod);refreshMethod.setAccessible(true);refreshMethod.invoke(target, null);return methodProxy.invokeSuper(target, args);}});return (T) enhancer.create();// 創(chuàng)建代理對(duì)象 } } import org.apache.commons.lang3.StringUtils;import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target;import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME;/*** @author hujunzheng* @create 2018-07-06 9:59**/ @Target({METHOD}) @Retention(RUNTIME) @Documented public @interface ToRefresh {//刷新方法String method() default StringUtils.EMPTY; }

RelaxedConfigurationBinder

  動(dòng)態(tài)將propertysource綁定到帶有@ConfigurationProperties注解的bean中

  參考:org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor

import com.cmos.common.exception.GeneralException; import org.springframework.boot.bind.PropertiesConfigurationFactory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.*; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.SpringValidatorAdapter;import javax.validation.Validation;import static org.springframework.core.annotation.AnnotatedElementUtils.getMergedAnnotation;/*** @author hujunzheng* @create 2018-07-03 18:01** 不強(qiáng)依賴(lài)ConfigurationProperties,進(jìn)行配置注入**/ public class RelaxedConfigurationBinder<T> {private final PropertiesConfigurationFactory<T> factory;public RelaxedConfigurationBinder(T object) {this(new PropertiesConfigurationFactory<>(object));}public RelaxedConfigurationBinder(Class<T> type) {this(new PropertiesConfigurationFactory<>(type));}public static <T> RelaxedConfigurationBinder<T> with(T object) {return new RelaxedConfigurationBinder<>(object);}public static <T> RelaxedConfigurationBinder<T> with(Class<T> type) {return new RelaxedConfigurationBinder<>(type);}public RelaxedConfigurationBinder(PropertiesConfigurationFactory<T> factory) {this.factory = factory;ConfigurationProperties properties = getMergedAnnotation(factory.getObjectType(), ConfigurationProperties.class);javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();factory.setValidator(new SpringValidatorAdapter(validator));factory.setConversionService(new DefaultConversionService());if (null != properties) {factory.setIgnoreNestedProperties(properties.ignoreNestedProperties());factory.setIgnoreInvalidFields(properties.ignoreInvalidFields());factory.setIgnoreUnknownFields(properties.ignoreUnknownFields());factory.setTargetName(properties.prefix());factory.setExceptionIfInvalid(properties.exceptionIfInvalid());}}public RelaxedConfigurationBinder<T> setTargetName(String targetName) {factory.setTargetName(targetName);return this;}public RelaxedConfigurationBinder<T> setPropertySources(PropertySource<?>... propertySources) {MutablePropertySources sources = new MutablePropertySources();for (PropertySource<?> propertySource : propertySources) {sources.addLast(propertySource);}factory.setPropertySources(sources);return this;}public RelaxedConfigurationBinder<T> setPropertySources(Environment environment) {factory.setPropertySources(((ConfigurableEnvironment) environment).getPropertySources());return this;}public RelaxedConfigurationBinder<T> setPropertySources(PropertySources propertySources) {factory.setPropertySources(propertySources);return this;}public RelaxedConfigurationBinder<T> setConversionService(ConversionService conversionService) {factory.setConversionService(conversionService);return this;}public RelaxedConfigurationBinder<T> setValidator(Validator validator) {factory.setValidator(validator);return this;}public RelaxedConfigurationBinder<T> setResolvePlaceholders(boolean resolvePlaceholders) {factory.setResolvePlaceholders(resolvePlaceholders);return this;}public T doBind() throws GeneralException {try {return factory.getObject();} catch (Exception ex) {throw new GeneralException("配置綁定失敗!", ex);}} }

?

轉(zhuǎn)載于:https://www.cnblogs.com/hujunzheng/p/9272866.html

總結(jié)

以上是生活随笔為你收集整理的springboot项目接入配置中心,实现@ConfigurationProperties的bean属性刷新方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。