Skip to content

Commit

Permalink
Merge pull request #335 from lihongzheshuai/branch-theme-chirpy
Browse files Browse the repository at this point in the history
merge post java-go-12
  • Loading branch information
lihongzheshuai authored Sep 2, 2024
2 parents db6cb45 + 8b7c8d3 commit e003be6
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 1 deletion.
14 changes: 14 additions & 0 deletions _includes/custom/custom-post-content-after.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
<div style="margin-bottom: 20px;">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7615326632728696"
crossorigin="anonymous"></script>
<!-- content-footer -->
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-7615326632728696"
data-ad-slot="6416549436"
data-ad-format="auto"
data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<!-- 插入QQ群加群链接 -->
<p style="text-align: center;">
<a href="https://qm.qq.com/q/gKhDy9QkTK" style="font-weight: bold; color: #030303;">交流探讨,加入群聊【Java一起学高级群(982860385)】</a>
Expand Down
14 changes: 14 additions & 0 deletions _includes/custom/custom-post-content-inner.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

<p>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7615326632728696"
crossorigin="anonymous"></script>
<ins class="adsbygoogle"
style="display:block; text-align:center;"
data-ad-layout="in-article"
data-ad-format="fluid"
data-ad-client="ca-pub-7615326632728696"
data-ad-slot="2477304429"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</p>
5 changes: 4 additions & 1 deletion _posts/2024-08-21-java-go-11-import-log-three.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ _IDEA中如何刷新Gradle项目_

## 二、源码解读SLF4J如何寻找Log实现层框架

{% include custom/custom-post-content-inner.html %}

