news 2026/8/26 15:37:44

自己动手写一个tomcat之6log以及pipeline和valve

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自己动手写一个tomcat之6log以及pipeline和valve

写在前面

源码 。
本文看下log日志如何设计,并开发valve和pipeline内容,最终将log应用到valve和pipeline中。

1:log

毫无疑问,先定义日志接口:

/** * 日志底层接口 */publicinterfaceLogger{publicstaticfinalintFATAL=Integer.MIN_VALUE;publicstaticfinalintERROR=1;publicstaticfinalintWARNING=2;publicstaticfinalintINFORMATION=3;publicstaticfinalintDEBUG=4;publicStringgetInfo();publicintgetVerbosity();publicvoidsetVerbosity(intverbosity);publicvoidlog(Stringmessage);publicvoidlog(Exceptionexception,Stringmsg);publicvoidlog(Stringmessage,Throwablethrowable);publicvoidlog(Stringmessage,intverbosity);publicvoidlog(Stringmessage,Throwablethrowable,intverbosity);}

定义base实现类:

packagemonitomcat.server.logger;// ...publicabstractclassLoggerBaseimplementsLogger{protectedintdebug=0;protectedstaticfinalStringinfo="com.minit.logger.LoggerBase/1.0";protectedintverbosity=ERROR;// ...publicabstractvoidlog(Stringmsg);/** * 核心方法 首先打印异常堆栈,之后调用抽象方法public abstract void log(String msg);执行子类具体动作 * @param msg * @param throwable */publicvoidlog(Stringmsg,Throwablethrowable){CharArrayWriterbuf=newCharArrayWriter();PrintWriterwriter=newPrintWriter(buf);writer.println(msg);throwable.printStackTrace(writer);ThrowablerootCause=null;if(throwableinstanceofServletException)rootCause=((ServletException)throwable).getRootCause();if(rootCause!=null){writer.println("----- Root Cause -----");rootCause.printStackTrace(writer);}log(buf.toString());}// ...}

其中public abstract void log(String msg);抽象方法需要在子类中给出具体的实现,如下给出的实现类们:

分别是标准输出,标准错误输出,文件输出。
我们来修改bootstrap,修改context容器的日志为文件输出:

packagemonitomcat.server.startup;// .../** * 总的服务器对象,负责管理类工作的对象(类似于公司的管理层,负责管理,但不负责具体干活,但管理本身不也是一种职责嘛!所以也可以用单一职责来进行说明) */publicclassBootstrap{// ...publicstaticvoidmain(String[]args){// ...StandardContextservletContainer=newStandardContext();// 设置日志servletContainer.setLogger(newFileLogger());// ...}}

写日志输出到文件:

packagemonitomcat.server.connector.http;// ...publicclassHttpConnectorimplementsRunnable{// ...publicvoidrun(){// ...while(true){Socketsocket=null;try{// ...log("connector receive new request, assign to processor");// ...}catch(Exceptione){e.printStackTrace();}}}publicvoidstart(){Threadthread=newThread(this);// ...log("httpConnector.starting "+threadName);}// ...}

测试一下:

2:pipeline和valve

本部分内容要达到的目的是通过责任链的方式来拦截一层一层容器的执行,比如可以添加日志,权限校验等。
其中valve是执行的单元,接口如下:

packagemonitomcat.server;importjava.io.IOException;importjavax.servlet.ServletException;/** * 容器执行逻辑单元 */publicinterfaceValve{publicStringgetInfo();publicContainergetContainer();publicvoidsetContainer(Containercontainer);publicvoidinvoke(Requestrequest,Responseresponse,ValveContextcontext)throwsIOException,ServletException;}

base实现类:

packagemonitomcat.server.valves;importmonitomcat.server.Container;importmonitomcat.server.Valve;publicabstractclassValveBaseimplementsValve{protectedContainercontainer=null;protectedintdebug=0;protectedstaticStringinfo="com.minit.valves.ValveBase/0.1";publicContainergetContainer(){return(container);}publicvoidsetContainer(Containercontainer){this.container=container;}publicintgetDebug(){return(this.debug);}publicvoidsetDebug(intdebug){this.debug=debug;}publicStringgetInfo(){return(info);}}

为了维护valve的执行状态,定义执行的上下文接口:

/** * valve执行上下文,执行具体的链式调用动作 */publicinterfaceValveContext{publicStringgetInfo();publicvoidinvokeNext(Requestrequest,Responseresponse)throwsIOException,ServletException;}

pipeline接口:

publicinterfacePipeline{publicValvegetBasic();publicvoidsetBasic(Valvevalve);publicvoidaddValve(Valvevalve);publicValve[]getValves();publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException;publicvoidremoveValve(Valvevalve);}

pipeline实现类:

