最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring NamedContextFactory實現(xiàn)服務(wù)隔離的示例詳解

 更新時間:2024年05月22日 10:24:09   作者:linyb極客之路  
假設(shè)我們有個場景,我們需要實現(xiàn)服務(wù)之間的數(shù)據(jù)隔離、配置隔離、依賴的spring bean之間隔離,大家會有什么實現(xiàn)思路?今天給大家介紹spring-cloud-context里面有個NamedContextFactory可以達(dá)到上面的效果,需要的朋友可以參考下

前言

假設(shè)我們有個場景,我們需要實現(xiàn)服務(wù)之間的數(shù)據(jù)隔離、配置隔離、依賴的spring bean之間隔離。大家會有什么實現(xiàn)思路?今天給大家介紹spring-cloud-context里面有個NamedContextFactory可以達(dá)到上面的效果

NamedContextFactory簡介

NamedContextFactory可以實現(xiàn)子容器,通過它創(chuàng)建子容器,然后通過NamedContextFactory.Specification可以定制子容器會用到的bean。

所以為什么通過NamedContextFactory可以達(dá)到數(shù)據(jù)隔離、配置隔離、依賴的spring bean之間隔離,本質(zhì)就是利用NamedContextFactory為不同的服務(wù),創(chuàng)建出不同的子容器,子容器之間彼此不共享,從而達(dá)到隔離的效果

下面通過一個示例來講解

示例

注: 示例就模擬一個用戶注冊成功后發(fā)送華為云短信,下單成功后發(fā)送阿里云短信為例子

1、模擬定義短信接口

public interface SmsService {

    void send(String phone, String content);
}

2、模擬定義相應(yīng)短信實現(xiàn)類

public class DefaultSmsService implements SmsService {
    @Override
    public void send(String phone, String content) {
        System.out.printf("send to %s content %s used default sms%n", phone, content);
    }
}

public class AliyunSmsService implements SmsService {
    @Override
    public void send(String phone, String content) {
        System.out.printf("send to %s content %s used aliyun sms%n", phone, content);
    }
}
public class HuaWeiSmsService implements SmsService {
    @Override
    public void send(String phone, String content) {
        System.out.printf("send to %s content %s used huawei sms%n", phone, content);
    }
}

3、自定義短信默認(rèn)配置類

@Configuration
public class DefaultSmsClientConfiguration {


    @Bean
    @ConditionalOnMissingBean
    public SmsService smsService(){
        return new DefaultSmsService();
    }

}

4、定制短信需要的子容器NamedContextFactory.Specification

public class SmsClientSpecification implements NamedContextFactory.Specification{
    private String name;

    private Class<?>[] configuration;

    public SmsClientSpecification() {
    }

    public SmsClientSpecification(String name, Class<?>[] configuration) {
        this.name = name;
        this.configuration = configuration;
    }

    @Override
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Class<?>[] getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Class<?>[] configuration) {
        this.configuration = configuration;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        SmsClientSpecification that = (SmsClientSpecification) o;
        return Arrays.equals(configuration, that.configuration)
                && Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(configuration, name);
    }

    @Override
    public String toString() {
        return new StringBuilder("SmsSpecification{").append("name='")
                .append(name).append("', ").append("configuration=")
                .append(Arrays.toString(configuration)).append("}").toString();
    }
}

屬性講解

name: 子容器的名稱(示例中我們會把用戶服務(wù)名和訂單服務(wù)名當(dāng)成子容器名稱)

configuration: name子容器需要的configuration

NamedContextFactory.Specification的作用是當(dāng)創(chuàng)建子容器時,如果容器的name匹配了Specification的name,則會加載 Specification對應(yīng)Configuration類,并將Configuration類里面標(biāo)注@Bean的返回值注入到子容器中

5、為不同的服務(wù)創(chuàng)建不同的SmsClientSpecification并注入到spring容器中

@Configuration
@Import(SmsClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SmsClient {

    /**
     * Synonym for name (the name of the client).
     *
     * @see #name()
     * @return name of the Sms client
     */
    String value() default "";

    /**
     * The name of the sms client, uniquely identifying a set of client resources,
     * @return name of the Sms client
     */
    String name() default "";

    /**
     * A custom <code>@Configuration</code> for the sms client. Can contain override
     * <code>@Bean</code> definition for the pieces that make up the client
     */
    Class<?>[] configuration() default {};
}

@Configuration
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(SmsClientConfigurationRegistrar.class)
public @interface SmsClients {

	SmsClient[] value() default {};

	Class<?>[] defaultConfiguration() default {};

}

注: 利用import機(jī)制,將SmsClientSpecification注入到spring容器

public class SmsClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(SmsClients.class.getName(), true);
		if (attrs != null && attrs.containsKey("value")) {
			AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
			for (AnnotationAttributes client : clients) {
				registerClientConfiguration(registry, getClientName(client),
						client.get("configuration"));
			}
		}
		if (attrs != null && attrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name,
					attrs.get("defaultConfiguration"));
		}
		Map<String, Object> client = metadata
				.getAnnotationAttributes(SmsClient.class.getName(), true);
		String name = getClientName(client);
		if (name != null) {
			registerClientConfiguration(registry, name, client.get("configuration"));
		}
	}

	private String getClientName(Map<String, Object> client) {
		if (client == null) {
			return null;
		}
		String value = (String) client.get("value");
		if (!StringUtils.hasText(value)) {
			value = (String) client.get("name");
		}
		if (StringUtils.hasText(value)) {
			return value;
		}
		throw new IllegalStateException(
				"Either 'name' or 'value' must be provided in @SmsClient");
	}

	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(SmsClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
		registry.registerBeanDefinition(name + ".SmsClientSpecification",
				builder.getBeanDefinition());
	}

}

