前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊如何利用服务定位器模式按需返回我们需要的服务实例

聊聊如何利用服务定位器模式按需返回我们需要的服务实例

原创
作者头像
lyb-geek
发布2023-06-13 15:26:01
2050
发布2023-06-13 15:26:01
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路

前言

什么是服务定位器模式

服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象。在服务定位器中,每个服务(或组件)都只有一个单独的实例,并通过ID 唯一地标识。 用这个 ID 就能从服务定位器中得到这个服务(或组件)。

何时可以考虑使用服务定位器模式

服务定位器模式的目的是按需返回服务实例,当依赖是按需的或需要在运行时查找时,我们可以使用服务定位器模式将客户端与具体实现解耦。

服务定位器包含的组件

  • 客户端:在运行时需要服务的消费者。
  • 服务定位器:服务定位器负责将服务按需返回给客户端。它抽象了服务的查找或创建。
  • 初始上下文:它创建、注册和缓存服务。这是查找和创建的起点。
  • 服务工厂: 服务工厂为服务提供生命周期管理,支持创建、查找或删除服务。
  • 服务:客户所需服务的具体实现。

服务定位器执行流程

ff9b6cd9bb4ee2181ebd12f1fb4f4aaf_f22a5a39afa1c433191619d777be6ea5.png
ff9b6cd9bb4ee2181ebd12f1fb4f4aaf_f22a5a39afa1c433191619d777be6ea5.png

下面我们就以一个模拟发送短信的例子,来体验一把服务定位器模式。因spring已经提供了服务定位器,本示例就以spring提供的服务定位器为例

前置知识

spring 服务定位器

spring的服务定位器主要是通过ServiceLocatorFactoryBean实现。它实现 FactoryBean接口,并封装了服务定位器模式的所有设计组件,为客户端提供了一个干净的 API 以按需获取对象

spring服务定位器实现流程

bb6e5faf0c7410e8ca55851af75375c3_c9f48a08d6a4a6aa18958b733c433216.png
bb6e5faf0c7410e8ca55851af75375c3_c9f48a08d6a4a6aa18958b733c433216.png

示例

1、定义一个实体类,这个实体类后边插件绑定具体短信服务会用到

代码语言:java
复制
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SmsRequest implements Serializable {

    private Map<String,Object> metaDatas;

    private String to;

    private String message;

    private SmsType smsType;


}

2、定义短信发送服务接口

代码语言:java
复制
public interface SmsProvider {


    SmsResponse sendSms(SmsRequest smsRequest);


}

3、定义短信服务定位器工厂,用来选取具体的短信服务

代码语言:java
复制
public interface SmsFactory {

    SmsProvider getProvider(SmsType smsType);
}

4、定义短信发送具体实现类

代码语言:java
复制
@Component
public class AliyunSmsProvider implements SmsProvider {
    @Override
    public SmsResponse sendSms(SmsRequest smsRequest) {
        System.out.println("来自阿里云短信:" + smsRequest);
        return SmsResponse.builder()
                .code("200").message("发送成功")
                .success(true).result("阿里云短信的回执").build();
    }


}

注: 该具体服务必须是spring的bean

5、配置ServiceLocatorFactoryBean

代码语言:java
复制
 @Bean
    @ConditionalOnMissingBean
    public FactoryBean smsFactory(){
        ServiceLocatorFactoryBean serviceLocatorFactoryBean = new ServiceLocatorFactoryBean();
        serviceLocatorFactoryBean.setServiceLocatorInterface(SmsFactory.class);
        // spring beanName映射,自定义名称映射关系,
        Properties properties = new Properties();
        properties.setProperty(SmsType.ALIYUN.toString(),"aliyunSmsProvider");
        properties.setProperty(SmsType.TENCENT.toString(),"tencentSmsProvider");
        serviceLocatorFactoryBean.setServiceMappings(properties);
        return serviceLocatorFactoryBean;
    }

注: 短信服务定位器工厂,本质是通过beanName来找到具体的短信服务,如下示例

代码语言:java
复制
public interface SmsFactory {

    SmsProvider getProvider(String beanName);
}

但为了保持一定的业务语义,我们可以通过

代码语言:java
复制
   serviceLocatorFactoryBean.setServiceMappings(properties);

来实现业务类型-->beanName的映射

映射源码如下

代码语言:java
复制
	private String tryGetBeanName(@Nullable Object[] args) {
			String beanName = "";
			if (args != null && args.length == 1 && args[0] != null) {
				beanName = args[0].toString();
			}
			// Look for explicit serviceId-to-beanName mappings.
			if (serviceMappings != null) {
				String mappedName = serviceMappings.getProperty(beanName);
				if (mappedName != null) {
					beanName = mappedName;
				}
			}
			return beanName;
		}

6、业务如何使用

代码语言:java
复制
@RequiredArgsConstructor
public class SmsService {


    private final SmsFactory smsFactory;


    public SmsResponse sendSms(SmsRequest smsRequest){

        return smsFactory.getProvider(smsRequest.getSmsType()).sendSms(smsRequest);


    }
}

7、测试

代码语言:java
复制
 @Autowired
    private SmsService smsService;

    @Test
    public void testAliyunSms(){
        SmsRequest smsRequest = SmsRequest.builder()
                .message("模拟使用阿里云短信发送")
                .to("136000000001")
                .smsType(SmsType.ALIYUN)
                .build();

        SmsResponse smsResponse = smsService.sendSms(smsRequest);
        Assert.assertTrue(smsResponse.isSuccess());
        System.out.println(smsResponse);

    }
78c7da0e5b3302dc5f28b71303f362a1_968379dfede685a8ce804ac60629a3ed.png
78c7da0e5b3302dc5f28b71303f362a1_968379dfede685a8ce804ac60629a3ed.png

总结

眼尖的朋友可能会发现,你上面实现的服务定位器,用如下方法

代码语言:java
复制
   @Autowired
    private ApplicationContext applicationContext;


  

    @Override
    public void run(ApplicationArguments args) throws Exception {
        SmsProvider smsProvider = applicationContext.getBean("aliyunSmsProvider",SmsProvider.class);
        smsProvider.sendSms()
    }

也能实现,干嘛那么繁琐,如果你翻看源码,就会发现,他底层实现和上述的实现基本上一样

代码语言:java
复制
	private Object invokeServiceLocatorMethod(Method method, Object[] args) throws Exception {
			Class<?> serviceLocatorMethodReturnType = getServiceLocatorMethodReturnType(method);
			try {
				String beanName = tryGetBeanName(args);
				Assert.state(beanFactory != null, "No BeanFactory available");
				if (StringUtils.hasLength(beanName)) {
					// Service locator for a specific bean name
					return beanFactory.getBean(beanName, serviceLocatorMethodReturnType);
				}
				else {
					// Service locator for a bean type
					return beanFactory.getBean(serviceLocatorMethodReturnType);
				}
			}
			catch (BeansException ex) {
				if (serviceLocatorExceptionConstructor != null) {
					throw createServiceLocatorException(serviceLocatorExceptionConstructor, ex);
				}
				throw ex;
			}
		}

其实服务定位器模式和依赖注入都是控制反转概念的实现,服务定位器将一组职责相似的服务内聚到了一起,并实现服务提供方、服务使用方完全的解耦,上面举的例子也可以看成一种策略+工厂模式的具体实现。最后提一嘴,serviceLocatorFactoryBean.setServiceMappings(properties);这个也不是必须的,只要你业务语义和beanName名字一样即可

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-service-locator

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 前置知识
  • 示例
  • 总结
  • demo链接
相关产品与服务
短信
腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档