packagemonitomcat.server.core;// ...publicclassStandardPipelineimplementsPipeline{// ...publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException{System.out.println("StandardPipeline invoke()");// Invoke the first Valve in this pipeline for this request(newStandardPipelineValveContext()).invokeNext(request,response);}// ...protectedclassStandardPipelineValveContextimplementsValveContext{protectedintstage=0;publicStringgetInfo(){returninfo;}publicvoidinvokeNext(Requestrequest,Responseresponse)throwsIOException,ServletException{System.out.println("StandardPipelineValveContext invokeNext()");intsubscript=stage;stage=stage+1;// Invoke the requested Valve for the current request threadif(subscript<valves.length){valves[subscript].invoke(request,response,this);}elseif((subscript==valves.length)&&(basic!=null)){basic.invoke(request,response,this);}else{thrownewServletException("standardPipeline.noValve");}}}}

这里StandardPipelineValveContext是ValveContext的实现类,是一个内部类,执行具体的链式调用动作,所以pipeline这里更像是一个管理者角色,并不是真正干活的,但要维护一些信息,比如valves数组。
这里有一点需要注意,必须要保证容器能够一层一层的调用,所以这里定义basic valve的概念,负责调用下一层container的pipeline,定义context的basic valve:

packagemonitomcat.server.core;// ...finalclassStandardContextValveextendsValveBase{// ...publicvoidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)throwsIOException,ServletException{// ...try{System.out.println("Call service()");servletWrapper.invoke(request,response);}// ...}}

负责调用wrapper container的pipeline。
定义wrapper basic valve:

packagemonitomcat.server.core;// ...publicclassStandardWrapperValveextendsValveBase{@Overridepublicvoidinvoke(Requestrequest,Responseresponse,ValveContextcontext)throwsIOException,ServletException{// ...if(instance!=null){instance.service(requestFacade,responseFacade);}}}

负责调用最终的servlet,就执行到头了。接着我们只需要为每一层的容器定义其pipeline,然后设置basic valve以及其他valve就可以了,这里pipeline的创建,以及添加valve的动作因为是公共的,所以我们放在base容器中:

packagemonitomcat.server.core;// ...publicabstractclassContainerBaseimplementsContainer,Pipeline{// ...protectedPipelinepipeline=newStandardPipeline(this);publicPipelinegetPipeline(){return(this.pipeline);}publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException{// System.out.println("ContainerBase invoke()");pipeline.invoke(request,response);}publicsynchronizedvoidaddValve(Valvevalve){pipeline.addValve(valve);}publicValvegetBasic(){return(pipeline.getBasic());}publicValve[]getValves(){return(pipeline.getValves());}publicsynchronizedvoidremoveValve(Valvevalve){pipeline.removeValve(valve);}publicvoidsetBasic(Valvevalve){pipeline.setBasic(valve);}}

发起执行的动作在ServletProcessor中,如下:

publicclassServletProcessor{privateHttpConnectorconnector;publicServletProcessor(HttpConnectorconnector){this.connector=connector;}// public void process(HttpRequestImpl request, HttpResponseImpl response) throws IOException, ServletException {publicvoidprocess(Requestrequest,Responseresponse)throwsIOException,ServletException{this.connector.getContainer().invoke(request,response);}}

这样程序就按照如下图的方式执行了:

写在后面

参考文章列表

手把手带你写一个 MiniTomcat 。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 15:33:58

华为MetaERP # EBS 预算主数据 → Fusion BC 预算主数据完整映射实施方法论适用于:迁移蓝图、ETL 规范、主数据梳理工作包、业务调研问卷、数据转换方案整体思路:**先对象对

EBS 预算主数据 → Fusion BC 预算主数据完整映射实施方法论适用于&#xff1a;迁移蓝图、ETL 规范、主数据梳理工作包、业务调研问卷、数据转换方案 整体思路&#xff1a;先对象对齐 → 业务规则对齐 → 字段级映射 → 清洗转换规则定义 → 加载顺序 → 校验方案 → 迁移风险规…

作者头像 李华
网站建设 2026/8/26 15:28:52

PDF批量处理完整指南:用PDF补丁丁把成百上千个文件一口气搞定

PDF批量处理完整指南&#xff1a;用PDF补丁丁把成百上千个文件一口气搞定 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱&#xff0c;可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档&#xff0c;探查文档结构&#xff0c;提取图片、转成图片等等 项目地址: htt…

作者头像 李华
网站建设 2026/8/26 15:26:03

BioJava蛋白质结构叠加:CE、FATCAT与TM-align三种算法完整指南

BioJava蛋白质结构叠加&#xff1a;CE、FATCAT与TM-align三种算法完整指南 【免费下载链接】biojava :book::microscope::coffee: BioJava is an open-source project dedicated to providing a Java library for processing biological data. 项目地址: https://gitcode.com…

作者头像 李华