6、創(chuàng)建短信NameContextFactory

public class SmsClientNameContextFactory extends NamedContextFactory<SmsClientSpecification> {

    public SmsClientNameContextFactory() {
        super(DefaultSmsClientConfiguration.class, "sms", "sms.client.name");
    }

    public SmsService getSmsService(String serviceName) {
        return getInstance(serviceName, SmsService.class);
    }
}

注: super三個參數(shù)講解

public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
			String propertyName) {
		this.defaultConfigType = defaultConfigType;
		this.propertySourceName = propertySourceName;
		this.propertyName = propertyName;
	}

defaultConfigType: 默認(rèn)配置類,NamedContextFactory創(chuàng)建子容器時,默認(rèn)就會加載該配置類,該配置類主要用來做兜底,當(dāng)找不到容器為name的configuration,則會使用該配置類

propertySourceName: 給propertySource取個名稱

propertyName: 子容器可以通過讀取配置propertyName來獲取容器名。當(dāng)創(chuàng)建子容器時通常會提供子容器的容器name。子容器中的Environment會被寫入一條配置,sms.client.name=容器name

7、將SmsClientNameContextFactory注入到spring容器

   @Bean
    @ConditionalOnMissingBean
    public SmsClientNameContextFactory smsClientNameContextFactory(@Autowired(required = false) List<SmsClientSpecification> smsSpecifications){
        SmsClientNameContextFactory smsClientNameContextFactory = new SmsClientNameContextFactory();
        smsClientNameContextFactory.setConfigurations(smsSpecifications);
        return smsClientNameContextFactory;
    }

8、創(chuàng)建不同的短信配置類

public class AliyunSmsClientConfiguration {

    @ConditionalOnMissingBean
    @Bean
    public SmsService smsService() {
       return new AliyunSmsService();
    }
}

public class HuaWeiSmsClientConfiguration {

    @ConditionalOnMissingBean
    @Bean
    public SmsService smsService() {
       return new HuaWeiSmsService();
    }
}

注: 因為上述配置只需被子容器加載,因此不需要加 @Configuration

9、為用戶服務(wù)和訂單服務(wù)指定NamedContextFactory.Specification

@Configuration
@SmsClients(value = {@SmsClient(name = OrderService.SERVICE_NAME, configuration = AliyunSmsClientConfiguration.class),
        @SmsClient(name = UserService.SERVICE_NAME, configuration = HuaWeiSmsClientConfiguration.class)})
public class SmsClientAutoConfiguration {
}

10、測試

模擬用戶注冊

@Service
@RequiredArgsConstructor
public class UserService {

    private final ApplicationContext applicationContext;

    public static final String SERVICE_NAME = "userService";

    public void registerUser(String userName, String password,String mobile){
        System.out.println("注冊用戶"+userName+"成功");
        UserRegisterEvent event = new UserRegisterEvent(userName,password,mobile);
        applicationContext.publishEvent(event);
    }
}

@Component
@RequiredArgsConstructor
public class UserRegisterListener {

    private final SmsClientNameContextFactory smsClientNameContextFactory;


    @EventListener
    @Async
    public void listener(UserRegisterEvent event) {
        SmsService smsService = smsClientNameContextFactory.getSmsService(UserService.SERVICE_NAME);
        smsService.send(event.getMobile(), "恭喜您注冊成功!初始密碼為:"+event.getPassword()+",請盡快修改密碼!");
    }
}

核心:

 SmsService smsService = smsClientNameContextFactory.getSmsService(UserService.SERVICE_NAME);

和 @SmsClient(name = UserService.SERVICE_NAME)對應(yīng)起來

運(yùn)行查看控制臺

當(dāng)服務(wù)名不匹配時,再觀察控制臺

發(fā)現(xiàn)此時是走默認(rèn)配置

總結(jié)

