Spring你不知道的一种解耦模式

目录
  • 前言
  • 一个例子入门
  • 应用Service Locator Pattern
  • 剖析Service Locator Pattern
  • 总结

前言

不知道大家在项目中有没有遇到过这样的场景,根据传入的类型,调用接口不同的实现类或者说服务,比如根据文件的类型使用 CSV解析器或者JSON解析器,在调用的客户端一般都是用if else去做判断,比如类型等于JSON,我就用JSON解析器,那如果新加一个类型的解析器,是不是调用的客户端还要修改呢?这显然太耦合了,本文就介绍一种方法,服务定位模式Service Locator Pattern来解决,它帮助我们消除紧耦合实现及其依赖性,并提出将服务与其具体类解耦。

一个例子入门

我们通过一个例子来告诉你如何使用Service Locator Pattern

假设我们有一个从各种来源获取数据的应用程序,我们必须解析不同类型的文件,比如解析CSV文件和JSON文件。

定义一个类型的枚举

public enum ContentType {
  JSON,
  CSV
}

定义一个解析的接口

public interface Parser {
  List parse(Reader r);
}

根据不同的文件类型有不同的实现类

// 解析csv
@Component
public class CSVParser implements Parser {
  @Override
  public List parse(Reader r) { .. }
}
// 解析json
@Component
public class JSONParser implements Parser {
  @Override
  public List parse(Reader r) { .. }
}

最后写一个调用的客户端,通过switch case根据不同的类型调用不同的实现

@Service
public class Client {
  private Parser csvParser, jsonParser;
  @Autowired
  public Client(Parser csvParser, Parser jsonParser) {
    this.csvParser = csvParser;
    this.jsonParser = jsonParser;
  }
  public List getAll(ContentType contentType) {
    ..
    switch (contentType) {
      case CSV:
        return csvParser.parse(reader);
      case JSON:
        return jsonParser.parse(reader);
      ..
    }
  }
  ..
}

可能大部分人都是像上面一样的方式实现的,也能正常运行,那深入思考下,存在什么问题吗?

现在假如产品经理提出了一个新需求要支持XML类型的文件,是不是客户端也要修改代码,需要在switch case中添加新的类型,这就导致客户端和不同的解析器紧密耦合。

那么有什么更好的方法呢?

应用Service Locator Pattern

没错,那就是用上我们的服务定位模式Service Locator Pattern

让我们定义我们的服务定 位器接口ParserFactory, 它有一个接受内容类型参数并返回Parser的方法。

public interface ParserFactory {
  Parser getParser(ContentType contentType);
}

我们配置ServiceLocatorFactoryBean使用ParserFactory作为服务定 位器接口,ParserFactory这个接口不需要写实现类。

@Configuration
public class ParserConfig {
  @Bean("parserFactory")
  public FactoryBean serviceLocatorFactoryBean() {
    ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
    // 设置服务定位接口
    factoryBean.setServiceLocatorInterface(ParserFactory.class);
    return factoryBean;
  }
}

设置解析器Bean的名称为类型名称,方便服务定位

// 设置bean的名称和类型一致
@Component("CSV")
public class CSVParser implements Parser { .. }
@Component("JSON")
public class JSONParser implements Parser { .. }
@Component("XML")
public class XMLParser implements Parser { .. }

修改枚举, 添加XML

public enum ContentType {
  JSON,
  CSV,
  XML
}

最后用客户端调用,直接根据类型调用对应的解析器,没有了switch case

@Service
public class Client {
  private ParserFactory parserFactory;
  @Autowired
  public Client(ParserFactory parserFactory) {
    this.parserFactory = parserFactory;
  }
  public List getAll(ContentType contentType) {
    ..
    // 关键点,直接根据类型获取
    return parserFactory
        .getParser(contentType)
        .parse(reader);
  }
  ..
}

嘿嘿,我们已经成功地实现了我们的目标。现在再加新的类型,我们只要扩展添加新的解析器就行,再也不用修改客户端了,满足开闭原则。

如果你觉得Bean的名称直接使用类型怪怪的,这边可以建议你按照下面的方式来。

