一、概述
责任链模式是行为模式的一种,它将需要触发的Handler组成一条链,
发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个Handler来处理它或者直到最后也没有对象处理而留在链末尾端;
责任连模式的重点是在链上,由一条链去处理相似的请求,在链中决定谁来处理这个请求。
责任链模式的角色:
- 抽象处理者角色(Handler):该角色对请求进行抽象处理,并定义一个方法和返回下一个处理的引用。
- 具体处理者(Concrete Handler)角色:该角色接到请求后,可以选择处理掉,或则将请求传给下一个处理则。
由于具体处理者持有对下一个处理者的引用,因此,如果需要,可以访问下一个处理者。 - 客户端:事件的发起者。
二、实现举例
以对字符串进行简单过滤处理为例:
- 定义一个接口,包含一个处理方法
public interface Filter {
String doFilter(String str);
}
- 不同的处理过滤器,实现接口重写方法
如:
// FaceFilter.java
public class FaceFilter implements Filter {
@Override
public String doFilter(String str) {
return str.replace(":)", "^V^");
}
}
// SesitiveFilter.java
public class SesitiveFilter implements Filter {
@Override
public String doFilter(String str) {
//process the sensitive words
String r = str.replace("敏感", "和谐");
return r;
}
}
以及其他具体的filter。
- FilterChain同样实现Filter,重写doFilter方法。以存放并依次执行各filter的处理
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter {
List<Filter> filters = new ArrayList<Filter>();
public FilterChain addFilter(Filter f) {
this.filters.add(f);
return this;
}
// 各filter依次处理
public String doFilter(String str) {
String r = str;
for(Filter f: filters) {
r = f.doFilter(r);
}
return r;
}
}
- 消息处理类
public class MsgProcessor {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
/**
* 处理方式0:过滤项全部放在一起处理 X
* 不利于灵活的修改和扩展。
* @return
*/
public String process_0() {
// 先处理带尖括号的信息
String r=msg.replace('<', '[')
.replace('>', ']');
// 处理敏感字眼
r=r.replace("敏感", "和谐");
return r;
}
/** 处理方式1 BEGIN**/
// 配置需要的filter
Filter[] filters = { new SesitiveFilter(), new FaceFilter()};
// 按照预先配置的filter一次处理。
public String process_1(){
String r=msg;
// 处理脚本语句和敏感字眼
for(Filter f:filters){
r=f.doFilter(r);
}
return r;
}
/** 处理方式1 END**/
/** 处理方式2 BEGIN**/
// 使用一个处理链
FilterChain fc;
public FilterChain getFc() {
return fc;
}
public void setFc(FilterChain fc) {
this.fc = fc;
}
public String process() {
return fc.doFilter(msg);
}
/** 处理方式2 END**/
}
- 测试
public class Main {
public static void main(String[] args) {
String msg = "<script>, 敏感信息:)";
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);
FilterChain fc = new FilterChain();
fc.addFilter(new FaceFilter())
.addFilter(new SesitiveFilter());
mp.setFc(fc);
String result = mp.process();
System.out.println(result);
}
}
输出结果:
<script>, 和谐信息^V^
三、典型应用
Web
Servlet 和 Filter
以声明的方式插入到Http请求响应的处理过程,用于拦截请求和响应.
Netty
ChannelPieline 和 ChannelHandler
Channel的数据管道抽象为ChannelPieline,消息在ChannelPieline中流动传递,ChannelPieline持有I/O事件拦截器ChannelHandler的链表.可以通过新增和删除Handler来实现不同的业务逻辑定制.
public void initServer() {
unStop();
logger.info("Attempt to init " + (ConfigUtil.IS_INNER_PROXY ? "UDP" : "TCP") + " Server!" + logTunnelInfo);
server = ConfigUtil.IS_INNER_PROXY ? new ProxyUdpServer() : new ProxyTcpServer();
server.setSelfPort(Integer.parseInt(channelConfig.getChannelTheEndPort()));
server.setChannelInitializer(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline channelPipeline = ch.pipeline();
channelPipeline.addLast(ConfigUtil.IS_INNER_PROXY ? new UdpChecker(channelConfig, corral) : new TcpChecker(channelConfig, corral));
channelPipeline.addLast(ConfigUtil.IS_INNER_PROXY ? new DatagramPacketDecoder() : new MsgDecoder());
channelPipeline.addLast(new ProxyServerChannelHandler());
}
});
server.start();
try {
server.bind().addListener(future -> applicationListenerManager.publishEvent(ConfigUtil.IS_INNER_PROXY ? InnerProxyToOutterProxy.class : OutterProxyToInnerProxy.class, new EnhancedParams(channelConfig, corral)));
} catch (Exception e) {
logger.error("Proxy " + (ConfigUtil.IS_INNER_PROXY ? "UDP" : "TCP") + " Server start failed!" + logTunnelInfo, e);
}
}
四、简单实践
场景描述: 某一键配置场景,需要将当前系统业务参数配置导出到Excel。
业务参数按照按不同类别写入不同的sheet页。可以把文件流作为输入,写不同sheet页作为多个Handler,后续新增配置参数或sheet页都可以很方便的修改。
- 接口
import org.apache.poi.ss.usermodel.Workbook;
import com.tdtech.eplatform.gatekeeper.util.BusinessException;
public interface ExcelWriteHandler {
Workbook doWriteExcel(Workbook wb) throws BusinessException;
}
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.ss.usermodel.Workbook;
import com.tdtech.eplatform.gatekeeper.util.BusinessException;
public class ExcelWriteHandlerChain implements ExcelWriteHandler{
List<ExcelWriteHandler> excelWriters = new ArrayList<ExcelWriteHandler>();
public ExcelWriteHandlerChain addHandler(ExcelWriteHandler eWriteHandler) {
this.excelWriters.add(eWriteHandler);
return this;
}
@Override
public Workbook doWriteExcel(Workbook workbook) throws BusinessException {
Workbook wb = workbook;
for(ExcelWriteHandler excelWriter: excelWriters) {
wb = excelWriter.doWriteExcel(wb);
}
return wb;
}
}
- 具体的某一个处理类
import java.util.List;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.tdtech.eplatform.gatekeeper.common.IGetAndSetConfig;
import com.tdtech.eplatform.gatekeeper.config.entity.ChannelConfig;
import com.tdtech.eplatform.gatekeeper.constant.IConfigFileSet;
import com.tdtech.eplatform.gatekeeper.util.BusinessException;
import com.tdtech.eplatform.gatekeeper.util.FileUtil;
@Service
public class ExcelWriteTunnelHandler implements ExcelWriteHandler{
@Autowired
private IGetAndSetConfig getconfp;
@Override
public Workbook doWriteExcel(Workbook wb) throws BusinessException {
Sheet sheetTunnel = wb.getSheet(IConfigFileSet.sheetNameofTunnnel);
if (sheetTunnel == null) {
throw new BusinessException("1", "写配置文件失败");
}
List<ChannelConfig> allChannelConfig = getconfp.getAllChannelConfig();
CellStyle style = FileUtil.getCellStyle(wb);
int rowNum = 0;
for (ChannelConfig channelConfig : allChannelConfig) {
Row rows = sheetTunnel.createRow(++rowNum);
FileUtil.setCellValue(rows, 0, style, channelConfig.getChannelName());
FileUtil.setCellValue(rows, 1, style, channelConfig.getOppositeEndForwardVirtualIP());
FileUtil.setCellValue(rows, 2, style, channelConfig.getOppositeEndOppositeVirtualIP());
FileUtil.setCellValue(rows, 3, style, String.valueOf(channelConfig.getChannelTheEndPort()));
FileUtil.setCellValue(rows, 4, style, String.valueOf(channelConfig.getChannelToEndPort()));
}
return wb;
}
}
private boolean writeEasyconfigExcel() throws BusinessException {
boolean rst = false;
// 读空模板文件
ExcelReader eReader = new ExcelReader(IConfigFileSet.exportEasyconfBaseFile,IConfigFileSet.sheetNameofListenIPMapping);
if(!eReader.isSheetExist()){
logger.error("sheetNameofGate not exists.");
throw new BusinessException("1", "获取一键配置文件失败。");
}
ExcelWriteHandlerChain handlers = new ExcelWriteHandlerChain();
// 写网闸参数/通道参数/监听参数
handlers.addHandler(eWriteForGate)
.addHandler(eWriteForTunnel)
.addHandler(eWriteForListener);
Workbook wb = handlers.doWriteExcel(eReader.getWorkBook());
rst = FileUtil.writeExcel(wb,IConfigFileSet.exportEasyconfFilePath);
return rst;
}
五、体会
所谓责任,就是一个Handler做某一种处理。各个Handler之间低耦合,易于扩展。
所谓链,就是各个Handler依次对请求做相应处理的一种组织形式。
请求者与处理者是解耦的。请求者只需要发出请求,链条中的各个具体处理者负责处理自己的那部分。
简言之就是:分离职责,动态组合。