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

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

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

前言

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

NamedContextFactory簡(jiǎn)介

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

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

下面通過(guò)一個(gè)示例來(lái)講解

示例

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

1、模擬定義短信接口

public interface SmsService {

    void send(String phone, String content);
}

2、模擬定義相應(yīng)短信實(shí)現(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: 子容器的名稱(示例中我們會(huì)把用戶服務(wù)名和訂單服務(wù)名當(dāng)成子容器名稱)

configuration: name子容器需要的configuration

NamedContextFactory.Specification的作用是當(dāng)創(chuàng)建子容器時(shí),如果容器的name匹配了Specification的name,則會(huì)加載 Specification對(duì)應(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三個(gè)參數(shù)講解

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

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

propertySourceName: 給propertySource取個(gè)名稱

propertyName: 子容器可以通過(guò)讀取配置propertyName來(lái)獲取容器名。當(dāng)創(chuàng)建子容器時(shí)通常會(huì)提供子容器的容器name。子容器中的Environment會(huì)被寫入一條配置,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();
    }
}

注: 因?yàn)樯鲜雠渲弥恍璞蛔尤萜骷虞d,因此不需要加 @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、測(cè)試

模擬用戶注冊(cè)

@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("注冊(cè)用戶"+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(), "恭喜您注冊(cè)成功!初始密碼為:"+event.getPassword()+",請(qǐng)盡快修改密碼!");
    }
}

核心:

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

和 @SmsClient(name = UserService.SERVICE_NAME)對(duì)應(yīng)起來(lái)

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

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

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

總結(jié)

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

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

相關(guān)文章

  • java中斷線程的正確姿勢(shì)完整示例

    java中斷線程的正確姿勢(shì)完整示例

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

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

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

    Spring MVC之WebApplicationContext_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

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

    Java實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理詳解

    這篇文章主要介紹了Java實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理詳解,Java常用的動(dòng)態(tài)代理模式有JDK動(dòng)態(tài)代理,也有cglib動(dòng)態(tài)代理,本文重點(diǎn)講解JDK的動(dòng)態(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)速度方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • 解決報(bào)錯(cuò):java.lang.IllegalStateException: Failed to execute CommandLineRunner問(wèn)題

    解決報(bào)錯(cuò):java.lang.IllegalStateException: Failed to&nb

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

    Java中的PrintWriter 介紹_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

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

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

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

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

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

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

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

最新評(píng)論

宣化县| 万全县| 海南省| 休宁县| 临澧县| 白城市| 金门县| 香河县| 石景山区| 普格县| 云龙县| 玉屏| 阆中市| 会宁县| 长乐市| 平谷区| 双江| 林芝县| 铜川市| 普兰县| 龙山县| 荆门市| 嵊州市| 宜阳县| 文水县| 康乐县| 漠河县| 铜梁县| 景泰县| 隆林| 灵武市| 武城县| 东光县| 安化县| 上思县| 山丹县| 伊吾县| 远安县| 昆山市| 炎陵县| 年辖:市辖区|