折腾Java设计模式之责任链模式
Posted 大萌小路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了折腾Java设计模式之责任链模式相关的知识,希望对你有一定的参考价值。
责任链模式
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
简介
意图 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
何时使用 在处理消息的时候以过滤很多道。
如何解决 拦截的类都实现统一接口。
关键代码 Handler 里面聚合它自己,在 handleRequest 里判断是否合适,如果没达到条件则向下传递。
纯责任链与不纯责任链
纯:纯责任链中的节点只有两种行为,一处理责任,二将责任传递到下一个节点。不允许出现某一个节点处理部分或全部责任后又将责任向下传递的情况。
不纯:允许某个请求被一个节点处理部分责任后再向下传递,或者处理完后其后续节点可以继续处理该责任,而且一个责任可以最终不被任何节点所处理。
主要角色
Handler(抽象处理者): 定义一个处理请求的接口,提供对后续处理者的引用
ConcreteHandler(具体处理者): 抽象处理者的子类,处理用户请求,可选将请求处理掉还是传给下家;在具体处理者中可以访问链中下一个对象,以便请求的转发
应用实例
1、红楼梦中的 "击鼓传花"。
2、JS 中的事件冒泡。
3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
优点
1、降低耦合度。它将请求的发送者和接收者解耦。
2、简化了对象。使得对象不需要知道链的结构。
3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。
缺点
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。
使用场景
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
Github 项目描述
跳转到我的责任链设计模式源码
1. 出行方式
travel 包里主要对出行方式的责任链模式。跟进用户身上的钱,在优先级如飞机 -> 火车 -> 大巴的顺序下选择对应的出行模式。
public class Application {
public static void main(String[] args) {
Handler planeHandler = new PlaneHandler();
Handler trainHandler = new TrainHandler();
Handler busHandler = new BusHandler();
planeHandler.setNext(trainHandler);
trainHandler.setNext(busHandler);
planeHandler.handleRequest("老王", 40d);
planeHandler.handleRequest("张三", 140d);
planeHandler.handleRequest("李四", 240d);
planeHandler.handleRequest("吴老五", 340d);
}
}
抽象处理
@Data
public abstract class Handler {
/**
* 下一个链节点
*/
protected Handler next;
public abstract void handleRequest(String name, Double wallet);
}
具体的处理者(飞机、火车、大巴)
@Slf4j
public class PlaneHandler extends Handler {
private double price = 280d;
@Override
public void handleRequest(String name, Double wallet) {
if (price <= wallet) {
log.info("{}身上的钱可以坐飞机。", name);
return;
}
if (Objects.nonNull(next)) {
next.handleRequest(name, wallet);
return;
}
log.info("{}钱不够,只能徒步啦", name);
}
}
@Slf4j
public class TrainHandler extends Handler {
private double price = 149.99d;
@Override
public void handleRequest(String name, Double wallet) {
if (price <= wallet) {
log.info("{}身上的钱只能坐火车。", name);
return;
}
if (Objects.nonNull(next)) {
next.handleRequest(name, wallet);
return;
}
log.info("{}钱不够,只能徒步啦", name);
}
}
@Slf4j
public class BusHandler extends Handler {
private double price = 59.99d;
@Override
public void handleRequest(String name, Double wallet) {
if (price <= wallet) {
log.info("{}身上的钱只能坐大巴。", name);
return;
}
if (Objects.nonNull(next)) {
next.handleRequest(name, wallet);
return;
}
log.info("{}钱不够,只能徒步啦", name);
}
}
2. 出行方式 2,参考 Filter 链的写法
travel2 包是对 travel 包的重新写法。
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
Handler planeHandler = new PlaneHandler();
Handler trainHandler = new TrainHandler();
Handler busHandler = new BusHandler();
chain.addHandler(planeHandler);
chain.addHandler(trainHandler);
chain.addHandler(busHandler);
chain.handle("老王", 40d);
chain.handle("张三", 140d);
chain.handle("李四", 240d);
chain.handle("吴老五", 340d);
}
}
抽象处理者
public interface Handler {
void handleRequest(String name, Double wallet, HandlerChain chain);
}
具体处理者(飞机、火车、大巴)
@Slf4j
public class PlaneHandler implements Handler {
private double price = 280d;
@Override
public void handleRequest(String name, Double wallet, HandlerChain chain) {
if (price <= wallet) {
log.info("{}身上的钱可以坐飞机。", name);
chain.reuse();
return;
}
chain.handle(name, wallet);
}
}
@Slf4j
public class TrainHandler implements Handler {
private double price = 149.99d;
@Override
public void handleRequest(String name, Double wallet, HandlerChain chain) {
if (price <= wallet) {
log.info("{}身上的钱只能坐火车。", name);
chain.reuse();
return;
}
chain.handle(name, wallet);
}
}
@Slf4j
public class BusHandler implements Handler {
private double price = 59.99d;
@Override
public void handleRequest(String name, Double wallet, HandlerChain chain) {
if (price <= wallet) {
log.info("{}身上的钱只能坐大巴。", name);
chain.reuse();
return;
}
chain.handle(name, wallet);
}
}
责任链管理者
@Slf4j
public class HandlerChain {
private List<Handler> handlerList = new ArrayList<>();
/**
* 维护当前链上位置
*/
private int pos;
/**
* 链的长度
*/
private int handlerLength;
public void addHandler(Handler handler) {
handlerList.add(handler);
handlerLength = handlerList.size();
}
public void handle(String name, double wallet) {
if (CollectionUtils.isEmpty(handlerList)) {
log.error("有钱,但没提供服务,{}也估计就只能步行了。", name);
return;
}
if (pos >= handlerLength) {
log.error("身上钱不够,{}也估计就只能步行了。", name);
reuse();
return;
}
Handler handler = handlerList.get(pos++);
if (Objects.isNull(handler)) {
log.error("假服务,{}也估计就只能步行了。", name);
reuse();
return;
}
handler.handleRequest(name, wallet, this);
}
/**
* 链重新使用
*/
public void reuse() {
pos = 0;
}
}
学习 Web 项目的 Filter
待补充...
补充补充遗留的 Filter 过滤器中的责任链处理。
本次主要是对 Tomcat 中的 Filter 处理简单的梳理,如有不正确的地方,还望指出来,大家互勉,共进。
老项目大家可以在 web.xml 中配置 filter,现使用 Springboot 后,也有两种配置 filter 方式,通过创建 FilterRegistrationBean 的方式和通过注解 @WebFilter+@ServletComponentScan 的方式。
三个主要的角色
FIlter,不多介绍了。
FilterChain servlet 容器提供的开发调用链的过滤请求的资源。通过调用下一个 filter 实现过滤,在整体链上。
FilterConfig filter 的配置器,在 servlet 容器在 Filter 初始化的时候传递信息。
具体的 filter,主要说说 Spring 中的两个抽象 Filter,GenericFilterBean 和 OncePerRequestFilter。
前者主要是做 init 和 destroy 的操作,重点还是 init 方法,destroy 只是空实现而已。
后者主要是做真正的 doFilter 操作,也是我们在 Spring 中创建 Filter 通常继承的。
而 ApplicationFilterChain 就算 Tomcat 中的 FilterChain 实现。
//The int which is used to maintain the current position in the filter chain.
private int pos = 0;
//The int which gives the current number of filters in the chain.
private int n = 0;
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
//安全相关的,暂不关注
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
//真正的doFilter
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
//pos 调用链中当前连接点所在的位置
//n 调用链总节点长度
// Call the next filter if there is one
if (pos < n) {
//对节点进行自增 pos++
ApplicationFilterConfig filterConfig = filters[pos++];
try {
//当前节点小于总长度后,从filter配置类中取出filter
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
//真正的filter
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
//到了调用链结尾处,就真正调用servlet实例的servlet.service(request, response);
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
//重复使用filter调用链,pos重设为0
void reuse() {
pos = 0;
}
重点从 ApplicationFilterChain 中挑出几个重要的方法拿出来分析下 Filter 的调用链,其实还有几处没有具体讲到,ApplicationFilterChain 是合适创建的,Filter 是怎么加入到 ApplicationFilterChain 中的。这涉及到 Tomcat 是怎样加载 Content 的,下次分析 Tomcat 的时候,再来具体分析,它是如何运作的,如何加载 web.xml。
以上是关于折腾Java设计模式之责任链模式的主要内容,如果未能解决你的问题,请参考以下文章