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

news2024/11/17 11:40:55

前言

什么是服务定位器模式

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

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

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

服务定位器包含的组件

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

服务定位器执行流程

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

前置知识

spring 服务定位器

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

spring服务定位器实现流程

示例

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

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SmsRequest implements Serializable {

    private Map<String,Object> metaDatas;

    private String to;

    private String message;

    private SmsType smsType;


}

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

public interface SmsProvider {


    SmsResponse sendSms(SmsRequest smsRequest);


}

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

public interface SmsFactory {

    SmsProvider getProvider(SmsType smsType);
}


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

@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

 @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来找到具体的短信服务,如下示例

public interface SmsFactory {

    SmsProvider getProvider(String beanName);
}


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

   serviceLocatorFactoryBean.setServiceMappings(properties);

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

映射源码如下

	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、业务如何使用

@RequiredArgsConstructor
public class SmsService {


    private final SmsFactory smsFactory;


    public SmsResponse sendSms(SmsRequest smsRequest){

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


    }
}

7、测试

 @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);

    }

总结

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

   @Autowired
    private ApplicationContext applicationContext;


  

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

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

	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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/642167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM 调优分析 如何进行JVM调优

文章目录 1.为什么需要进行JVM调优&#xff1f;2.什么情况下可能需要JVM调优3.JVM调优参数4.JVM调优参数设置参考5.JVM内部结构1. 类加载器&#xff08;Class Loader&#xff09;2. 运行时数据区&#xff08;Runtime Data Area&#xff09;3. 垃圾收集器&#xff08;Garbage Co…

css常见布局方式

css常见布局方式 0、前言1、两栏布局1.1 浮动 margin1.2 浮动 BFC&#xff08;overflow: hidden&#xff09;1.3 定位 margin-left1.4 给父容器设置flex布局&#xff0c;左盒子固定宽度&#xff0c;然后给右子元素设置 flex: 1。1.5 table布局 2、三栏布局2.1 float布局2.2 …

小红书数据分析:流量手到擒来,热点创作大解密!

年中大促618接近尾声&#xff0c;对小红书来说&#xff0c;这次的“反向”营销博得满堂彩。 为了呼吁大家不要冲动消费&#xff0c;线下举办了“反冲动俱乐部”活动&#xff0c;以“365天无理由退货”、“退货的商品可兑换成小红书现金券”等玩法在一众促销信息中呈现差异化内…

如何从Prometheus单独提取个别数据持久化到InfluxDB

背景 首先解释一个问题&#xff0c;为什么会选择让InfluxDB来持久化Prometheus的数据&#xff0c;直接存在Prometheus中不行吗&#xff1f;下面是Claude的回答&#xff0c;我来总结一下&#xff1a; 1&#xff09;InfluxDB提供了更强大的Flux查询语言&#xff0c;比如提供了复…

长鑫存储面试(部分)

你平时写代码时&#xff0c;如何保证代码可靠、可复用、可扩展、可维护&#xff1f;有总结经验吗&#xff0c;请举例说明。参考答案 by newBing&#xff1a; 编写文档&#xff1a;编写文档是保证代码可维护性的重要手段。文档应该包括代码的设计思路、实现细节、使用方法等。 遵…

NeRF系列(2):NeRF in the wild : Neural Radiance Fields for Unconstrained Photo Collections论文解读与公式推导

NeRF in the Wild: Neural Radiance Fields for Unconstrained Photo Collections 论文&#xff1a;https://openaccess.thecvf.com/content/CVPR2021/papers/Martin-Brualla_NeRF_in_the_Wild_Neural_Radiance_Fields_for_Unconstrained_Photo_CVPR_2021_paper.pdfhttps://op…

pyspark安装教程

pyspark安装教程 一、Windows下配置pyspark环境1.1 JDK下载安装1.2 Scala下载安装1.3 spark下载安装1.4 Hadoop下载安装1.5 pyspark下载安装 二、pyspark原理简介 一、Windows下配置pyspark环境 在python中使用pyspark并不是单纯的导入pyspark包就可以实现的&#xff0c;而是需…

从0到字节跳动30W年薪,我在测试行业“混”的第5个年头····

一些碎碎念 什么都做了&#xff0c;和什么都没做其实是一样的&#xff0c;走出“瞎忙活”的安乐窝&#xff0c;才是避开弯路的最佳路径。希望我的经历能帮助到有需要的朋友。 在测试行业已经混了5个年头了&#xff0c;以前经常听到开发对我说&#xff0c;天天的点点点有意思没…

使用Jmeter读取和使用Redis数据

