目标:高复用性,高内聚,低耦合
目的:高可读性,重用性,可靠性
=,如果项目中出现级联删除,就是删除person的时候同时删除card对象,那么他们的依赖关系就介于聚合和组合之间,被算作组合关系
依赖,关联,聚合,组合
这四种关系,都表示类与类之间的关联关系,说明两个类之间有某种联系。而依据与联系的紧密程度,由弱到强,依次成为 依赖<关联<聚合<组合
其中标准的java bean对象都是聚合关系,也就是说面向对象的封装特征中类和它的引用类型属性的关系为聚合关系,继承关系要慎用,通常情况下都是用组合关系代替,除非是is a的关系,has a的关系必须用聚合or组合关系
七大原则
单一职责原则:抽象好的类只负责一种功能事,否则耦合度高,做修改时可能会影响另一个功能
接口隔离原则:一个类A对另一个类B的依赖(a中通过接口b引用来让它的接口实现类B来干活)应该建立在最小接口之上,类B在类A中被用到几个方法,接口b中就应该有多少对应的,否则b应该被拆分成对应的b1接口;
系统中有些特殊的对象我们只允许它存在一个,不能被重复创建,防止资源浪费,更为了避免两个对象间状态不一致产生的问题,比如Windows系统始终只能弹出一个任务管理器窗口,因为如果弹出多个首先会造成资源浪费,最严重的是如果多个任务管理器显示状态不一致问题会给用户带来很大困扰
单例模式三要素
饿汉式:不够节约资源,但可以避免一切线程安全问题
懒汉式:更加节约资源,但高并发下容易产生一些列线程安全问题,导致系统中出现多个单例对象,造成问题,解决方法
注意区分,单例模式和Spring的singleton
核心组件
问题:工厂方法内部还是有大量的if-else语句,不符合开闭原则,需要进一步消灭里面的if-else
案例:Spring中的BeanFactory用到了简单工厂模式,工厂方法是getBean,传入唯一标识(beanName,beanClass)可以获取到相应的bean,符合简单工厂模式特征
重要组件
针对简单工厂的升级版,不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品类型提供不同的普通工厂类,如此依赖工厂方法模式让一个产品的创建过程延迟到其子类工厂中,也就是先确定具体工厂,确定后再由该工厂负责他所创建的对象,用同数量的工厂类消灭简单工厂中工厂方法中的if-else
新问题:由于工厂方法模式中的每个子工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销,即便是符合开闭原则,但增加新功能很容易导致类爆炸
案例
Spring factoryBean
我们希望一个工厂可以提供多个产品对象,而不是单一的产品对象,如一个电器工厂,它可以生产电视机、电冰箱、空调等多种电器,而不是只生产某一种电器,可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产
产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。
重要组件
超级工厂IProductFactoty:里面有多个接口方法factorymethod,这些接口方法对应着一个产品等级结构的创建,例如我们用三个接口方法,分别代表着生产电视、洗衣机、空调这三种产品
ConcreteFactory,实现IProductFactoty中的多个接口方法factorymethod,生产具体的产品ConcreteProduct,ConcreteFactory代表是一个产品族,比如美的和海尔这两个具体工厂
Prodouct抽象产品 ,对应产品等级结构,比如这里有电视、洗衣机、空调三种产品
ConcreteProduct具体产品,真正的产品,这里有六个,海尔和美的的电视、洗衣机、空调
工厂模式总结:
工厂模式的核心原则是解耦,提供创建对象代码的重用性,为了符合开闭原则①客户端层面(前端js代表做选择),选择好了相关的子类工厂,接口层面来创建相关的对象工厂,创建的过程可以结合配置文件,到时候业务发生变化只需要改配置文件即可,更好的符合开闭原则
原型模式(Prototype Pattern)是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。即用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
浅克隆:将原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。即用基类Object的clone()方法或序列化。
深克隆:序列化完成深克隆,或者在clone的过程中手动克隆下相关的对象属性
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
建造者模式
● Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,builder中组合了一个product产品对象,在该接口中一般声明两类方法,一类是抽象方法方法是buildPartX(),它们用于创建复杂对象的各个部件,细节由各个子类来实现;另一类方法是getResult(),它们用于返回已创建完毕复杂对象。
● ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
● Product(产品角色):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
● Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。
在有些情况下,为了简化系统结构,可以将Director和抽象建造者Builder进行合并,合并方式就是,在Builder内部设置一个核心方法construct,Builder自己调用自己的建造方法来完成一系列角色创建
客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
源码应用
个人应用@Builder
产品的创建与生产过程解耦,掩盖了真实的产品创建过程
意义:每一个具体建造者都相对独立,可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”
定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method)
模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法
ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。
模板方法,代表是一个由多个小步骤组成的一个复杂流程,其中这些小步骤很多是固定套路,只有个别方法是不固定的
固定套路我们自己在抽象类或其他类中定义具体方法,在模板方法里直接拿来用,大大增强了这种方法的重用性,不固定的流程我们定义为抽象方法或者是空方法,具体的实现过程肯定是延迟到子类中来做实现,最后直接由子类调用模板方法完成这个具体的复杂流程
应用案例
参考文章
我的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?答案是引入一个电源适配器(AC Adapter),俗称充电器或变压器,有了这个电源适配器,生活用电和笔记本电脑即可兼容
适配器模式有两种实现方式:类适配器和对象适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
在对象适配器模式结构图中包含如下几个角色:
● Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
● Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
● Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码
经典模式,对象适配器
class Adapter extends Target {
private Adaptee adaptee; //维持一个对适配者对象的引用
public Adapter(Adaptee adaptee) {
this.adaptee=adaptee;
}
@override
public void request() {
adaptee.specificRequest(); //转发调用
}
}
类适配器
public interface AmerciaInterface{
void powerCharge();
}
public class AmericaCharge implements AmerciaInterface{
@Override
public void powerCharge() {
System.out.println("美国插口");
}
}
public interface GBSocketInterface {
void powerWithThreeFlat();
}
public class GBCharge implements GBSocketInterface{
@Override
public void powerCharge() {
System.out.println("国标插口");
}
}
public class AmerciaHotel {
//旅馆中有一个德标的插口
private AmerciaInterface dbSocket;
public void setSocket (AmerciaInterface dbSocket){
this.dbSocket = dbSocket;
}
//旅馆中有一个充电的功能
public void charge(){
//使用美国插口充电
dbSocket.powerWithTwoRound();
}
}
public class GBAdatpter implements AmerciaInterface{
private GBSocketInterface gbSocketInterface;
public GBAdatpter(GBSocketInterface gbSocketInterface) {
this.gbSocketInterface = gbSocketInterface;
}
@Override
public void powerCharge() {
System.out.println("适配器适配:Amercia => GB");
gbSocketInterface.powerCharge();
}
}
public class Test {
public static void main(String[] args) {
// 我去美国旅游,带去的充电器gbCharge是国标的
GBCharge gbCharge = new GBCharge();
//来到美国,我找了一家宾馆住下,宾馆充电器仅支持美国充电器
AmerciaHotel amerciaHotel = new AmerciaHotel();
// 由于没法充电,我拿出随身带去的适配器,并且将我带来的充电器插在适配器的上端插孔中。这个上端插孔是符合国标的,我的充电器完全可以插进去
GBAdatpter gbAdatpter = new GBAdatpter(gbCharge);
//再将适配器的下端插入宾馆里的插座上
amerciaHotel.setCharge(gbAdatpter);
//开始充电
amerciaHotel.charge();
}
}
适配器模式在不做任何改变的前提下将一个类的接口转换成客户期望的另一个接口,让原本不兼容的接口可以合作无间。
适配器模式经典案例org.springframework.web.servlet.DispatcherServlet#doDispatch
这里也可以这样改版:HandlerAdapter接口用一个list容器handlerAdapter代替,这是一个类容器,用于管理这四种适配器类,也就是接口一定程度上也可以看做是一系列类的管理器(容器)
这里为每个不同的handler方法都对应一个HandlerAdapter,适配器的作用就是适配(support方法)和处理(handler),真正的请求处理逻辑是HandlerAdapter做的,这在一定程度上可以大大减少if-else的使用,优化代码结构
应用案例
而绿灯亮起,汽车可以继续前行。在这个过程中,交通信号灯是汽车(更准确地说应该是汽车驾驶员)的观察目标,而汽车是观察者。随着交通信号灯的变化,汽车的行为也将随之而变化,一盏交通信号灯可以指挥多辆汽车
观察者模式描述了如何建立对象与对象之间的依赖关系,以及如何构造满足这种需求的系统。观察者模式包含观察目标和观察者两类对象,一个目标可以有任意数目的与之相依赖的观察者,一旦观察目标的状态发生改变,所有的观察者都将得到通知。作为对这个通知的响应,每个观察者都将监视观察目标的状态以使其状态与目标状态同步,这种交互也称为发布-订阅(Publish-Subscribe)。观察目标是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅它并接收通知。
实际案例
事件模型ApplicationEvent(类似于Subject),此类为Spring事件中用于扩展的事件对象,构造方法中Object类似的的source是具体的事件,所有需要使用事件驱动模型发布的事件(类似于ConcreateSubject) 都可以继承此类,例如
事件发布者ApplicationEve ntPublisher(Client中的关键组件),实现该接口则具备了事件发布能力,最典型的就是AbstractApplicationContext,通过publishEvent方法来进行事件发布,①对象转化,object类似转化为ApplicationEvent类型②获取到ApplicationEventMulticaster③判断spring容器是否具有父容器,有的话会在事件发布的时候通知到父容器,主要是通过事件发布器来完成的,事件发布者往往作为一个实例属性聚合到真正的时间发布者
真正的事件发布者:RealApplicationEventPublisher(类似于Client),持有事件发布者ApplicationEventPublisher,调用publishEvent方法传参事件对象event。即作为ApplicationEvent的核心属性,事件源source
广播事件发布器ApplicationEventMulticaster(类似于ObserverManagement),实现后具有事件发布的能力(类似于),在refresh的13行方法中完成初始化SimpleApplicationEventMulticaster或其它,内部有一个Map集合retrieverCache统一对全部的监听器进行管理,对管理器进行添加、移除和集体通知(multicastEvent方法)
ApplicationListener事件监听器(类似于Observer观察者),核心就是一个时间相应方法onApplicationEvent,被各类监听器实现,比如AbstractTypeAwareSupport(类似于ConcreateObserver观察者))
装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在现实生活中,这种情况也到处存在,例如一张照片,我们可以不改变照片本身,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小相框的外面再套一个大相框。
装饰模式是一种用于替代继承的技术,核心使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
● Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
● ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
● Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
● ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
不用改动原有代码,靠增加装饰者类完成,更符合开闭原则,继承会伴随着类爆炸问题,可完美避免
,例如Spring中Wrapper或Decorator结尾的类
去茶馆喝茶,最简单的方式就是跟茶馆服务员说想要一杯什么样的茶,是铁观音、碧螺春还是西湖龙井?正因为茶馆有服务员,顾客无须直接和茶叶、茶具、开水等交互,整个泡茶过程由服务员来完成,顾客只需与服务员交互即可,整个过程非常简单省事, 在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互。外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。在外观模式中,那些需要交互的业务类被称为子系统(Subsystem)。如果没有外观类,那么每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大
https://www.bilibili.com/video/BV1G4411c7N4?p=89&vd_source=c2a7fc0879fca3ca35e6d65757407476
内存属于计算机的“稀缺资源”,不应该用来“随便浪费”
当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题。例如在一个文本字符串中存在很多重复的字符,如果每一个字符都用一个单独的对象来表示,将会占用较多的内存空间,那么我们如何去避免系统中出现大量相同或相似的对象,同时又不影响客户端程序通过面向对象的方式对这些对象进行操作?
享元模式通过共享技术实现相同或相似对象的重用,在逻辑上每一个出现的字符都有一个对象与之对应,然而在物理上它们却共享同一个享元对象,这个对象可以出现在一个字符串的不同地方,相同的字符对象都指向同一个实例,在享元模式中,存储这些共享实例对象的地方称为享元池(Flyweight Pool)。我们可以针对每一个不同的字符创建一个享元对象,将其放在享元池中,需要时再从享元池取出
享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)
享元模式通常和工厂模式&单例模式搭配使用
Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类提供抽象方法供具体享元类设置自己的属性(内部状态,例如旗子颜色),同时也提供公用方法来设置外部数据(外部状态UnsharedConcreteFlyweight => Coordinate旗子的坐标信息)。
ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态(旗子颜色)提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。例如 Coordinate旗子的坐标信息
FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,内部维护一个集合类型的享元池用于存储具体的享元类,可以结合工厂方法模式进行设计;如果对象存在,则直接从享元池获取,如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
一个软件系统中可以处理某个请求的对象不止一个,例如SCM系统中的采购单审批,主任、副董事长、董事长和董事会都可以处理采购单,他们可以构成一条处理采购单的链式结构,采购单沿着这条链进行传递,这条链就称为职责链。职责链可以是一条直线、一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。链上的每一个对象都是请求处理者,职责链模式可以将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行相应的处理,客户端无须关心请求的处理细节以及请求的传递,只需将请求发送到链上即可,实现请求发送者和请求处理者解耦。
abstract class Handler {
//维持对下家的引用
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor=successor;
}
public abstract void handleRequest(String request);
}
class ConcreteHandler extends Handler {
public void handleRequest(String request) {
if (请求满足条件) {
//处理请求
}
else {
this.successor.handleRequest(request); //转发请求
}
}
}
具体处理者是抽象处理者的子类,它具有两大作用:第一是处理请求,不同的具体处理者以不同的形式实现抽象请求处理方法handleRequest();第二是转发请求,如果该请求具体处理者类的典型代码如下:
消灭大量重复接口、重复流程代码,统一风格,高可读性,高扩展性,符合开闭原则
应用案例:新版统计数据导出
1、大一统接口:task,全部的导出功能共用的接口
2、service组件状态标识常量管理类ExportTaskType,22个组件代表22个service功能组件,意味着消灭了21个重复接口
3、前端公用查询基类StatisticsBasisVo,相当于对请求参数的公用提取
4、容器管理类:ProducerExportTaskContext
5、ProducerExportStrategy导出生产者接口模型
6、ConsumerExportStrategy 导出消费者接口模型
7、泛型抽象类AbstractProducerExportTaskHandler,导出生产者端流程的抽象类,相当于这22处导出公共部分的抽取,
8、导出公用逻辑服务者端接口ConsumerExportTaskService
9、导出公用逻辑消费者端接口ConsumerExportTaskService
10,AbstractConsumerExportTaskHandler: 导出消费者端流程抽象,同6
11 ConcreateProducerService:真正干活的导出生产者service组件,有22个负责不同业务模块的导出,基础自AbstractProducerExportTaskHandler,内部公其父类的execute方法,每个子类负责实现自己各种,大方面分按生产者和消费者为2类,消费者基础6,10,生产者继承5,7
12 ExportTaskListener监听kafka发送过来的消息,做消费,也就是数据处理
刘虹杰导出模式总结
应用设计模式:代理模式、AOP
背景:转人工逻辑
核心架构AOP(通用服务代码和核心业务代码解耦合、更适合团队开发、提高可重用新),用作,代码结构采用策略模式+ 模板方法+建造者模式(模板方法是),小功能上使用了适配器,aop切面配置切点,拦截满足切点条件的方法,拦截方法后将其作为任务分发,任务分发依据是读取类中注解信息,注解信息中存储着service名称,从方法注解中取出service类名,再去Spring容器中取出相应的service类,service类本质是由之前一个个if-else判断拆分开来的,拆分后大量公用代码提前为公共方法,对应模板类的功能方法,非通用方法用抽象方法代替,表示具体实现流程需要延迟到子类去实现,然后将这一系列流程组装成为模板方法,作为一个个service业务类的模板,业务类本质上都是基于模板类的,业务类和模板类都是继承关系,即父子类关系,每一个service业务类都可以看做是基于父类模板方法基本算法框架,把需要补全的内容进行补全
@Autowired
private Map userInfoSessionFillServiceMap;
但每个serviceImpl中单独写方法去注册,请求可能是来自哪里呢?web浏览器最常见,但也可能是aop动态代理,静态代理,中拿到的方法入参出参封装出来的请求,Spring代理模式是基于cglib或者jdk动态代理的,常见应用
被拆分的类,很多公共属性和公共流程(方法),我们提取公共流程为一个公用父类的公用方法属性,比如入参的合法性验证,非公用流程父类中使用抽象方法,实现步骤需要推迟到子类具体问题具体分析,这样子类可以在不改变算法架构的情况下重新定义该算法的某些特征即可,这就是模板方法模式,其中,为了保证模板方法的通用性与扩展性,模板方法中的入参和出参都要选用高层的基类或抽象类,也可以使用泛型类,模板方法模式抽象中的模板类(模板方法)通常可以用作我们某个项目的基本架构
建造者模式
关于CreateTypeFailResult的优化,此前针对每一种失败原因都对应个map属性,不美观,显得很多余,而且每初始化一个map类型属性会耗费一定资源,所以我们把这些个map都统一为1个,增加一个字段failType来标识识别类型,还有就行如果把时间看做资源,我们通过是从redis缓存中取数据,缓存中没数据才去mysql中去取,通过redis覆盖mysql的操作,大大节省了时间资源,这些都是蝇量模式的思想
观察者模式升级版(监听者模式)
Spring中bean中的scope取值Singleton,可以理解为工厂范围内的单例模式,也就是说一个beanName只能对应一个单实例对象,和从类的角度来看,可以存在多个类型一样的单例bean,只要是beanName不重复即可(一级缓存决定的),这是和传统单例模式最大区别,好处是
项目架构核心技术Kafka(生产者消费者中间件模式)
生产者端
消费者端
本质是一个kafka的监听器,监听到生产者端的请求,获取请求然后根据请求去收集导出数据,消费者端的代码流程和生产者端是完全一致的,也是拆分成了很多service,这里的service都是泛型类,我们之前有对这些入参的公共参数提取作为统一的参数基类,这里泛型限定为基类的子类,service中对父类抽象方法的实现都是用的泛型类,service类声明中已经直接完成,通过泛型代替了类型转换相关操作,消费者端的核心逻辑就是①拿到查询条件从es中取出目标数据,然后用来做拼接
状态锁