public enum ContentType {
  JSON(TypeConstants.JSON_PARSER),
  CSV(TypeConstants.CSV_PARSER),
  XML(TypeConstants.XML_PARSER);
  private final String parserName;
  ContentType(String parserName) {
    this.parserName = parserName;
  }
  @Override
  public String toString() {
    return this.parserName;
  }
  public interface TypeConstants {
    String CSV_PARSER = "csvParser";
    String JSON_PARSER = "jsonParser";
    String XML_PARSER = "xmlParser";
  }
}
@Component(TypeConstants.CSV_PARSER)
public class CSVParser implements Parser { .. }
@Component(TypeConstants.JSON_PARSER)
public class JSONParser implements Parser { .. }
@Component(TypeConstants.XML_PARSER)
public class XMLParser implements Parser { .. }

剖析Service Locator Pattern

通过前面的例子,想必大家基本知道服务定 位器模式如何使用了吧,现在我们深入剖析下。

服务定 位器模式消除了客户端对具体实现的依赖。以下引自 Martin Fowler 的文章总结了核心思想: “服务定 位器背后的基本思想是拥有一个知道如何获取应用程序可能需要的所有服务的对象。因此,此应用程序的服务定 位器将有一个在需要时返回“服务”的方法。”

SpringServiceLocatorFactoryBean实现了 FactoryBean接口,创建了Service Factory服务工厂Bean

总结

我们通过使用服务定 位器模式实现了一种扩展 Spring 控制反转的绝妙方法。它帮助我们解决了依赖注入未提供最佳解决方案的用例。也就是说,依赖注入仍然是首选,并且在大多数情况下不应使用服务定 位器来替代依赖注入。

