学校的奖学金评选流程
公司的请假系统
来自菜鸟的感慨
哇塞,这个奖学金评选系统好牛逼哦:幸运儿的申请,层层上报,最终会由校长进行审批
哇塞,这个OA系统好智能啊:不同的请假天数,可以到对应审批人的手里
要是我去实现这样一个系统,该怎么做呢?
请假系统倒是好说,直接根据请假天数,来个if-else
就可以指定审批人了
但是,奖学金评选系统呢?难道写成这样?
// 辅导员审批
if (isTrue) {
instructor.approve();
// 系主任审批
if (isOk) {
departmentHead.approve();
// 院长审批
if (isGoodEnough) {
dean.approve();
// 校长审批
if (isGood) {
headmaster.approve();
} else {
System.out.println("很遗憾,未通过全校公示");
}
} else {
System.out.println("很遗憾,未在学院评选中脱颖而出");
}
} else {
System.out.println("该生表现欠佳,不予通过");
}
} else {
System.out.println("信息填写有误,请重新认真填写");
}
我的天,这是什么代码啊,简直像坨屎
责任链模式的定义
敲黑板,划重点
重点1:多个请求处理者
重点2:将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链
重点3:请求沿着这条链传递,直到有对象处理它为止
请求沿着这条链传递,直到有handler处理它或者达到末尾为止
自己的理解
if-else
去实现从UML图中,可以提炼出三个关键角色
总结一下责任链模式
按照博客开头的描述,实现一个请假系统
第一步:创建抽象处理者
public abstract class Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public Handler getNext() {
return next;
}
public abstract void handleRequest(String name, int days);
}
第二步:创建组长、总监、部长三个具体处理者,实现具体的处理逻辑
public class PMHandler extends Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 3) {
System.out.println(name + ",组长已经同意您的请假审批!");
} else {
if (getNext() != null) {
getNext().handleRequest(name, days);
} else {
System.out.println("请假天数太多,申请被驳回!");
}
}
}
}
public class DirectorHandler extends Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 7) {
System.out.println(name + ",中心总监已经同意您的请假审批");
} else {
if (getNext() != null) {
getNext().handleRequest(name, days);
} else {
System.out.println("请假天数太多,申请被驳回!");
}
}
}
}
public class MinisterHandler extends Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 15) {
System.out.println(name + ",部长已经同意您的请假审批");
} else {
if (getNext() != null) {
getNext().handleRequest(name, days);
} else {
System.out.println("请假天数太多,申请被驳回!");
}
}
}
}
第三步:创建Clinet类,在类中创建并使用责任链(向责任链传递请求)
public class OASystem {
public static void main(String[] args) {
// 创建具体处理者
Handler pm = new PMHandler();
Handler director = new DirectorHandler();
Handler minister = new MinisterHandler();
// 构建责任链
pm.setNext(director);
director.setNext(minister);
// 使用责任链
pm.handleRequest("张三", 5);
}
}
需求一:
需求二:
现有代码存在的问题:
灵光一现
在使用一些库函数的时候,经常看到类似的代码:
Escapers.builder()
.addEscape('"', """)
.addEscape('\'', "'")
.addEscape('&', "&")
.addEscape('<', "<")
.addEscape('>', ">")
.build();
这样的代码,增加、删除或者调整属性的顺序,都非常方便
较为成熟的责任链实现
取消抽象处理者中的next引用
单独的责任链类:
// 抽象处理者
public interface Handler {
public abstract void handleRequest(String name, int days);
}
// 具体处理者
public class PMHandler implements Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 3) {
System.out.println(name +",组长已经同意您的请假审批!");
}
}
}
public class DirectorHandler implements Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 7) {
System.out.println(name + ",中心总监已经同意您的请假审批");
}
}
}
public class MinisterHandler implements Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 15) {
System.out.println(name + ",部长已经同意您的请假审批");
}
}
}
// 责任链类
public class HandlerChain implements Handler {
private List<Handler> handlerList;
public HandlerChain() {
this.handlerList = new ArrayList<>();
}
public HandlerChain addHandler(Handler handler) {
handlerList.add(handler);
return this;
}
@Override
public void handleRequest(String name, int days) {
for (Handler handler : handlerList) {
handler.handleRequest(name, days);
}
}
}
// 客户类
public class OASystem {
public static void main(String[] args) {
// 创建具体处理者
Handler pm = new PMHandler();
Handler director = new DirectorHandler();
Handler minister = new MinisterHandler();
// 构建责任链
HandlerChain chain = new HandlerChain()
.addHandler(pm)
.addHandler(director)
.addHandler(minister);
// 使用责任链
chain.handleRequest("王二", 10);
}
}
责任链模式的优点如下:
if···else
语句。自己对增强了给对象指派职责的灵活性
的理解
list
记录责任链
责任链模式具有如下缺点:
基于tomcat源码进行学习
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-coreartifactId>
<version>8.0.41version>
dependency>
dependencies>
说到Filter(过滤器),笔者最大的印象:
RemoteHostFilter
HttpHeaderSecurityFilter
Filter在请求进入容器后、未进入servlet之前,对请求进行预处理;同时,Filter可以在servlet返回响应后、未返回给客户端之前,对响应进行处理
tomcat的源码中,定义了Filter接口
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public void destroy();
}
init()
方法:servlet容器在实例化Filter对象后、调用Filter对象的doFilter方法前,调用init()方法(仅一次)以表明该filter可以提供服务
doFilter()
方法:
pos
的形式调用下一个filter-->
filter1.doFilter()执行处理逻辑、调用chain.doFilter() -->
chain.doFilter()调用filter2 -->
filter2.doFilter()执行处理逻辑、调用chain.doFilter() -->
…destroy()
方法:
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
filters
:ApplicationFilterConfig类型的数组,记录FilterChain上的filter;初始化长度为0,使用时按照INCREMENT = 10
的增量进行扩容pos
:保存FilterChain当前位置的int变量,实际就是filters数组的索引,可以指向某个filtern
:FilterChain的实际大小,也就是filters数组中存储的filter的数量;filters数组每次按照10进行扩容,这是filters数组的容量,并非filter的数量servlet
:FilterChain上的filter访问到链尾时,将执行servlet实例的service()方法;通过servlet的service()方法,实现对用户请求的处理private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
private int pos = 0;
private int n = 0;
private Servlet servlet = null;
ApplicationFilterChain相关方法的简单介绍
ApplicationFilterFactory
的createFilterChain()
是一个工厂方法,负责完成ApplicationFilterChain的创建,包括设置servlet、向链中添加filter等
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
// 为FilterChain添加filter的关键代码
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName());
// ...,其他代码省略
filterChain.addFilter(filterConfig);
}
ApplicationFilterChain的addFilter()
方法,被createFilterChain()方法调用,涉及filter是否重复添加的校验、filters数组的扩容、filter的添加
void addFilter(ApplicationFilterConfig filterConfig) {}
doFilter()方法(重头戏)
ApplicationFilterChain的doFilter()方法源码如下,从代码不难看出:doFilter()方法最终将调用internalDoFilter()方法
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) {
// 省略异常的处理
}
} else {
internalDoFilter(request,response);
}
}
internalDoFilter()方法:
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
filter = filterConfig.getFilter();
// ... 省略代码
if( Globals.IS_SECURITY_ENABLED ) {
// ... 省略代码
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
}
// ... 省略catch
return; // 巧妙的return处理,后续会提到原因
}
try {
// ... 省略代码
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
if( Globals.IS_SECURITY_ENABLED ) {
// ... 省略代码
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);
} else {
servlet.service(request, response);
}
} else {
servlet.service(request, response);
}
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);
}
// ... 省略catch和finally
}
笔者在看到这里时,整个人是懵逼的:pos的更新确实能沿着FilterChain调用其上的filter,问题是开头是if (pos < n)
并非while(pos < n)
,这咋能实现循环呢?别骗我读书少哦
耐下性子,点开filter的doFilter()方法,发现奥妙就在这里:filter的doFilter()方法,都会在合适的时机调用传入的chain的doFilter()方法
例如,FailedRequestFilter
的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!isGoodRequest(request)) {
FailReason reason = (FailReason) request.getAttribute(Globals.PARAMETER_PARSE_FAILED_REASON_ATTR);
int status;
switch (reason) {
// 为各种错误类型设置status
}
// 执行失败,直接返回携带错误信息的响应
((HttpServletResponse) response).sendError(status);
return;
}
// 执行成功,继续访问FilterChain上的下一个filter
chain.doFilter(request, response);
}
RemoteHostFilter
的doFilter()方法:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
process(request.getRemoteHost(), request, response, chain);
}
protected void process(String property, ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isAllowed(property)) { // 请求被允许,继续访问FilterChain上的下一个Filter
chain.doFilter(request, response);
} else {
// 请求不被允许,则返回携带错误信息的响应
}
}
也就是说,FilterChain的doFilter()方法和filter的doFilter()方法,通过相互调用形成了一条处理请求/响应对的责任链
FilterChain的doFilter()方法中的return
语句
pos < n
条件不满足,则if
语句之外的diamante,即调用servlet.service()方法在工作的过程中,笔者也看到过责任链模式的源码。这里不再一一赘述,只做简要记录
ServletHandler.Chain也使用了责任链模式(貌似servlet的请求/响应对的处理都喜欢使用责任链模式)
<dependency>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-servletartifactId>
<version>10.0.7version>
dependency>
关于责任链的实现
ApplicationFilterChain
的实现参考文档