欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

NamedContextFactory簡介

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

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

下面通過一個(gè)示例來講解

示例

注: 示例就模擬一個(gè)用戶注冊成功后發(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對應(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ì)加載該配置類,該配置類主要用來做兜底,當(dāng)找不到容器為name的configuration,則會(huì)使用該配置類

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

propertyName: 子容器可以通過讀取配置propertyName來獲取容器名。當(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、測試

模擬用戶注冊

@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)行查看控制臺(tái)

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

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

總結(jié)

本文主要是聊下通過NamedContextFactory來實(shí)現(xiàn)服務(wù)隔離,核心點(diǎn)就是通過創(chuàng)建不同子容器進(jìn)行隔離。這種方式在ribbon、openfeign、以及l(fā)oadbalancer都有類似的實(shí)現(xiàn),感興趣朋友可以查閱其源碼。不過這邊有細(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ù)隔離的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 了解JAVA并發(fā)工具常用設(shè)計(jì)套路

    了解JAVA并發(fā)工具常用設(shè)計(jì)套路

    這篇文章主要介紹了了解JAVA并發(fā)工具常用設(shè)計(jì)套路,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2019-06-06
  • Java常用測試工具大全

    Java常用測試工具大全

    這篇文章主要為大家詳細(xì)介紹了Java常用測試工具,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • java如何將int數(shù)組轉(zhuǎn)化為Integer數(shù)組

    java如何將int數(shù)組轉(zhuǎn)化為Integer數(shù)組

    這篇文章主要介紹了java如何將int數(shù)組轉(zhuǎn)化為Integer數(shù)組,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java BigDecimal基礎(chǔ)用法詳解

    Java BigDecimal基礎(chǔ)用法詳解

    Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數(shù)進(jìn)行精確的運(yùn)算。雙精度浮點(diǎn)型變量double可以處理16位有效數(shù),但在實(shí)際應(yīng)用中,可能需要對更大或者更小的數(shù)進(jìn)行運(yùn)算和處理
    2022-06-06
  • java校驗(yàn)json的格式是否符合要求的操作方法

    java校驗(yàn)json的格式是否符合要求的操作方法

    在日常開發(fā)過程中,會(huì)有這樣的需求,校驗(yàn)?zāi)硞€(gè)json是否是我們想要的數(shù)據(jù)格式,這篇文章主要介紹了java校驗(yàn)json的格式是否符合要求,需要的朋友可以參考下
    2023-04-04
  • Spring內(nèi)置任務(wù)調(diào)度如何實(shí)現(xiàn)添加、取消與重置詳解

    Spring內(nèi)置任務(wù)調(diào)度如何實(shí)現(xiàn)添加、取消與重置詳解

    任務(wù)調(diào)度是我們?nèi)粘i_發(fā)中經(jīng)常會(huì)碰到的,下面這篇文章主要給大家介紹了關(guān)于Spring內(nèi)置任務(wù)調(diào)度如何實(shí)現(xiàn)添加、取消與重置的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • Java文件與Base64之間的轉(zhuǎn)化方式

    Java文件與Base64之間的轉(zhuǎn)化方式

    這篇文章介紹了如何使用Java將文件(如圖片、視頻)轉(zhuǎn)換為Base64編碼,以及如何將Base64編碼轉(zhuǎn)換回文件,通過提供具體的工具類實(shí)現(xiàn),作者希望幫助讀者更好地理解和應(yīng)用這一過程
    2025-02-02
  • SpringCloudGateway開發(fā)過程解析

    SpringCloudGateway開發(fā)過程解析

    這篇文章主要介紹了SpringCloudGateway開發(fā)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • SpringBoot整合Liquibase實(shí)現(xiàn)對數(shù)據(jù)庫管理和遷移

    SpringBoot整合Liquibase實(shí)現(xiàn)對數(shù)據(jù)庫管理和遷移

    Liquibase是一個(gè)用于用于跟蹤、管理和應(yīng)用數(shù)據(jù)庫變化的開源工具,通過日志文件(changelog)的形式記錄數(shù)據(jù)庫的變更(changeset),然后執(zhí)行日志文件中的修改,將數(shù)據(jù)庫更新或回滾(rollback)到一致的狀態(tài),本文主要介紹SpringBoot與Liquibase的集成,需要的朋友可以參考下
    2024-11-11
  • 基于FileNotFoundException問題的解決

    基于FileNotFoundException問題的解決

    這篇文章主要介紹了基于FileNotFoundException問題的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03

最新評論