到此这篇关于Spring你不知道的一种解耦模式的文章就介绍到这了,更多相关Spring解耦模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Boot小型项目如何使用异步任务管理器实现不同业务间的解耦

    目录 前言 一.异步任务管理器是什么? 二.实现步骤 1.自定义线程池 2. 新建异步任务管理器类 3. 新建异步工厂类 4. 调用 5. 实现效果 总结 前言 在有些业务场景中,系统对于响应时间有一定的要求,而一个方法里面同步执行的业务逻辑太多势必会影响响应速度,带来不好的用户体验.比如登录时记录登录用户的访问记录.注册时发送邮件.短信通知等等场景,不需要等待处理结果之后再进行下一步操作,这时候就可以使用异步线程进行处理,这样主线程不会因为这些耗时的操作而阻塞,保证主线程的流程可以正常进行.

  • 如何基于Spring使用工厂模式实现程序解耦

    这篇文章主要介绍了如何基于Spring使用工厂模式实现程序解耦,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1. 啥是耦合.解耦? 既然是程序解耦,那我们必须要先知道啥是耦合,耦合简单来说就是程序的依赖关系,而依赖关系则主要包括 1. 类之间的依赖 2. 方法间的依赖 比如下面这段代码: public class A{ public int i; } public class B{ public void put(A a){ System.o

  • Spring超详细讲解IOC与解耦合

    目录 前言 一.所谓耦合 二.Spring 三.核心IOC理解 1.容器 2.控制反转 3.依赖注入 四.Bean的实例化 1.无参构造 2.工厂静态方法 3.工厂实例方法(常用) 五.Bean的依赖注入 1.set注入 2.有参构造 六.第一个Spring案例 前言 回想写过的图书管理系统.租房系统.电影院卖票系统都是基于原生的JavaSE.OOP,没有用到任何框架,在层与层的关系中一个类要想获得与其他类的联系主要的方式还是靠new,这就导致层与层之间.对象与对象之间的依赖性强“动一发而迁全身

  • Spring Security OAuth2 授权码模式的实现

    写在前边 在文章OAuth 2.0 概念及授权流程梳理 中我们谈到OAuth 2.0的概念与流程,这里我准备分别记一记这几种授权模式的demo,一方面为自己的最近的学习做个总结,另一方面做下知识输出,如果文中有错误的地方,请评论指正,在此不胜感激 受众前提 阅读本文,默认读者已经过Spring Security有一定的了解,对OAuth2流程有一定了解 本文目标 带领读者对Spring Security OAuth2框架的授权码模式有一个比较直观的概念,能使用框架搭建授权码模式授权服务器与资源服

  • Spring 环境下实现策略模式的示例

    背景 最近在忙一个需求,大致就是给满足特定条件的用户发营销邮件,但是用户的来源有很多方式:从 ES 查询的.从 csv 导入的.从 MongoDB 查询-.. 需求很简单,但是怎么写的优雅,方便后续扩展,就存在很多门道了. 我们的项目是基于 Spring Boot 开发的,因此这篇文章也会基于 Spring Boot 作为基础框架,教你如何使用 Spring 依赖注入的特性,优雅的实现策略模式. 1. 简单粗暴 最简单粗暴直接的方式莫过于 if...else- 了,伪代码如下: if(来源 ==

  • Java的三种代理模式简述

    目录 一.代理模式是什么 二.Java的三种代理模式 1.静态代理 2.动态代理(也叫JDK代理) 3.Cglib代理 一.代理模式是什么 代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展. 比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing(). 1 public class Singer{ 2 public void sing(){ 3 System.out.println("唱一首歌"); 4 } 5 } 假如你希望,通过你的某种方式

  • 关于spring aop两种代理混用的问题

    目录 spring aop两种代理混用问题 一.首先复习一下两种代理 二.我们项目是spring-boot项目 spring的aop和代理模式理解 代理模式代码的主要特点是 代理模式目前实现的方式有三种 Aop的最大意义是 spring aop两种代理混用问题 工作繁忙,但是遇到问题还是要总结积累下来,今天项目中出现了代理混用的问题,解决之后记录一下对两种代理方式的学习理解. 一.首先复习一下两种代理 JDK动态代理 和 cglib代理 1.如果目标对象实现了接口,默认情况下会采用JDK的动态代

  • Java RabbitMQ的三种Exchange模式

    目录 简介 Direct模式 Fanout Exchange 示例代码 配置类 生产者 消费者 测试代码 Topic模式 示例代码 配置类 消息发送者 消息接收者 测试代码 总结 前言: 上一章讲解RabbitMq的相关知识和如何使用Spring Boot集成Rabbitmq发送消息,本文将讲解RabbitMQ三种Exchange模式. 简介 RabbitMQ的Exchange通常有三种模式分别为:Direct模式.Fanout模式.Topic模式. Direct模式 Rabbit的Direct

  • 浅谈Tomcat三种运行模式

    tomcat的运行模式有3种 一.bio(blocking I/O) 即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作(即java.io包及其子包).是基于JAVA的HTTP/1.1连接器,Tomcat7以下版本在默认情况下是以bio模式运行的.一般而言,bio模式是三种运行模式中性能最低的一种.我们可以通过Tomcat Manager来查看服务器的当前状态.(Tomcat7 或以下,在 Linux 系统中默认使用这种方式) 二.nio(new I/O) 是Java SE

  • 浅谈Spring的两种事务定义方式

    一.声明式 这种方法不需要对原有的业务做任何修改,通过在XML文件中定义需要拦截方法的匹配即可完成配置,要求是,业务处理中的方法的命名要有规律,比如setXxx,xxxUpdate等等.详细配置如下: <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="

  • 浅谈Spring的两种配置容器

    Spring提供了两种容器类型 SpringIOC容器是一个IOC Service Provider.提供了两种容器类型:BeanFactory和ApplicationContext.Spring的IOC容器是一个提供IOC支持的轻量级容器.除了基本的ioc支持,它作为轻量级容器还提供了IOC之外的支持. BeanFactory BeanFactory是基础类型IOC容器.顾名思义,就是生产Bean的工厂.能够提供完整的IOC服务.没有特殊指定的话,其默认采用延迟初始化策略.只有当客户端对象需要

  • 深入理解java三种工厂模式

    适用场合: 7.3 工厂模式的适用场合 创建新对象最简单的办法是使用new关键字和具体类.只有在某些场合下,创建和维护对象工厂所带来的额外复杂性才是物有所值.本节概括了这些场合. 7.3.1 动态实现 如果需要像前面自行车的例子一样,创建一些用不同方式实现同一接口的对象,那么可以使用一个工厂方法或简单工厂对象来简化选择实现的过程.这种选择可以是明确进行的也可以是隐含的.前者如自行车那个例子,顾客可以选择需要的自行车型号:而下一节所讲的XHR工厂那个例子则属于后者,该例中所返回的连接对象的类型取决

  • [Oracle] Data Guard 之 三种保护模式介绍

    Data Guard提供如下三种数据保护模式: 1)最高保护模式(Maximum Protection)这里的"最大保护"是指最大限度的保护数据不丢失,也就是至少有一个standby和primary保持实时同步,但这样做的代价很大,即当一个事务提交时,不但要写到primary段的online redo log,还有写到至少一个standby的standby redo log.这样会有一个严重的问题,就是当standby出现故障或网络故障,导致日志无法同步时,primary数据库会被sh

随机推荐

其他