一、概述
責任鏈模式是行為模式的一種,它將需要觸發的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依次對請求做相應處理的一種組織形式。
請求者與處理者是解耦的。請求者只需要發出請求,鏈條中的各個具體處理者負責處理自己的那部分。
簡言之就是:分離職責,動態組合。