亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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

NamedContextFactory簡介

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

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

下面通過一個示例來講解

示例

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

1、模擬定義短信接口

public interface SmsService {

    void send(String phone, String content);
}

2、模擬定義相應短信實現(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、自定義短信默認配置類

@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: 子容器的名稱(示例中我們會把用戶服務名和訂單服務名當成子容器名稱)

configuration: name子容器需要的configuration

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

5、為不同的服務創(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機制,將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: 默認配置類,NamedContextFactory創(chuàng)建子容器時,默認就會加載該配置類,該配置類主要用來做兜底,當找不到容器為name的configuration,則會使用該配置類

propertySourceName: 給propertySource取個名稱

propertyName: 子容器可以通過讀取配置propertyName來獲取容器名。當創(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、為用戶服務和訂單服務指定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)對應起來

運行查看控制臺

當服務名不匹配時,再觀察控制臺

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

總結

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

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

相關文章

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

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

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

    Java常用測試工具大全

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

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

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

    Java BigDecimal基礎用法詳解

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

    java校驗json的格式是否符合要求的操作方法

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

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

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

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

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

    SpringCloudGateway開發(fā)過程解析

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

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

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

    基于FileNotFoundException問題的解決

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

最新評論