pc端宣传网站开发做+淘宝客最大的网站是叫什么
web/
2025/10/5 4:53:15/
文章来源:
pc端宣传网站开发,做+淘宝客最大的网站是叫什么,微信小商店分销系统,哪个网站可以做会计分录阅读推荐程序员跳槽时机已到#xff0c;闲聊中面试官无意泄题SpringBoot作为日常开发利器#xff0c;开箱即用#xff0c;大量的star等已经成为节省开发的重要框架之一#xff0c;但是各个框架的star中引入的日志框架却不尽相同#xff0c;有的是log4j#xff0c;有的是s…阅读推荐程序员跳槽时机已到闲聊中面试官无意泄题SpringBoot作为日常开发利器开箱即用大量的star等已经成为节省开发的重要框架之一但是各个框架的star中引入的日志框架却不尽相同有的是log4j有的是slf4j这导致我们在引入多个框架的star的时候往往会引入多个日志框架每一个日志框架彼此效率不尽相同那么我们能不能做到在项目中仅引入一个统一的日志框架呢本篇我们就来探索SpringBoot如何实现统一日志操作为什么需要日志首先我们需要明白日志的作用是什么--即用来在程序运行过程中将我们需要的信息打印出来便于我们在调试中查找和观察。在JAVA中存在很多常见的日志框架如JUL、JCL、Jboss-logging、log4j、logback、slf4j等这么多日志框架我们该如何选择日志门面与日志实现在日志框架选型之前我们先了解一个概念什么是日志门面日志门面,不是具体的日志解决方案,它只服务于各种各样的日志系统允许最终用户在部署其应用时使用其所希望的日志实现来使用日志功能。而日志实现则是基于对应的日志门面的规范来实现的具体日志功能的框架常见的日志门面与日志实现关系如下:每一种日志框架输出信息的效率也不尽相同而我们日常开发使用的框架中往往都会引入一个日志框架来辅助输出框架信息然而框架之间由于历史迭代原因及框架性能等问题选择的日志框架也不一样常见的框架与默认选择的日志系统关系如下:由于历史迭代原因JCL和jboss-logging日志框架基本已经很久没有更新了不太适合作为现在框架的主流选择那么剩下的选择中log4j、slf4j是使用最多的然而由于log4j的输出性能问题log4j的作者选择重新编写了一个日志门面--Slf4j并且编写了基于Slf4j的日志实现--logback其输出信息的效率远超log4j解决了log4j遗留下的性能问题所以在SpringBoot框架中默认也选择了Slf4j来作为默认日志框架slf4j的使用现在我们来看看slf4j的使用,引入maven依赖:org.slf4j slf4j-api 1.7.28按照slf4j官方的说法日志记录方法的调用不应该来直接调用日志的实现类而是调用日志抽象层里面的实现方法获取通过日志工厂创建的日志实例即可输出对应的日志:import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class HelloWorld { public static void main(String[] args) { Logger logger LoggerFactory.getLogger(HelloWorld.class);[图片上传中...(slf4j日志输出过程.png-6f5073-1583207284091-0)] logger.info(Hello World); }}这里我们注意到了一点使用slf4j的输出日志的时候我们也引入了logback这个基于slf4j日志门面实现的具体日志输出框架如果不指定具体的日志输出实现将会找不到具体的日志输出实例slf4j的日志输出过程如图所示:slf4j日志输出过程从图中可以看到应用程序调用了slf4j的api接口以后具体的实现则是由slf4j日志门面找到对应的日志的系统来实现日志输出解决多框架日志不统一问题现在我们再回到日志统一的问题上前面已经了解了开发常用的框架如Spring、mybatis等使用的框架都是框架开发者自己选择的如果我们每个框架就引入一个日志系统并且最终需要打印日志的时候会出现使用n种日志系统平台并且每一种的日志打印的格式、内容和性能都需要手动控制不仅让项目变大而且增大了项目复杂度对性能也有很大的影响那么我们该如何让所有的开源框架统一使用Slf4j来输出呢我们来看下slf4j官方给我们的方案如图所示:sfl4j适配日志从图中我们可以看出来官方的方案是针对不同的日志框架开发了一套适配兼容的框架与之对应使用这些兼容jar来替代原来的日志框架即可例如log4j日志框架与之对应的就是log4j-over-slf4j.jar并且常见的日志框架slf4j团队都实现了一套与之对应的基于slf4j的兼容框架关系如下:日志框架slf4j兼容框架log4jlog4j-over-slf4jcommons loggingjcl-over-slf4jjava.util.loggingjui-to-slf4jSpringBoot如何处理日志关系在使用SpringBoot的时候我们会发现官方默认使用的是spring‐boot‐starter‐logging这个starter来引入日志系统的我们展开该依赖的依赖图如下SpringBoot处理日志关系可以看到spring‐boot‐starter‐logging这个starter中引入了四个日志实例的依赖分别是logback和我们前面提到的日志兼容jar的依赖并且最终引入了slf4j的日志门面的依赖实现了统一日志处理。但是为什么兼容jar引入后就能解决日志输出的问题呢难道兼容包有什么神奇的黑科技吗其实不然我们随便展开其中的几个兼容日志jar的包名如图日志兼容包的包名关系原来这些日志兼容包的包名与原来的日志框架的包名完全一样并且完全按照slf4j的方式实现了一套和以前一样的API这样依赖这些日志框架的开源框架在运行的时候查找对应包名下的class也不会报错但熟悉java类加载机制的都知道两个jar的包名以及使用的class都一样的话加载会出现异常我们进入spring‐boot‐starter‐logging的pom依赖中一探究竟最后在maven依赖中发现了端倪如Spring框架使用的是commons-logging而在spring-boot-starter-logging中将spring的日志依赖排除如下org.springframework spring‐core commons‐logging commons‐logging 这样spring框架在运行时使用的时候使用的就是兼容jar中的日志实例了SpringBoot成功的完成了一次日志系统统一的偷天换日操作。slf4j的桥接原理通过查看SpringBoot的日志处理我们可以大致总结如下几步操作:1、将系统中其他日志框架先排除出去2、用中间包来替换原有的日志框架3、我们导入slf4j其他的实现通过以上的操作即可完成日志系统的统一但是我们开始有了新的疑惑slf4j是怎么做到的自动查找对应的实现日志并且完成了日志的正常打印操作的呢这个就要涉及到slf4j的桥接原理我们先来看看slf4j源码中关于日志调用相关的代码://slf4j日志调用过程相关的代码//根据名称获取日志实例public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory getILoggerFactory(); return iLoggerFactory.getLogger(name);}//获取日志实例工厂并且完成日志实例的查找与初始化操作 public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE UNINITIALIZED) { INITIALIZATION_STATE ONGOING_INITIALIZATION; //查找实现类 performInitialization(); } ... return StaticLoggerBinder.getSingleton().getLoggerFactory(); ... }可以看到整个过程中是通过StaticLoggerBinder.getSingleton() 来进行初始化日志工厂操作而StaticLoggerBinder这个类是从哪来的呢我们发现StaticLoggerBinder类并不存在于slf4j的jar中而是通过查找org/slf4j/impl/StaticLoggerBinder.class类的路径来发现具体的实现类代码如下://设置默认的查找日志实例的StaticLoggerBinder路径private static String STATIC_LOGGER_BINDER_PATH org/slf4j/impl/StaticLoggerBinder.class;private static Set findPossibleStaticLoggerBinderPathSet() { ....... paths ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); ......}这个时候我们就该思考一个问题如果我们同时存在了多个StaticLoggerBinder 时会加载哪一个呢熟悉java类加载机制可知类加载器会按照一定的顺序逐个扫描jar包目录并且加载出来所以先被类加载器扫描的StaticLoggerBinder会优先被加载具体的加载顺序如下:1.$java_home/lib 目录下的java核心api2.$java_home/lib/ext 目录下的java扩展jar包3.java -classpath/-Djava.class.path所指的目录下的类与jar包4.$CATALINA_HOME/common目录下按照文件夹的顺序从上往下依次加载5.$CATALINA_HOME/server目录下按照文件夹的顺序从上往下依次加载6.$CATALINA_BASE/shared目录下按照文件夹的顺序从上往下依次加载7.项目/WEB-INF/classes下的class文件8.项目/WEB-INF/lib下的jar文件根据slf4j桥接原理改造logger我们都知道平时使用slf4j输出日志的时候往往获取Logger实例来进行日志打印但是Logger仅仅支持本地日志不支持分布式环境的日志而在slfj中有LogBean实例可以支持分布式日志包含了链路相关信息那么我们是否可以改造slf4j的桥接过程使得我们可以灵活的使用本地日志或者分布式日志呢首先我们先看看我们需要实现的需求:logger和logbean结合,统一日志入口logbean降低代码侵入性无缝替换第三方框架中的日志,根据需求加入到分布式日志中想要实现这个功能有以下两个思路实现:1.我们通过自定义appender基于logback的appender进行扩展可以实现分别输出本地日志以及分布式日志但是缺陷在于appender扩展性不高很多参数信息获取不到例如上下文信息等2.我们通过实现Logger接口用来将Logger和LogBean聚合在一起从而实现LogBean集成到Logger中同样此种方式的缺陷在于对于第三方框架日志我们无能为力无法直接替换使用并且在使用的时候需要使用自定义的LogFactory第一种思路我们可以看出来局限性太高灵活度不够接下来我们尝试使用第二种方案实现聚合Logger和LogBean对外公开统一的api进行日志输出使用:public class CustomLogger implements LocationAwareLogger { private Logger logger; //提供getLogger方法获取logger public static LoggerFacade getLogger(Class clazz) { LoggerFacade loggerFacade new LoggerFacade(); loggerFacade.logger LoggerFactory.getLogger(clazz); return loggerFacade; } ... //打印本地日志的同时 输出到logbean中 Override public void warn(String msg) { logger.warn(msg); appendExtra(msg, Level.WARN); } ...... public void appendExtra(String str, Level level) { String date DateFormatUtils.format(new Date(), yyyy-MM-dd HH:mm:ss); //获取上下文通过上下文判断如果存在则获取分布式环境的LogBean实例 ThreadContext threadContext ContextContainer.retrieveServiceContext(); if (threadContext ! null) { LogBean logBean threadContext.getLogBean(); if (logBean ! null) { logBean.getInner().getExtra().add(date level.toString() simpleName(getName()) - str); } } }}接下来我们可以替换slf4j的实现修改为我们自定义的CustomerLogger内部调用logback的日志本地输出而通过前面桥接原理可以知道slf4j具体桥接获取实例的过程是通过LoggerFactory来获取那么我们来尝试修改LoggerFactory的代码实现替换为CustomerLogger实例public class CustomLoggerFactory implements ILoggerFactory { private static CustomLoggerFactory customLoggerFactory; public static CustomLoggerFactory getInstance(LoggerContext loggerContext) { if (customLoggerFactory null) { customLoggerFactory new CustomLoggerFactory(loggerContext); } return customLoggerFactory; } //logback的LoggerFactory实现 private LoggerContext loggerContext; public CustomLoggerFactory(LoggerContext loggerContext) { this.loggerContext loggerContext; } //返回CustomLogger Override public Logger getLogger(String name) { ch.qos.logback.classic.Logger logger loggerContext.getLogger(name); return CustomLogger.getLogger(logger); } public LoggerContext getLoggerContext() { return loggerContext; } Override public ILoggerFactory getLoggerFactory() { if (!initialized) { return defaultLoggerContext; } if (contextSelectorBinder.getContextSelector() null) { throw new IllegalStateException( contextSelector cannot be null. See also NULL_CS_URL); } LoggerContext loggerContext contextSelectorBinder.getContextSelector().getLoggerContext(); return CustomLoggerFactory.getInstance(loggerContext); }}由以上替换后,项目中通过LoggerFactory获取的到logger对象 就替换成了CustomLogger对象了从而实现了降低侵入将Logger与LogBean整合的效果结语Hi~ o(▽)ブ 整理了约100G的面试、学习资料但是呢篇幅有限。若你有此需求那便可免费分享下载在简信发送“面试”或 点击此链接获取资源下载方式下载吧。网盘上百G资源java面试题详解java视频及资料
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87168.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!