上文中([***一起学Java(10)-为项目引入Log框架(Log篇二-引入SLF4J接口层框架)***](https://www.coderli.com/java-go-9-import-log-one/)),在未引入Logback应用层框架的时候,打印日志时控制台输出:

Expand Down Expand Up @@ -97,6 +98,8 @@ public class LogMain {

### org.slf4j.LoggerFactory 实现代码

{% include custom/custom-post-content-inner.html %}

`org.slf4j.LoggerFactory`中依次调用:

```java
Expand Down Expand Up @@ -286,4 +289,4 @@ getServiceLoader中通过Java中的`java.util.ServiceLoader`去寻找实现类

当前情况下,classpath下自然找不到这些文件。`providerList`为空所以在`bind`函数中输出了之前我们关注警告信息。

下一篇我们再继续研究引入Logback框架的情况,估计已经猜出大半了
下一篇我们再继续研究引入Logback框架的情况,估计你已经猜出大半了
98 changes: 98 additions & 0 deletions _posts/2024-09-02-java-go-12-import-log-four-logback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
layout: post
title: 一起学Java(12)-[日志篇]教你分析SLF4J源码,掌握SLF4J如何与Logback无缝集成的原理
date: 2024-09-02 08:00 +0800
author: onecoder
image: /images/post/java-go-12/logback-provider.png
comments: true
tags: [Java,Log, SLF4J, Logback, 一起学Java]
categories: [一起学Java系列,(2)引入日志篇]
---
继续完成上篇([一起学Java(11)-[日志篇]教你分析SLF4J源码,掌握Logger接口实现类加载原理](https://www.coderli.com/java-go-11-import-log-three/))留给自己的任务,研究Logback是如何和SLF4J无缝集成的。

<!--more-->

在之前的`SLF4J`源码研究中([教你分析SLF4J源码,掌握Logger接口实现类加载原理](https://www.coderli.com/java-go-11-import-log-three/))我们已经知道`SLF4J`中利用`java.util.ServiceLoader` 机制寻找SLF4JServiceProvider的实现类,从而得到构造Logger实例的工厂类。那么我们就先看看`java.util.ServiceLoader`是个什么东西。

## 一、java.util.ServiceLoader定位和原理

`java.util.ServiceLoader` 是 Java 提供的一个服务发现机制,它允许程序在运行时动态地加载和使用实现了某个接口或抽象类的服务实现。这个机制在 Java 的模块化系统和插件系统中非常有用。正好符合Log框架的场景定位。

`ServiceLoader` 通过一种称为***服务提供者接口(SPI, Service Provider Interface***的机制工作。服务提供者接口是一个接口或抽象类,而服务实现类是该接口或抽象类的具体实现。

### 1. **服务声明**

服务提供者接口在 JAR 包中的 `META-INF/services` 目录下声明。该目录下的每个文件名对应一个服务接口,全路径名,文件内容是该接口的一个或多个实现类的全路径名。

例如,假设你有一个接口 `com.example.MyService`,那么你需要在 `META-INF/services` 目录下创建一个名为 `com.example.MyService` 的文件,文件内容如下:

```java
com.example.impl.MyServiceImpl1
com.example.impl.MyServiceImpl2
```

这表示 `MyService` 有两个实现类:`MyServiceImpl1``MyServiceImpl2`

### 2. **加载服务**

使用 `ServiceLoader` 加载服务的代码通常如下:

```java
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
service.doSomething(); // doSomething是MyService接口中定义的方法
}
```

`ServiceLoader.load()` 方法会查找并实例化所有在 `META-INF/services/com.example.MyService` 文件中列出的服务实现类,后面就是实现类的使用了,也就不难理解了。

以上述理论为基础,来看看Logback中的实现。

## 二、Logback代码解析,如何实现SLF4JServiceProvider接口

{% include custom/custom-post-content-inner.html %}

展开logback-classic源码包,找到`META-INF/services`文件夹,果然有名为`org.slf4j.spi.SLF4JServiceProvider`的文件

![logback-classic中META-INF/services目录下的文件](/images/post/java-go-12/logback-provider.png)

内容如下:

```plaintext
ch.qos.logback.classic.spi.LogbackServiceProvider
```

即Logback提供的`SLF4JServiceProvider`接口的实现类。这样,根据上文的分析([教你分析SLF4J源码,掌握Logger接口实现类加载原理](https://www.coderli.com/java-go-11-import-log-three/)),在`SLF4J`***LoggerFactory***中便可以通过`java.util.ServiceLoader`获取到Logback的***SLF4JServiceProvider***实现,并在bind函数中执行后续的Logger初始化操作。

```java
private final static void bind() {
try {
List<SLF4JServiceProvider> providersList = findServiceProviders();
reportMultipleBindingAmbiguity(providersList);
if (providersList != null && !providersList.isEmpty()) {
PROVIDER = providersList.get(0);
// SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
PROVIDER.initialize();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(providersList);
} else {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Reporter.warn("No SLF4J providers were found.");
Reporter.warn("Defaulting to no-operation (NOP) logger implementation");
Reporter.warn("See " + NO_PROVIDERS_URL + " for further details.");

Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
postBindCleanUp();
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
```

> 在bind函数源码中可以看到,如果项目中发现了多个`SLF4JServiceProvider`的实现类,SLF4J只会取第一个进行初始化。
{: .prompt-tip }

至此,SLF4J如何与Logback无缝集成的原理也就大致了解清楚了。对于Logback而言,原生实现了`SLF4JServiceProvider`接口,自然也就不需要桥接层了。对于像Log4j 2这种没有原生实现`SLF4JServiceProvider`的框架,其实我们也能大致猜到其桥阶层的实现原理:按照`java.util.ServiceLoader`原理向上实现SLF4J的`SLF4JServiceProvider`接口,向下调用对应的日志实现层框架,下一步我们就看一下Log4j 2的桥接方式,来印证一下我们的猜想。
Binary file removed images/onecoder-photo.jpg
Binary file not shown.
Binary file added images/post/java-go-12/logback-provider.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed images/previews/java-log.png
Binary file not shown.

0 comments on commit e003be6

Please sign in to comment.