国内响应式网站欣赏,抖音seo关键词优化,桂林论坛天涯社区,网上做任务佣金高的网站文章目录 一、前言二、源码解析GenericXMLConfiguratorlogback.xml解析通过SaxEvent构建节点model解析model节点DefaultProcessor解析model 三、总结 一、前言
上一篇介绍了logback模块解析logback.mxl文件的入口, 我们可以手动指定logback.xml文件的位置, 也可以使用其它的名… 文章目录 一、前言二、源码解析GenericXMLConfiguratorlogback.xml解析通过SaxEvent构建节点model解析model节点DefaultProcessor解析model 三、总结 一、前言
上一篇介绍了logback模块解析logback.mxl文件的入口, 我们可以手动指定logback.xml文件的位置, 也可以使用其它的名字, 本节我们继续讨论logback是如何解析logback.xml文件的。
二、源码解析
拿出我们上一节的继承图
其中ContextAware和ContextAwareBase是有关日志上下文LoggerContext注入与打印启动日志的, 我们不介绍。
看到这个Aware结尾的有的同学可能会感觉到很有亲切感, 没错, spring中有很多这种Aware结尾的类, 例如ApplicationContextAware、EnvironmentAware等xxxAware, 它们都会提供一个setXxx的方法, 用来在框架启动的时候注入一个xxx对象。
GenericXMLConfigurator
public abstract class GenericXMLConfigurator extends ContextAwareBase {/*** SaxEvent解析器*/protected SaxEventInterpreter saxEventInterpreter;/*** model解析器的上下文*/protected ModelInterpretationContext modelInterpretationContext;/*** 配置文件节点路径和action的映射* 默认是SimpleRuleStore*/private RuleStore ruleStore;public final void doConfigure(URL url) throws JoranException {InputStream in null;try {// 1.给Context设置ConfigurationWatchList; 用于配置文件热更新informContextOfURLUsedForConfiguration(getContext(), url);URLConnection urlConnection url.openConnection();// per http://jira.qos.ch/browse/LOGBACK-117 LBCORE-105// per http://jira.qos.ch/browse/LOGBACK-163 LBCORE-127urlConnection.setUseCaches(false);in urlConnection.getInputStream();// 2.解析配置; url.toExternalForm():返回url表示的绝对路径的字符串形式doConfigure(in, url.toExternalForm());} catch (IOException ioe) {String errMsg Could not open URL [ url ].;// 打印解析异常日志addError(errMsg, ioe);throw new JoranException(errMsg, ioe);} finally {if (in ! null) {try {// 3.关闭流in.close();} catch (IOException ioe) {String errMsg Could not close input stream;addError(errMsg, ioe);throw new JoranException(errMsg, ioe);}}}}
}这个方法就是开始解析logback.xml文件真正的入口了public final void doConfigure(URL url) throws JoranException
informContextOfURLUsedForConfiguration方法用来设置动态热加载的配置文件, 也就是我们configuration debugtrue scantrue scanPeriod10 second 这里动态刷新的默认文件doConfigure: 进一步解析
doConfigure(final InputSource inputSource)
直接从doConfigure跳过来看这个方法即可
public final void doConfigure(final InputSource inputSource) throws JoranException {// 发布配置开始事件context.fireConfigurationEvent(newConfigurationStartedEvent(this));long threshold System.currentTimeMillis();// 1.解析日志配置文件; 例如logback.xmlSaxEventRecorder recorder populateSaxEventRecorder(inputSource);// 获取解析节点的结果; 每个节点都会解析成StartEvent, BodyEvent, EndEventListSaxEvent saxEvents recorder.getSaxEventList();if (saxEvents.isEmpty()) {addWarn(Empty sax event list);return;}// 2.根据xml节点的解析生成对应的model对象, top默认是configuration的modelModel top buildModelFromSaxEventList(recorder.getSaxEventList());if (top null) {addError(ErrorCodes.EMPTY_MODEL_STACK);return;}// 3.语法检查sanityCheck(top);// 4.解析model节点(核心)processModel(top);// no exceptions at this levelStatusUtil statusUtil new StatusUtil(context);// 5.发布配置解析结束事件if (statusUtil.noXMLParsingErrorsOccurred(threshold)) {// xml解析无异常addInfo(Registering current configuration as safe fallback point);registerSafeConfiguration(top);context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this));} else {// xml解析发生异常context.fireConfigurationEvent(newConfigurationEndedWithXMLParsingErrorsEvent(this));}}方法小结
这里就是解析logback.xml的整个流程了, 编排了5个点
populateSaxEventRecorder方法用来解析文件, 然后返回解析对象根据解析logback.xml的结果生成对应的model检查语法(不介绍)解析model节点(核心)发布解析完成事件(成功/失败)
logback.xml解析
populateSaxEventRecorder
public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException {SaxEventRecorder recorder new SaxEventRecorder(context);// sax解析配置文件, 每一个节点都会解析成SaxEvent, 分为StartEvent, BodyEvent, EndEventrecorder.recordEvents(inputSource);return recorder;
}SaxEventRecorder
public class SaxEventRecorder extends DefaultHandler implements ContextAware {// 节点路径final ElementPath elementPath;// 解析节点得到的结果对象ListSaxEvent saxEventList new ArrayListSaxEvent();public void recordEvents(InputSource inputSource) throws JoranException {// 创建sax解析器SAXParser saxParser buildSaxParser();try {// sax解析; 当前类SaxEventRecorder也是个DefaultHandlersaxParser.parse(inputSource, this);return;} catch (xxxException ie) {// ...异常处理}throw new IllegalStateException(This point can never be reached);}// 解析节点的起始标签public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {// ...}// 解析标签内容部分public void characters(char[] ch, int start, int length) {// ...}// 解析到标签结尾部分时触发public void endElement(String namespaceURI, String localName, String qName) {// ... }
}方法小结
SaxEventRecorder对象用来封装解析logback.xml的逻辑, 同时它也是一个DefaultHandler对象, 负责处理每个节点的具体解析逻辑使用sax解析logback.xml文件每个节点解析结果存放在实例变量saxEventList中
关于常见的xml解析框架的对比如下
特性DOM4JSAXJSOUP解析方式基于树模型加载整个文档到内存基于事件驱动逐行解析类似 DOM 树模型专注于 HTML/XML性能性能较高但文件过大时内存消耗明显性能最高适合大文件解析性能适中适合中小型 XML 或 HTML 文档内存占用较高依赖于内存加载整个文档最低仅在解析时占用较少内存较高但通常适合处理网页等较小文件功能支持强大的 XPath 支持支持修改和生成 XML只支持读取不能修改文档支持解析和修改文档HTML/XML 均适用易用性较高API 友好较低需要手动处理事件逻辑非常高简洁的 API类似 jQuery 操作修改能力支持动态修改不支持修改支持动态修改灵活度高适用场景适合处理中小型 XML 文件适合处理超大文件或流式读取适合处理 HTML/XML 文件尤其是网页解析依赖性需要引入额外依赖如 dom4j jar 包无需额外依赖Java 内置支持需要引入 jsoup jar 包 DOM4J功能全面支持 XPath适合需要频繁修改 XML 的场景但处理超大文件时会占用大量内存。 SAX性能最佳占用内存最少适合超大文件的解析但使用复杂且无法修改文档内容。 JSOUP偏向网页内容解析API 简单易用支持 XML 和 HTML 的解析和修改适合中小型文件处理。
通过SaxEvent构建节点model
buildModelFromSaxEventList
// 通过节点的saxEvent构建节点的model
public Model buildModelFromSaxEventList(ListSaxEvent saxEvents) throws JoranException {// 构建saxEvent解析器buildSaxEventInterpreter(saxEvents);// 解析节点, 解析节点的顺序是StartEvent, BodyEvent, EndEvent, 最终使用playSaxEvents();Model top saxEventInterpreter.getSaxEventInterpretationContext().peekModel();return top;
}// 构建saxEvent解析器, 并添加标签路径对应的action
protected void buildSaxEventInterpreter(ListSaxEvent saxEvents) {// 1.创建ruleStore, 默认是SimpleRuleStoreRuleStore rs getRuleStore();// 2.将路径和对应的解析对象action绑定addElementSelectorAndActionAssociations(rs);// 3.构建saxEvent解析器this.saxEventInterpreter new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents);// 给saxEvent解析器上下文添加contextSaxEventInterpretationContext interpretationContext saxEventInterpreter.getSaxEventInterpretationContext();interpretationContext.setContext(context);// 4.给没有指定action的标签路径添加默认action; 这里是ImplicitModelAction, 用来解析ruleStore规则之外的标签, 给父标签对象添加属性用setImplicitRuleSupplier(saxEventInterpreter);
}方法小结
创建ruleStore, 默认是SimpleRuleStore将路径和对应的解析对象action绑定构建saxEvent解析器给没有指定action的标签路径添加默认action; 这里是ImplicitModelAction, 用来解析ruleStore规则之外的标签, 给父标签对象添加属性用
具体的解析逻辑在saxEvent解析器SaxEventInterpreter中, saxEvent经过action处理之后会得到对应标签节点的model对象
解析model节点
public void processModel(Model model) {// 1.创建ModelInterpretationContext并添加默认对象; 当一个标签没有指定class时, 会从这里尝试获取buildModelInterpretationContext();// configuration节点this.modelInterpretationContext.setTopModel(model);modelInterpretationContext.setConfiguratorHint(this);// 2.创建解析model的核心类DefaultProcessor defaultProcessor new DefaultProcessor(context, this.modelInterpretationContext);// 3.将model与modelHandler和Analyser关联addModelHandlerAssociations(defaultProcessor);// disallow simultaneous configurations of the same contextReentrantLock configurationLock context.getConfigurationLock();try {configurationLock.lock();// 开始解析modeldefaultProcessor.process(model);} finally {configurationLock.unlock();}
}方法小结
构建model解析时的上下文ModelInterpretationContext, 并添加默认标签的class类(如下面的表格)创建解析model的核心类DefaultProcessor将model和对应的处理类(modelHandler)关联起来使用DefaultProcessor解析model
默认标签的属性对应的类
通过buildModelInterpretationContext方法添加在ModelInterpretationContext.DefaultNestedComponentRegistry属性中
类属性默认值AppenderBaselayoutPatternLayout.classUnsynchronizedAppenderBaselayoutPatternLayout.classAppenderBaseencoderPatternLayoutEncoderUnsynchronizedAppenderBaseencoderPatternLayoutEncoderSSLComponentsslSSLConfigurationSSLConfigurationparametersSSLParametersConfigurationSSLConfigurationkeyStoreKeyStoreFactoryBeanSSLConfigurationtrustStoreKeyStoreFactoryBeanSSLConfigurationkeyManagerFactoryKeyManagerFactoryFactoryBeanSSLConfigurationtrustManagerFactoryTrustManagerFactoryFactoryBeanSSLConfigurationsecureRandomSecureRandomFactoryBean
例如下面的配置, 由于FileAppender是UnsynchronizedAppenderBase的子类, 并且encoder节点没有指定class, 而encoder是UnsynchronizedAppenderBase的一个属性, 所以这里取默认值PatternLayoutEncoder
appender nameFILE classch.qos.logback.core.FileAppenderfilelogs/app.log/file!-- encoder不指定class的时候, 默认使用的是PatternLayoutEncoder --encoderpattern%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n/pattern/encoder
/appender我们常用的也就appender标签下的这两个layout和encoder属性
各标签节点路径以及其对应的model和modelHandler如下表格
一般情况下我们看节点路径和action以及handler就行, 这个handler就是用来处理logback.xml中各个标签的类。 这里的节点路径就是我们logback.xml文件中所有可以定义的节点了。
标签节点路径解析路径节点的Actionaction解析之后生成的model解析model的HandlerconfigurationConfigurationActionConfigurationModelConfigurationModelHandlerFullconfiguration/contextNameContextNameActionContextNameModelContextNameModelHandlerconfiguration/contextListenerLoggerContextListenerActionLoggerContextListenerModelLoggerContextListenerModelHandlerconfiguration/insertFromJNDIInsertFromJNDIActionInsertFromJNDIModelInsertFromJNDIModelHandlerconfiguration/loggerLoggerActionLoggerModelLoggerModelHandlerconfiguration/logger/levelLevelActionLevelModelLevelModelHandlerconfiguration/rootRootLoggerActionRootLoggerModelRootLoggerModelHandlerconfiguration/root/levelRootLoggerActionRootLoggerModelRootLoggerModelHandlerconfiguration/logger/appender-refAppenderRefActionAppenderRefModelAppenderRefModelHandlerconfiguration/root/appender-refAppenderRefActionAppenderRefModelAppenderRefModelHandlerconfiguration/includeIncludeActionIncludeModelIncludeModelHandlerconfiguration/propertiesConfiguratorPropertiesConfiguratorActionPropertiesConfiguratorModelPropertiesConfiguratorModelHandlerconfiguration/consolePluginConsolePluginAction无无configuration/receiverReceiverActionReceiverModelReceiverModelHandler*/variablePropertyActionPropertyModelPropertyModelHandler*/propertyPropertyActionPropertyModelPropertyModelHandlerconfiguration/importImportActionImportModelImportModelHandlerconfiguration/timestampTimestampActionTimestampModelTimestampModelHandlerconfiguration/shutdownHookShutdownHookActionShutdownHookModelShutdownHookModelHandlerconfiguration/sequenceNumberGeneratorSequenceNumberGeneratorActionSequenceNumberGeneratorModelSequenceNumberGeneratorModelHandlerconfiguration/serializeModelSerializeModelActionSerializeModelModelSerializeModelModelHandlerconfiguration/defineDefinePropertyActionDefineModelDefineModelHandlerconfiguration/evaluatorEventEvaluatorActionEventEvaluatorModelEventEvaluatorModelHandlerconfiguration/conversionRuleConversionRuleActionConversionRuleModelConversionRuleModelHandlerconfiguration/statusListenerStatusListenerActionStatusListenerModelStatusListenerModelHandler*/appenderAppenderActionAppenderModelAppenderModelHandlerconfiguration/appender/appender-refAppenderRefActionAppenderRefModelAppenderRefModelHandlerconfiguration/newRuleNewRuleAction无无*/paramParamActionParamModelParamModelHandler*/ifIfActionIfModelIfModelHandler*/if/thenThenActionThenModelThenModelHandler*/if/elseElseActionElseModelElseModelHandler*/appender/siftSiftActionSiftModelSiftModelHandler其它属性标签ImplicitModelActionImplicitModelImplicitModelHandler
DefaultProcessor解析model
public void process(Model model) {// 根节点为空, 直接异常if (model null) {addError(Expecting non null model to process);return;}// 1.将LoggerContext添加到ModelInterpretationContext中, 这是第一个也是最底层的initialObjectPush();// 2.使用第一阶段过滤器过滤model, 将满足条件的model使用handler处理mainTraverse(model, getPhaseOneFilter());// 3.处理依赖analyseDependencies(model);// 4.使用第二阶段过滤器过滤model, 将满足条件的model使用handler处理traversalLoop(this::secondPhaseTraverse, model, getPhaseTwoFilter(), phase 2);// 配置解析完成addInfo(End of configuration.);// 5.将LoggerContext从ModelInterpretationContext中弹出finalObjectPop();
}方法小结
将日志上下文loggerContext放到model解析器中使用第一阶段过滤器过滤model, 将满足条件的model使用handler处理, 不满足第一阶段过滤器的model有下面四个, 为什么这四个model这么特殊呢?? 因为它们需要依赖别的model(appender-ref标签)
LoggerModelRootLoggerModelAppenderModelAppenderRefModel
确定依赖顺序使用第二阶段过滤器过滤model, 将满足条件的model使用handler处理; 上面四个model将会在这里处理弹出loggerContext节点
具体的model节点解析将会在下节挑出几个重点来介绍, 到这里, logback解析logback.xml文件的整体流程就介绍完了
关于一些细节以及JoranConfigurator和JoranConfiguratorBase中相关的内容没有详细介绍, 读者想要了解的更多建议看下源码
三、总结
JoranConfigurator 是logback框架用来解析配置文件的核心类(logback.xml配置文件)logback.xml文件中每个节点都会被解析成一个saxEvent, 解析过程中借助解析器上下文SaxEventInterpretationContext保存相关信息RuleStore中映射了每个节点路径对应的action, action会创建对应节点的modelDefaultProcessor中记录了每个model和modelHandler的映射关系DefaultProcessor借助上下文对象ModelInterpretationContext将model分为两个阶段使用modelHandler进行处理最后将处理结果都放到了日志上下文LoggerContext中(这是我们打印日志的重要对象)流程就是(eg. configuration/appender/encoder - saxEvent - action - model - modelHandler - loggerContext)