spring-logging
启动错误日志打印
可以看到用了一个很大的try-catch,所有的启动错误都通过统一的handleRunFailure来处理
在handleRunFailure方法里,做了几个事情:
发消息;打印错误日志(report;处理钩子方法。。只关心打印错误日志这里
reportFailure方法里会遍历异常reporter,每个reporter都有适用的异常,匹配上了就打印,否则走下面兜底逻辑。
(实际上SpringBoot默认提供了一个reporter实现:FailureAnalyzers
reportException逻辑分为两块,一个是把分析逻辑分发给具体的FailureAnalyzer,analyzer提供一个格式化的分析结果,提供给不同的FailureAnalysisReporter遍历打印。
(FailureAnalysisReporter默认也是提供了一个Logging实现
深入看一下analyze逻辑:主要是继承AbstractFailureAnalyzer 抽象类
分为两个步骤:
- findCause:判断这个异常是否是当前类关心的 & 返回关心异常类
- getCauseType通过子类的范型找到关心的异常类
- 通过循环找cause判断当前异常栈里是否包含当前关心异常类
- analyze,把异常栈提供给analyzer,进行错误分析并格式化返回
以一个子类为例,分析+组装结果
看代码的时候发现经常使用到工厂,仔细看一下工厂产品是如何组装的
ArgumentResolver的构造函数:接收两个参数,clazz, object
后面在使用的时候 resolver.resolve(argClazz) 判断参数argClazz 如果等于clazz,则返回object
ps:这个接口命名看着有点奇怪,Spring认为ExceptionReporter是工厂?
getSpringFactoriesInstances负责根据配置文件进行类初始化(当作构造方法使用
SpringFactoriesLoader.forxxx是构造函数,读取并解析META-INF/spring.factories文件,获得了工厂接口类型下的具体实现类列表
load负责把这些全类名实例化,具体逻辑在instantiateFactory里
instantiateFactory 负责通过工厂全类名对类进行实例化
- Class.forName类加载获得类clazz
- 从clazz中获得construtor
- 使用construtor通过反射获得类,args是从resolver里通过参数类型换的实例
感觉这里有点奇怪 为什么要使用这个resolver呢
- 使用一个全局的工厂类放在里面也可以
- resolver在类型上只支持一个类型 很局限
这里迭代一下认知:argumentResolver支持and,可以支持多个类型
都看到这了,顺手看一下loggingSystem是如何初始化的
LoggingSystem初始化
LoggingApplicationListener
listener监听application消息,进行初始化(ApplicationStartingEvent
分为两个步骤
- 选择具体的实现类
- 执行beforeInitialize扩展点
这里选择具体的实现类逻辑如下
可以看到实际上是在当前的list里面按顺序取,这个list也是通过META-INF/spring.factories配置的:
这里有个问题,换别的loggingSystem的时候,这里是如何识别的呢?
先复习一下如何使用log4j2
- pom文件中,spring-boot-starter里排除starter-logging(logback,新增starter-log4j2
- src/main/resource 下新建log4j2.xml
1 |
|
(修改完了以后 在这里打个断点,发现真的变成了log4j2 真神奇
发现遍历时还是先判断logback,然后通过尝试类加载,判断是否选择logback