本文主要是聊下通過NamedContextFactory來實現(xiàn)服務(wù)隔離,核心點就是通過創(chuàng)建不同子容器進(jìn)行隔離。這種方式在ribbon、openfeign、以及l(fā)oadbalancer都有類似的實現(xiàn),感興趣朋友可以查閱其源碼。不過這邊有細(xì)節(jié)點需要注意,因為NamedContextFactory默認(rèn)是懶加載創(chuàng)建子容器,所以可能第一次調(diào)用會比較慢。這也是ribbon第一次調(diào)用慢的原因

以上就是spring NamedContextFactory實現(xiàn)服務(wù)隔離的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于spring NamedContextFactory服務(wù)隔離的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java中斷線程的正確姿勢完整示例

    java中斷線程的正確姿勢完整示例

    這篇文章主要為大家介紹了java中斷線程的正確姿勢完整示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • Springboot集成JWT實現(xiàn)登錄注冊的示例代碼

    Springboot集成JWT實現(xiàn)登錄注冊的示例代碼

    本文主要介紹了Springboot集成JWT實現(xiàn)登錄注冊的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Spring MVC之WebApplicationContext_動力節(jié)點Java學(xué)院整理

    Spring MVC之WebApplicationContext_動力節(jié)點Java學(xué)院整理

    這篇文章主要介紹了Spring MVC之WebApplicationContext的相關(guān)資料,需要的朋友可以參考下
    2017-08-08
  • Java實現(xiàn)JDK動態(tài)代理的原理詳解

    Java實現(xiàn)JDK動態(tài)代理的原理詳解

    這篇文章主要介紹了Java實現(xiàn)JDK動態(tài)代理的原理詳解,Java常用的動態(tài)代理模式有JDK動態(tài)代理,也有cglib動態(tài)代理,本文重點講解JDK的動態(tài)代理,需要的小伙伴可以參考一下的相關(guān)資料
    2022-07-07
  • Springboot使用異步方法優(yōu)化Service邏輯,提高接口響應(yīng)速度方式

    Springboot使用異步方法優(yōu)化Service邏輯,提高接口響應(yīng)速度方式

    這篇文章主要介紹了Springboot使用異步方法優(yōu)化Service邏輯,提高接口響應(yīng)速度方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • 解決報錯:java.lang.IllegalStateException: Failed to execute CommandLineRunner問題

    解決報錯:java.lang.IllegalStateException: Failed to&nb

    在項目開發(fā)中,可能會遇到Elasticsearch啟動報錯的問題,原因可能包括版本不一致、端口配置錯誤、配置文件不匹配及服務(wù)未啟動等,解決方法包括檢查進(jìn)程、重啟服務(wù)等,這些經(jīng)驗可以幫助開發(fā)者快速定位問題并解決,保證項目順利運(yùn)行
    2024-10-10
  • Java中的PrintWriter 介紹_動力節(jié)點Java學(xué)院整理

    Java中的PrintWriter 介紹_動力節(jié)點Java學(xué)院整理

    PrintWriter 是字符類型的打印輸出流,它繼承于Writer。接下來通過本文給大家介紹java中的 PrintWriter 相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧
    2017-05-05
  • SpringBoot實現(xiàn)登錄攔截器超詳細(xì)教程分享

    SpringBoot實現(xiàn)登錄攔截器超詳細(xì)教程分享

    對于管理系統(tǒng)或其他需要用戶登錄的系統(tǒng),登錄驗證都是必不可少的環(huán)節(jié),尤其在?SpringBoot?開發(fā)的項目中。本文為大家準(zhǔn)備了超詳細(xì)的SpringBoot實現(xiàn)登錄攔截器方法,快收藏一波吧
    2023-02-02
  • 深入淺析HashMap key和value能否為null

    深入淺析HashMap key和value能否為null

    HashMap的key和value可為null,線程不安全,HashTable的key和value均不可為null,線程安全,ConcurrentHashMap在多線程場景下使用,key和value也不能為null,還對它們進(jìn)行了測試和底層代碼分析,本文介紹HashMap key和value能否為null,感興趣的朋友跟隨小編一起看看吧
    2025-04-04
  • Java并發(fā)編程之重入鎖與讀寫鎖

    Java并發(fā)編程之重入鎖與讀寫鎖

    這篇文章主要介紹了Java并發(fā)編程之重入鎖與讀寫鎖,文中相關(guān)實例代碼詳細(xì),測試可用,具有一定參考價值,需要的朋友可以了解下。
    2017-09-09

最新評論

溆浦县| 鄂伦春自治旗| 深水埗区| 西林县| 阿巴嘎旗| 大竹县| 定襄县| 九龙县| 改则县| 阳泉市| 枣阳市| 凤阳县| 外汇| 普定县| 溧水县| 靖边县| 皮山县| 康平县| 平江县| 南和县| 玉溪市| 滕州市| 绥德县| 嘉义县| 诸城市| 浦城县| 连江县| 阜新市| 长子县| 林芝县| 南昌市| 丰原市| 金昌市| 苏州市| 宽城| 文昌市| 沭阳县| 绥德县| 张家口市| 凤山市| 景宁|