目录 前言 缓存 Redis服务和客户端安装 Jmeter使用Redis 总结&#xff1a; 前言 消息队列和缓存是目前主流的中间件&#xff0c;我们在日常测试过程中&#xff0c;无论是接口还是压力测试&#xff0c;都会遇到需要处理这些中间件数据的情况。本文以Redis对缓存做一个简单…

精选博客系列|vSphere 8 Update 1 介绍

去年&#xff0c;我们推出了 vSphere 8&#xff0c;这是传统和下一代应用程序的企业工作负载平台&#xff0c;该产品于 2022 年 11 月正式推出。今天&#xff0c;我们很高兴地宣布 vSphere 8 Update 1 版本。通过这个版本&#xff0c;客户可以获得增强的管理员操作效率&#xf…

【QT】QGridLayout的基础使用(添加控件、布局、间距)

目录 0.环境介绍 1.QGridLayout简介&#xff1a; 2.QGridLayout参数介绍 1&#xff09;添加控件方式 2&#xff09;添加布局方式 3.例子及代码 1&#xff09;三行三列 2&#xff09;有占多行多列的控件 0.环境介绍 windows vscode qt 我使用网格布局的情景是&#x…

深入理解侵入式容器与非侵入式容器(intrusive containers)

再传统的数据结构的实现中&#xff0c;分为侵入式容器和非侵入式容器两种 侵入式容器 这也是教材喜欢使用的数据结构的实现方式 &#xff0c;将数据结构放入类中&#xff0c;所以先讲这个 非侵入式容器&#xff1a; struct ListNode {ListNode *prev, *next;int value; }; …

五分钟搞懂分布式流控算法

流控是任何一个复杂系统都必须考虑的问题&#xff0c;本文介绍并比较了不同的流控算法&#xff0c;从而帮助我们可以基于系统需求和架构选择合适的方案。原文&#xff1a;Distributed Rate-Limiting Algorithms[1] 当我们设计分布式流控系统&#xff08;distributed rate-limit…

华硕TUF GAMING B460M i5-10500 电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件配置 硬件型号驱动情况 主板华硕TUF GAMING B460M-PLUS (LPC Controller B460芯片组 处理器特尔 Core i5-10500 3.10GHz六核已驱动 内存16 GB (IstRI DDR4 2666MH…

在字节划水的4年,很真实...

先简单交代一下&#xff0c;我是某不知名211的计算机本硕&#xff0c;18年毕业加入滴滴&#xff0c;之后跳槽到了头条&#xff0c;一直从事测试开发相关的工作。之前没有实习经历&#xff0c;算是四年半的工作经验吧。 这四年半之间完成了一次晋升&#xff0c;换了一家公司&am…

微信可以聚合聊天吗?如何同时管理多个微信?

现在很多企业用微信做私域流量运营&#xff0c;在里面搭建自己的私域流量。随着客户资源逐渐增多&#xff0c;需求增加&#xff0c;不仅需要联系客户&#xff0c;还要联系各大代理&#xff0c;开通多个微信号无疑是最佳方案。 但是不少做电商的朋友表示&#xff0c;微信号越来…

代理ip数据采集的优缺点

随着互联网时代的到来&#xff0c;数据已经成为企业发展和决策的关键。但是&#xff0c;不同的网站它对于数据访问的限制和反爬虫措施却是给企业的数据采集带来了挑战。针对这一问题&#xff0c;代理IP数据采集技术应运而生。但是使用代理ip来进行数据采集也有优缺点。 一、代理…

JS WEB框架Express日志模块winston和express-winston以及winston-daily-rotate-file优化

1.前言 1.Express的日志模块winston和express-winston已经提供了开箱即用的大多数功能&#xff0c;但是和其他语言相比&#xff0c;还缺失对日志记录的当前文件和行号的支持&#xff0c;需要自己实现&#xff0c;以此记录一下。 2.express-winston主要用于记录请求进入和结束时…

【运筹优化】ALNS自适应大领域搜索算法求解TSP问题 + Java代码实现

文章目录 一、TSP问题简介二、数学建模三、实现细节四、案例实战4.1 测试案例说明4.2 Java 完整代码4.2.1 TSP_Instance 实例类4.2.2 TSP_Solution 结果类4.2.3 TSP_Util 工具类4.2.4 TSP_Solver_ALNS 算法类4.2.5 RunAndPlot 运行类 4.3 运行结果展示 一、TSP问题简介 旅行推…

MySQL登录时报错:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘解决办法

问题描述 在云服务器使用 docker安装的Mysql5.7数据库&#xff0c;刚开始的时候使用正常&#xff0c;后面突然有一天就连接不上了&#xff0c;报错为: ERROR 1045 (28000): Access denied for user root1xxx(using password:YES)&#xff0c; 当登录MySQL数据库出现 Error 1045…