写在前面
偶然间看到一篇文章 《Java 中保持扩展性的几种套路和实现》,写的不错,但是类图画的差了点儿意思。于是,自己动手画了画,对其中的内容作了一些调整,对包做了进一步划分,便于理解消化
。以下是对GitHub项目 design-pattern-extend 的快览,后期将新的套路慢慢补充。
目录
- 写在前面
- 一、项目结构
- 二、关键信息
- 管道模式
- 过滤器链模式
- 事件分发模式
- 模板+工厂模式
- SPI模式
- 注解模式
- 其他
- 三、参考资料
- 写在后面
- 系列文章
一、项目结构
以下为GitHub项目 design-pattern-extend 的整体目录结构。images 目录下为设计模式的 plantuml 类图,client目录为模式main方法入口。
如果想向学习,又懒得敲代码的话,具体代码及示例请前往design-pattern-extend 自行获取。
design-pattern-extend
├─images
└─src└─main├─java│ └─cn│ └─thinkinjava│ └─design│ └─pattern│ └─extend│ ├─annotation│ │ └─client│ ├─eventdispatch│ │ ├─client│ │ ├─event│ │ ├─listener│ │ └─source│ ├─filterchain│ │ ├─client│ │ ├─filter│ │ └─request│ ├─pipeline│ │ ├─client│ │ ├─context│ │ └─value│ ├─spi│ │ ├─client│ │ └─service│ │ └─impl│ └─templatefactory│ ├─client│ └─handler│ └─base└─resources└─META-INF└─services
二、关键信息
下面简单说一下相关的设计模式扩展思路。
管道模式
管道模式简单说就是想对"某个对象"进行一些列的"操作"。
根据面向接口以及抽象的原则,
1、“操作”是要抽取出来一个接口,我们用管道值表示,即value包下的PipelineValue。
2、“某个对象”就是要操作的类,我们用上下文表示,即context包下的PipelineContext。
3、既然是管道,那“操作”得放到管道里面(添加/删除操作方法)还得执行管道操作方法(遍历管道值,调用方法),即pipeline包下的Pipeline。
这样,就形成3个体系,看类图,
以下为上下文对象,
package cn.thinkinjava.design.pattern.extend.pipeline.context;/*** 上下文** @author qiuxianbao* @date 2024/01/02*/
public interface PipelineContext {String FOR_TEST = "forTest";void set(String contextKey, Object contextValue);Object get(String contextKey);
}//
package cn.thinkinjava.design.pattern.extend.pipeline.context;import java.util.HashMap;
import java.util.Map;/*** 上下文的具体实现** @author qiuxianbao* @date 2024/01/02*/
public class StandardPipelineContext implements PipelineContext {private Map<String, Object> contextMap = new HashMap<>();@Overridepublic void set(String contextKey, Object contextValue) {contextMap.put(contextKey, contextValue);}@Overridepublic Object get(String contextKey) {return contextMap.get(contextKey);}}
以下为管道值对象,
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道中的操作对象** @author qiuxianbao* @date 2024/01/02*/
public interface PipelineValue {/*** 具体的操作** @param context 上下文* @return*/boolean execute(PipelineContext context);
}//
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;
import lombok.extern.slf4j.Slf4j;/*** 管道中的操作对象的抽象** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public abstract class AbstractPipelineValue implements PipelineValue {@Overridepublic boolean execute(PipelineContext context) {log.info("{} start", this.getClass().getSimpleName());boolean result = doExecute(context);log.info("{} end", this.getClass().getSimpleName());return result;}/*** 由子类实现** @param context* @return*/protected abstract boolean doExecute(PipelineContext context);
}//
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道中的操作对象的具体实现** @author qiuxianbao* @date 2024/01/02*/
public class ForeTest1Value extends AbstractPipelineValue {@Overrideprotected boolean doExecute(PipelineContext context) {// 比如:设置了一些值context.set(PipelineContext.FOR_TEST, true);return true;}
}//
package cn.thinkinjava.design.pattern.extend.pipeline.value;import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道中的操作对象的具体实现** @author qiuxianbao* @date 2024/01/02*/
public class ForeTest2Value extends AbstractPipelineValue {@Overrideprotected boolean doExecute(PipelineContext context) {return true;}
}
以下是管道操作,
package cn.thinkinjava.design.pattern.extend.pipeline;import cn.thinkinjava.design.pattern.extend.pipeline.value.PipelineValue;
import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;/*** 管道** 适用场景:* 当你的数据流需要经过很多同等逻辑处理时,可以考虑使用此套路,便于后续扩展** @author qiuxianbao* @date 2024/01/02*/
public interface Pipeline {/*** 执行操作** @param context 上下文,即要处理的对象* @return*/boolean invoke(PipelineContext context);/*** 添加操作** @param value 管道中的操作对象* @return*/boolean addValue(PipelineValue value);/*** 删除操作** @param value 管道中的操作对象* @return*/boolean removeValue(PipelineValue value);
}// 核心
package cn.thinkinjava.design.pattern.extend.pipeline;import cn.thinkinjava.design.pattern.extend.pipeline.value.PipelineValue;
import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;
import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;
import java.util.List;/*** 管道实现类** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class StandardPipeline implements Pipeline{private List<PipelineValue> valueList = new ArrayList<>();@Overridepublic boolean invoke(PipelineContext context) {boolean result = true;for (PipelineValue item : valueList) {try {result = item.execute(context);if (!result) {log.error("{}, execute is wrong", this.getClass().getSimpleName());}} catch (Exception e) {log.error(e.getMessage(), e);}}return result;}@Overridepublic boolean addValue(PipelineValue value) {if (valueList.contains(value)) {return true;}return valueList.add(value);}@Overridepublic boolean removeValue(PipelineValue value) {return valueList.remove(value);}
}
以下为客户端,
package cn.thinkinjava.design.pattern.extend.pipeline.client;import cn.thinkinjava.design.pattern.extend.pipeline.Pipeline;
import cn.thinkinjava.design.pattern.extend.pipeline.StandardPipeline;
import cn.thinkinjava.design.pattern.extend.pipeline.value.PipelineValue;
import cn.thinkinjava.design.pattern.extend.pipeline.context.PipelineContext;
import cn.thinkinjava.design.pattern.extend.pipeline.context.StandardPipelineContext;
import cn.thinkinjava.design.pattern.extend.pipeline.value.ForeTest1Value;
import cn.thinkinjava.design.pattern.extend.pipeline.value.ForeTest2Value;/*** 客户端** @author qiuxianbao* @date 2024/01/02*/
public class PipelineClient {public static void main(String[] args) {// 管道Pipeline pipeline = new StandardPipeline();// 管道中对象PipelineValue foreTestValue = new ForeTest1Value();PipelineValue graySwitchValue = new ForeTest2Value();pipeline.addValue(foreTestValue);pipeline.addValue(graySwitchValue);// 上下文PipelineContext context = new StandardPipelineContext();// 调用pipeline.invoke(context);System.out.println(context.get(PipelineContext.FOR_TEST));// ForeTest1Value start
// ForeTest1Value end
// ForeTest2Value start
// ForeTest2Value end
// true}
}
过滤器链模式
过滤器链,既然是链,那么就会有先后顺序。但它并不像前面说的管道那样,前面执行完,然后交给后面执行。它和管道是有区别的,这里巧妙地运用到了this和索引。
管道模式是一层进来然后出去,接着进行下一层。
// ForeTest1Value start
// ForeTest1Value end
// ForeTest2Value start
// ForeTest2Value end
过滤器链是从外到内一层一层都先进来,然后再由内到外一层一层再出去。
// ForTest1Filter before 1704180891151
// ForTest2Filter before 1704180891151
// ForTest2Filter end 1704180891152
// ForTest1Filter end 1704180891152
以下为操作对象(假设的),
package cn.thinkinjava.design.pattern.extend.filterchain.request;/*** @author qiuxianbao* @date 2024/01/02*/
public interface HttpRequest {
}//
package cn.thinkinjava.design.pattern.extend.filterchain.request;/*** @author qiuxianbao* @date 2024/01/02*/
public class StandardHttpRequest implements HttpRequest {
}
以下为过滤器对象,
package cn.thinkinjava.design.pattern.extend.filterchain.filter;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;/*** 过滤器** @author qiuxianbao* @date 2024/01/02*/
public interface Filter {void doFilter(HttpRequest httpRequest, FilterChain filterChain);
}//
package cn.thinkinjava.design.pattern.extend.filterchain.filter;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class ForTest1Filter implements Filter {@Overridepublic void doFilter(HttpRequest httpRequest, FilterChain filterChain) {log.info("{} before {}", this.getClass().getSimpleName(), System.currentTimeMillis());// 这里是重点filterChain.doFilter(httpRequest);log.info("{} end {}", this.getClass().getSimpleName(), System.currentTimeMillis());}
}//
package cn.thinkinjava.design.pattern.extend.filterchain.filter;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class ForTest2Filter implements Filter {@Overridepublic void doFilter(HttpRequest httpRequest, FilterChain filterChain) {log.info("{} before {}", this.getClass().getSimpleName(), System.currentTimeMillis());filterChain.doFilter(httpRequest);log.info("{} end {}", this.getClass().getSimpleName(), System.currentTimeMillis());}
}
以下为过滤器链,
package cn.thinkinjava.design.pattern.extend.filterchain;import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.Filter;/*** 拦截器链** 适用场景:* 常见的web请求场景** @author qiuxianbao* @date 2024/01/02*/
public interface FilterChain {void doFilter(HttpRequest httpRequest);void addFilter(Filter filter);
}// 核心
package cn.thinkinjava.design.pattern.extend.filterchain;import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.Filter;import java.util.ArrayList;
import java.util.List;/*** @author qiuxianbao* @date 2024/01/02*/
public class StandardFilterChain implements FilterChain {private List<Filter> filterList = new ArrayList<>();private int currentIndex = 0;@Overridepublic void doFilter(HttpRequest httpRequest) {if (currentIndex == filterList.size()) {return;}Filter filter = filterList.get(currentIndex);currentIndex = currentIndex + 1;filter.doFilter(httpRequest, this);}@Overridepublic void addFilter(Filter filter) {if (filterList.contains(filter)) {return;}filterList.add(filter);}
}
以下为客户端,
package cn.thinkinjava.design.pattern.extend.filterchain.client;import cn.thinkinjava.design.pattern.extend.filterchain.FilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.StandardFilterChain;
import cn.thinkinjava.design.pattern.extend.filterchain.request.HttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.request.StandardHttpRequest;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.ForTest1Filter;
import cn.thinkinjava.design.pattern.extend.filterchain.filter.ForTest2Filter;/*** 客户端** @author qiuxianbao* @date 2024/01/02*/
public class FilterClient {public static void main(String[] args) {FilterChain filterChain = new StandardFilterChain();filterChain.addFilter(new ForTest1Filter());filterChain.addFilter(new ForTest2Filter());HttpRequest request = new StandardHttpRequest();filterChain.doFilter(request);//ForTest1Filter before 1704180891151//ForTest2Filter before 1704180891151//ForTest2Filter end 1704180891152//ForTest1Filter end 1704180891152}
}
事件分发模式
事件派发器分配事件,谁满足了事件,则会有相应的事件监听器去处理事件。
一句话,抽象出来几个对象:事件、事件监听器(谁满足、怎么处理)、事件的产生(事件源)、事件派发器(能够拿到所有事件的监听器,进行循环)
以下为事件,
package cn.thinkinjava.design.pattern.extend.eventdispatch.event;/*** 事件** @author qiuxianbao* @date 2024/01/02*/
public interface Event {/*** 事件名称** @return*/String getName();
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.event;/*** @author qiuxianbao* @date 2024/01/02*/
public class EventForTest1 implements Event {@Overridepublic String getName() {return getClass().getSimpleName();}
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.event;/*** @author qiuxianbao* @date 2024/01/02*/
public class EventFor2 implements Event {@Overridepublic String getName() {return getClass().getSimpleName();}
}
以下为事件监听器,
package cn.thinkinjava.design.pattern.extend.eventdispatch.listener;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;/*** 事件监听器,处理事件** @author qiuxianbao* @date 2024/01/02*/
public interface EventListener {/*** 是否支持此事件** @param event* @return*/boolean supportEvent(Event event);/*** 处理事件** @return*/boolean handlerEvent(Event event);
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.listener;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class EventListenerForTest implements EventListener {@Overridepublic boolean supportEvent(Event event) {return event.getName().contains("Test");}@Overridepublic boolean handlerEvent(Event event) {log.info("{} \t handler {}", this.getClass().getSimpleName(), event.getName());return true;}
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.listener;import java.util.ArrayList;
import java.util.List;/*** 事件监听器管理** @author qiuxianbao* @date 2024/01/02*/
public class EventListenerManager {private static List<EventListener> eventListenerList = new ArrayList<>();/*** 添加事件监听器** @param eventListener* @return*/public static boolean addEventListener(EventListener eventListener) {if (eventListenerList.contains(eventListener)) {return true;}return eventListenerList.add(eventListener);}/*** 移除事件监听器** @param eventListener* @return*/public static boolean removeEventListener(EventListener eventListener) {if (eventListenerList.contains(eventListener)) {return eventListenerList.remove(eventListener);}return true;}/*** 获取事件监听器** @return*/public static List<EventListener> getEventListenerList() {return eventListenerList;}}
以下为事件源,
package cn.thinkinjava.design.pattern.extend.eventdispatch.source;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;/*** 事件源** @author qiuxianbao* @date 2024/01/02*/
public interface EventSource {/*** 发出事件** @return*/Event fireEvent();
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.source;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import cn.thinkinjava.design.pattern.extend.eventdispatch.event.EventForTest1;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class EventSourceForTest1 implements EventSource {@Overridepublic Event fireEvent() {// 发出的就是具体的事件了Event event = new EventForTest1();log.info("{} \t fireEvent {}", this.getClass().getSimpleName(), event.getName());return event;}
}//
package cn.thinkinjava.design.pattern.extend.eventdispatch.source;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import cn.thinkinjava.design.pattern.extend.eventdispatch.event.EventFor2;
import lombok.extern.slf4j.Slf4j;/*** @author qiuxianbao* @date 2024/01/02*/
@Slf4j
public class EventSourceFor2 implements EventSource {@Overridepublic Event fireEvent() {Event event = new EventFor2();log.info("{} \t fireEvent {}", this.getClass().getSimpleName(), event.getName());return event;}
}
以下为事件派发器,
package cn.thinkinjava.design.pattern.extend.eventdispatch;import cn.thinkinjava.design.pattern.extend.eventdispatch.event.Event;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListener;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerManager;
import org.apache.commons.collections.CollectionUtils;/*** 事件分发器** @author qiuxianbao* @date 2024/01/02*/
public class EventDispatcher {private EventDispatcher() {}/*** 分发事件** @param event*/public static void dispatchEvent(Event event) {// 核心if (CollectionUtils.isNotEmpty(EventListenerManager.getEventListenerList())) {for (EventListener eventListener : EventListenerManager.getEventListenerList()) {if (eventListener.supportEvent(event)) {eventListener.handlerEvent(event);}}}}
}
以下为客户端,
package cn.thinkinjava.design.pattern.extend.eventdispatch.client;import cn.thinkinjava.design.pattern.extend.eventdispatch.EventDispatcher;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerManager;
import cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSource;
import cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceForTest1;
import cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceFor2;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListener;
import cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerForTest;/*** @author qiuxianbao* @date 2024/01/02*/
public class EventClient {public static void main(String[] args) {// 创建事件监听器EventListener eventListener = new EventListenerForTest();EventListenerManager.addEventListener(eventListener);// 创建事件源EventSource eventSource1 = new EventSourceForTest1();EventSource eventSource2 = new EventSourceFor2();// 发布事件EventDispatcher.dispatchEvent(eventSource1.fireEvent());EventDispatcher.dispatchEvent(eventSource2.fireEvent());// 11:50:17.029 [main] INFO cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceForTest1 - EventSourceForTest1 fireEvent EventForTest1
// 11:50:17.067 [main] INFO cn.thinkinjava.design.pattern.extend.eventdispatch.listener.EventListenerForTest - EventListenerForTest handler EventForTest1
// 11:50:17.077 [main] INFO cn.thinkinjava.design.pattern.extend.eventdispatch.source.EventSourceFor2 - EventSourceFor2 fireEvent EventFor2}
}
模板+工厂模式
提到模板,通常都是在抽象类中实现通用逻辑,然后留出接口未实现的交给其子类实现。这里组合工厂,工厂用于维护所有的处理器。
以下是处理器 + 工厂,
package cn.thinkinjava.design.pattern.extend.templatefactory.handler.base;import cn.thinkinjava.design.pattern.extend.templatefactory.PiiContent;import java.lang.reflect.Field;/*** @author qiuxianbao* @date 2024/01/04*/
public interface PiiDomainFieldHandler {/*** 处理实际操作* 读----从PiiContent获取数据回填domain** @param domain* @param domainField* @param piiContent* @param <T>* @return*/<T extends Object> boolean handlerRead(T domain, Field domainField, PiiContent piiContent);/*** 处理实际操作* 写----将domain中需要写入pii的字段数据写入PiiContent** @param domain* @param domainField* @param piiContent* @param <T>* @return*/<T extends Object> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent);/*** 当前处理器是否支持该领域对象** @param domain* @param domainField* @param <T>* @return*/<T extends Object> boolean isSupport(T domain, Field domainField);/*** 获取处理器对应的元信息** @return*/String getPiiDomainMeta();
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler.base;import cn.thinkinjava.design.pattern.extend.templatefactory.PiiContent;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** 模板方法* 通过抽象类实现** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public abstract class PiiDomainFieldHandlerBase implements PiiDomainFieldHandler{@Overridepublic <T> boolean handlerRead(T domain, Field domainField, PiiContent piiContent) {log.info("{} handlerRead {}", this.getClass().getSimpleName(), domain.getClass().getSimpleName());return true;}@Overridepublic <T> boolean handlerWrite(T domain, Field domainField, PiiContent piiContent) {log.info("{} handlerWrite {}", this.getClass().getSimpleName(), domain.getClass().getSimpleName());return true;}
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandlerBase;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public class ForTestSupportFieldHandler extends PiiDomainFieldHandlerBase {@Overridepublic <T> boolean isSupport(T domain, Field domainField) {if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {log.info("{} is support, to do some business", this.getClass().getSimpleName());return true;}return false;}@Overridepublic String getPiiDomainMeta() {return this.getClass().getSimpleName();}
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandlerBase;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;/*** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public class ForTestNotSupportFieldHandler extends PiiDomainFieldHandlerBase {@Overridepublic <T> boolean isSupport(T domain, Field domainField) {if (this.getClass().getSimpleName().equalsIgnoreCase(domain.getClass().getSimpleName())) {log.info("{} is support, to do some business", this.getClass().getSimpleName());return true;}return false;}@Overridepublic String getPiiDomainMeta() {return this.getClass().getSimpleName();}
}//
package cn.thinkinjava.design.pattern.extend.templatefactory.handler;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandler;import java.util.ArrayList;
import java.util.List;/*** 工厂类* 手动添加处理器** @author qiuxianbao* @date 2024/01/04*/
public class PiiDomainFieldHandlerFactory {/*** 创建领域处理器** @return*/public static List<PiiDomainFieldHandler> createPiiDomainFieldHandler() {List<PiiDomainFieldHandler> piiDomainFieldHandlerList = new ArrayList();// 添加处理器piiDomainFieldHandlerList.add(new ForTestSupportFieldHandler());piiDomainFieldHandlerList.add(new ForTestNotSupportFieldHandler());return piiDomainFieldHandlerList;}
}
以下是上下文对象,
package cn.thinkinjava.design.pattern.extend.templatefactory;import java.util.HashMap;
import java.util.Map;/*** 上下文对象** @author qiuxianbao* @date 2024/01/04*/
public class PiiContent {public static String FORTEST="fortest";private Map<String, Object> piiDataMap = new HashMap<>();private Map<String, Object> piiContextMap = new HashMap<>();public void putPiiData(String domainFieldName, Object domainFieldValue) {piiDataMap.put(domainFieldName, domainFieldValue);}public Object getPiiData(String domainFieldName) {return piiDataMap.get(domainFieldName);}public void putPiiContext(String contextName, Object contextNameValue) {piiContextMap.put(contextName, contextNameValue);}public Object getPiiContext(String contextName) {return piiContextMap.get(contextName);}
}
以下是处理器注册器,从工厂中拿出处理器,对外提供处理操作,
package cn.thinkinjava.design.pattern.extend.templatefactory;import cn.thinkinjava.design.pattern.extend.templatefactory.handler.PiiDomainFieldHandlerFactory;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 处理器注册器** @author qiuxianbao* @date 2024/01/04*/
@Slf4j
public class PiiHandlerRegistry {private static Map<String, PiiDomainFieldHandler> piiDomainFieldHandlerMap = new HashMap<>();public static void putHandler(String piiDomainFieldName, PiiDomainFieldHandler piiDomainFieldHandler) {if (StringUtils.isEmpty(piiDomainFieldName)) {log.warn(" piiDomainFieldName is null,continue");return;}if (piiDomainFieldHandler == null) {log.warn(piiDomainFieldName + " piiDomainFieldHandler is null,continue");return;}if (!piiDomainFieldHandlerMap.containsKey(piiDomainFieldName)) {piiDomainFieldHandlerMap.put(piiDomainFieldName, piiDomainFieldHandler);}}public static <T extends Object> int handlerRead(T domain, Field domainField, PiiContent piiContent) {int num = 0;for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :piiDomainFieldHandlerMap.entrySet()) {if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {piiDomainFieldHandlerEntry.getValue().handlerRead(domain, domainField, piiContent);}}return num;}public static <T extends Object> int handlerWrite(T domain, Field domainField, PiiContent piiContent) {int num = 0;for (Map.Entry<String, PiiDomainFieldHandler> piiDomainFieldHandlerEntry :piiDomainFieldHandlerMap.entrySet()) {if (piiDomainFieldHandlerEntry.getValue().isSupport(domain, domainField)) {piiDomainFieldHandlerEntry.getValue().handlerWrite(domain, domainField, piiContent);}}return num;}public static Map<String, PiiDomainFieldHandler> getPiiDomainFieldHandlerMap() {return piiDomainFieldHandlerMap;}public static void init() {List<PiiDomainFieldHandler> piiDomainFieldHandlerList = PiiDomainFieldHandlerFactory.createPiiDomainFieldHandler();if (CollectionUtils.isNotEmpty(piiDomainFieldHandlerList)) {for (PiiDomainFieldHandler piiDomainFieldHandler :piiDomainFieldHandlerList) {putHandler(piiDomainFieldHandler.getPiiDomainMeta(), piiDomainFieldHandler);}}}
}
以下是客户端,
package cn.thinkinjava.design.pattern.extend.templatefactory.client;import cn.thinkinjava.design.pattern.extend.templatefactory.PiiContent;
import cn.thinkinjava.design.pattern.extend.templatefactory.PiiHandlerRegistry;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.ForTestNotSupportFieldHandler;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.ForTestSupportFieldHandler;
import cn.thinkinjava.design.pattern.extend.templatefactory.handler.base.PiiDomainFieldHandler;import java.util.Map;/*** 客户端** @author qiuxianbao* @date 2024/01/04*/
public class PiiClient {public static void main(String[] args) {// 通过工厂,把处理器放到Map中PiiHandlerRegistry.init();// 遍历处理器for (Map.Entry<String, PiiDomainFieldHandler> entryHandler :PiiHandlerRegistry.getPiiDomainFieldHandlerMap().entrySet()) {System.out.println(entryHandler.getKey() + "\t" + entryHandler.getValue().getPiiDomainMeta());}//PiiContent piiContent = new PiiContent();piiContent.putPiiContext(PiiContent.FORTEST, PiiContent.FORTEST);// 请求处理System.out.println("ForTestSupportFieldHandler start");PiiHandlerRegistry.handlerRead(new ForTestSupportFieldHandler(), null, piiContent);System.out.println("ForTestSupportFieldHandler end");// 请求处理System.out.println("ForTestNotSupportFieldHandler start");PiiHandlerRegistry.handlerRead(new ForTestNotSupportFieldHandler(), null, piiContent);System.out.println("ForTestNotSupportFieldHandler end");}
}
SPI模式
SPI核心就是ServiceLoader
package java.util;
public final class ServiceLoader<S>implements Iterable<S> {private static final String PREFIX = "META-INF/services/";
}
以下为简单示例:
1、resources目录下建META-INF/services目录
2、新建文件,文件名为接口全路径名。文件内容为实现类的全路径名。
接口和实现类,
package cn.thinkinjava.design.pattern.extend.spi.service;/*** @author qiuxianbao* @date 2024/01/02*/
public interface RemoteService {
}//
package cn.thinkinjava.design.pattern.extend.spi.service.impl;import cn.thinkinjava.design.pattern.extend.spi.service.RemoteService;/*** @author qiuxianbao* @date 2024/01/02*/
public class RemoteServiceImpl implements RemoteService {
}
工具类,
package cn.thinkinjava.design.pattern.extend.spi;import java.util.HashMap;
import java.util.Map;/*** 存储策略依赖的服务, 统一管理** @author qiuxianbao* @date 2024/01/02*/
public class DependServiceRegistryHelper {private static Map<String, Object> dependManagerMap = new HashMap<>();public static boolean registryMap(Map<Class, Object> dependManagerMap) {for (Map.Entry<Class, Object> dependEntry : dependManagerMap.entrySet()) {registry(dependEntry.getKey(), dependEntry.getValue());}return true;}public static boolean registry(Class cls, Object dependObject) {dependManagerMap.put(cls.getCanonicalName(), dependObject);return true;}public static Object getDependObject(Class cls) {return dependManagerMap.get(cls.getCanonicalName());}
}//
package cn.thinkinjava.design.pattern.extend.spi;import cn.thinkinjava.design.pattern.extend.spi.service.RemoteService;import java.util.Iterator;
import java.util.ServiceLoader;/*** SPI的方式加载** 说明:* 1.resource文件夹下建 META-INF/services文件夹* 2.创建一个文件,文件名为接口的全限定名,文件内容为接口实现类的全限定名** @author qiuxianbao* @date 2024/01/02*/
public class SpiServiceLoaderHelper {public static RemoteService getProductPackageRemoteServiceInterface() {Object serviceCache = DependServiceRegistryHelper.getDependObject(RemoteService.class);if (serviceCache != null) {return (RemoteService) serviceCache;}RemoteService serviceInterface = loadSpiImpl(RemoteService.class);DependServiceRegistryHelper.registry(RemoteService.class, serviceInterface);return serviceInterface;}/*** 以spi的方式加载实现类** @param cls* @return*/private static <P> P loadSpiImpl(Class<P> cls) {ServiceLoader<P> spiLoader = ServiceLoader.load(cls);Iterator<P> iterator = spiLoader.iterator();if (iterator.hasNext()) {return iterator.next();}throw new RuntimeException("SpiServiceLoaderHelper loadSpiImpl failed, please check spi service");}}
以下为客户端,
package cn.thinkinjava.design.pattern.extend.spi.client;import cn.thinkinjava.design.pattern.extend.spi.SpiServiceLoaderHelper;
import cn.thinkinjava.design.pattern.extend.spi.service.RemoteService;/*** @author qiuxianbao* @date 2024/01/02*/
public class SPIClient {public static void main(String[] args) {RemoteService remoteService= SpiServiceLoaderHelper.getProductPackageRemoteServiceInterface();System.out.println(remoteService);// cn.thinkinjava.main.extend.spi.service.impl.ProductPackageRemoteServiceInterfaceImpl@2752f6e2}
}
注解模式
通过添加注解,可以进行一些扩展操作。
比如:可以把所有加过注解的类通过Map缓存中,再进行反射处理。
TcpMapping + TcpMappingScan + TcpFinder
以下是一个简单示例:
package cn.thinkinjava.design.pattern.extend.annotation;import org.springframework.stereotype.Component;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于测试的标识注解** @author qiuxianbao* @date 2024/01/04*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ForTestAnnotation {
}//
package cn.thinkinjava.design.pattern.extend.annotation;/*** 代测试的Bean** @author qiuxianbao* @date 2024/01/04*/
@ForTestAnnotation
public class ForTestBean {public ForTestBean() {System.out.println(ForTestBean.class.getSimpleName() + " init");}}//
package cn.thinkinjava.design.pattern.extend.annotation;import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;/*** 注解解析器** @author qiuxianbao* @date 2024/01/04*/
@Component
public class ForTestAnnotationProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 获取目标类是否有ForTestAnnotation注解ForTestAnnotation annotation = AnnotationUtils.findAnnotation(AopUtils.getTargetClass(bean),ForTestAnnotation.class);if (annotation == null) {return bean;}// 处理想要的扩展System.out.println(beanName + " has ForTestAnnotation");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}//
package cn.thinkinjava.design.pattern.extend.annotation.client;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** 客户端** @author qiuxianbao* @date 2024/01/04*/
public class ForTestClient {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.thinkinjava.main.extend.annotation");System.out.println(ForTestClient.class.getSimpleName());}}
其他
…
三、参考资料
《Java 中保持扩展性的几种套路和实现》
写在后面
如果本文内容对您有价值或者有启发的话,欢迎点赞、关注、评论和转发。您的反馈和陪伴将促进我们共同进步和成长。
系列文章
【GitHub】- design-pattern(设计模式)