SLF4J/ JPA / JAX-RS 如何找到它们的实现?
How SLF4J/ JPA / JAX-RS find their implementation?
我正在学习 Java,我发现有许多标准化的功能:
- 日志记录(使用 SLF4J)
- 持久性(使用 JPA)
- REST(使用 JAX-RS)
- SOAP(使用 JAX-WS)
- 等等
让我们以 Sl4j 为例:要将其与 log4j 一起正确使用,我们必须导入 sl4j api、sl4j/log4j 桥和 log4j 实现。
问题 : 在我的class中,我只与Slf4j通信API.
我的应用程序如何知道 log4j 实现?
有人可以解释一下到底发生了什么吗?
此致
如果您正在谈论处理 slf4j
记录器,例如:
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FooClass.class);
那么就很简单了:org.slf4j.Logger
只是一个接口,它有几个实现。在使用库slf4j-log4j12
的情况下,这个接口由class org.slf4j.impl.Log4jLoggerAdapter
实现,内部包含
final transient org.apache.log4j.Logger logger;
所以它是一个简单的适配器,它包装您的日志记录请求并在 log4j
记录器对象上调用它们:
public void debug(String msg) {
logger.log(FQCN, Level.DEBUG, msg, null);
}
更具体地说,正确的 Logger
实现是由 LoggerFactory
产生的,它首先通过
创建 Log4jLoggerFactory
StaticLoggerBinder.getSingleton().getLoggerFactory()
,后者创建所需的 Log4jLoggerAdapter
个实例。
通常它通过适应级别工作,如来自 documentation 的 img 上的图片所示:
Slf4j
可以与 log4j
或任何其他底层日志库一起使用。
在 log4j
的情况下,它使用 log4j-slf4j-impl.jar
,其中包含与 log4j
库通信所必需的 类。
根据 documentation -
SLF4J doesn't resolve the logging implementation at execution, but
directly at the compilation with a bridging API. So more than the JAR
of SLF4J you need the following JARs : the bridging JAR and the JAR of
the implementation. Here is what you get with Log4J :
SLF4J 手册提到了 SLF4J 如何找到要使用的实现:Binding with a logging framework at deployment time。
SLF4J 将允许使用实现(Logback、Log4J 等)的东西称为 "SLF4J bindings" :
As mentioned previously, SLF4J supports various logging frameworks.
The SLF4J distribution ships with several jar files referred to as
"SLF4J bindings", with each binding corresponding to a supported
framework.
您拥有的 SLF4J 绑定与 SLF4J 的实现一样多。
当然,一个实现 API 根据其版本可能有不同的 "SLF4J bindings" :
To switch logging frameworks, just replace slf4j bindings on your
class path. For example, to switch from java.util.logging to log4j,
just replace slf4j-jdk14-1.7.22.jar with slf4j-log4j12-1.7.22.jar.
与实现的绑定不是在运行时而是在 compile-time 执行:每个 SLF4J 绑定在编译时都是硬连线的,以使用一个且仅一个特定的日志记录框架。
因此,您只需在类路径中包含 SLF4J 绑定(例如 slf4j-jdk14-1.7.22.jar)以便 SLF4J 使用它:
SLF4J does not rely on any special class loader machinery. In fact,
each SLF4J binding is hardwired at compile time to use one and only
one specific logging framework. For example, the
slf4j-log4j12-1.7.22.jar binding is bound at compile time to use
log4j. In your code, in addition to slf4j-api-1.7.22.jar, you simply
drop one and only one binding of your choice onto the appropriate
class path location. Do not place more than one binding on your class
path. Here is a graphical illustration of the general idea.
这就是为什么通常建议永远不要在类路径上放置一个以上的 SLF4J 绑定,因为 SLF4J 并非设计为在运行时选择实现。
OP 问了一个关于在某些不同情况下如何注入实现的一般性问题。
日志记录
如许多答案所述,SLF4J
给出接口,log4j-slf4j
给出实现。
当您使用以下语句时:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger LOG = LoggerFactory.getLogger(FooBarClass.class);
...
LOG.debug("Foobar");
这就是正在发生的事情:
我们尝试从 LoggerFactory
class 中声明的 getLogger
方法中获取 Logger
:
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
}
...
}
所以神奇的事情就发生在那个陈述上:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
因为类路径知道你实现的原因,StaticLoggerBinder
实现is provided by log4j
。
我们可以注意到,log4j
提供了自己的实现:
private final ILoggerFactory loggerFactory;
...
private StaticLoggerBinder() {
loggerFactory = new Log4jLoggerFactory();
}
就是这样!
坚持
对于 JPA/Hibernate 部分,您必须包括 hibernate-jpa-api
和 hibernate-*
(核心、实体管理器等)。
假设您要创建一个 EntityManagerFactory
:
import javax.persitence.EntityManagerFactory
import javax.persitence.Persistence;
...
private static EntityManagerFactory EMF = Peristence.createEntityManagerFactory("foobar", null);
至于 List
和 ArrayList
,您的类路径由您导入的 JAR 提供接口和实现。
EntityManagerFactory
来自 hibernate-jpa-api
,我们有一个 Persistence
class。
我们可以注意到 createEntityManagerFactory
方法首先列出所有 providers 并且对于其中的每一个,一个 createEntityManagerFactory
被触发。
这就是 hibernate
的来源。它提供了一个 HibernatePersistenceProvider
即 implements the PersistenceProvider
class.
这就是 Hibernate
的注入方式。
我正在学习 Java,我发现有许多标准化的功能:
- 日志记录(使用 SLF4J)
- 持久性(使用 JPA)
- REST(使用 JAX-RS)
- SOAP(使用 JAX-WS)
- 等等
让我们以 Sl4j 为例:要将其与 log4j 一起正确使用,我们必须导入 sl4j api、sl4j/log4j 桥和 log4j 实现。
问题 : 在我的class中,我只与Slf4j通信API.
我的应用程序如何知道 log4j 实现? 有人可以解释一下到底发生了什么吗?
此致
如果您正在谈论处理 slf4j
记录器,例如:
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FooClass.class);
那么就很简单了:org.slf4j.Logger
只是一个接口,它有几个实现。在使用库slf4j-log4j12
的情况下,这个接口由class org.slf4j.impl.Log4jLoggerAdapter
实现,内部包含
final transient org.apache.log4j.Logger logger;
所以它是一个简单的适配器,它包装您的日志记录请求并在 log4j
记录器对象上调用它们:
public void debug(String msg) {
logger.log(FQCN, Level.DEBUG, msg, null);
}
更具体地说,正确的 Logger
实现是由 LoggerFactory
产生的,它首先通过
Log4jLoggerFactory
StaticLoggerBinder.getSingleton().getLoggerFactory()
,后者创建所需的 Log4jLoggerAdapter
个实例。
通常它通过适应级别工作,如来自 documentation 的 img 上的图片所示:
Slf4j
可以与 log4j
或任何其他底层日志库一起使用。
在 log4j
的情况下,它使用 log4j-slf4j-impl.jar
,其中包含与 log4j
库通信所必需的 类。
根据 documentation -
SLF4J doesn't resolve the logging implementation at execution, but directly at the compilation with a bridging API. So more than the JAR of SLF4J you need the following JARs : the bridging JAR and the JAR of the implementation. Here is what you get with Log4J :
SLF4J 手册提到了 SLF4J 如何找到要使用的实现:Binding with a logging framework at deployment time。
SLF4J 将允许使用实现(Logback、Log4J 等)的东西称为 "SLF4J bindings" :
As mentioned previously, SLF4J supports various logging frameworks. The SLF4J distribution ships with several jar files referred to as "SLF4J bindings", with each binding corresponding to a supported framework.
您拥有的 SLF4J 绑定与 SLF4J 的实现一样多。 当然,一个实现 API 根据其版本可能有不同的 "SLF4J bindings" :
To switch logging frameworks, just replace slf4j bindings on your class path. For example, to switch from java.util.logging to log4j, just replace slf4j-jdk14-1.7.22.jar with slf4j-log4j12-1.7.22.jar.
与实现的绑定不是在运行时而是在 compile-time 执行:每个 SLF4J 绑定在编译时都是硬连线的,以使用一个且仅一个特定的日志记录框架。
因此,您只需在类路径中包含 SLF4J 绑定(例如 slf4j-jdk14-1.7.22.jar)以便 SLF4J 使用它:
SLF4J does not rely on any special class loader machinery. In fact, each SLF4J binding is hardwired at compile time to use one and only one specific logging framework. For example, the slf4j-log4j12-1.7.22.jar binding is bound at compile time to use log4j. In your code, in addition to slf4j-api-1.7.22.jar, you simply drop one and only one binding of your choice onto the appropriate class path location. Do not place more than one binding on your class path. Here is a graphical illustration of the general idea.
这就是为什么通常建议永远不要在类路径上放置一个以上的 SLF4J 绑定,因为 SLF4J 并非设计为在运行时选择实现。
OP 问了一个关于在某些不同情况下如何注入实现的一般性问题。
日志记录
如许多答案所述,SLF4J
给出接口,log4j-slf4j
给出实现。
当您使用以下语句时:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger LOG = LoggerFactory.getLogger(FooBarClass.class);
...
LOG.debug("Foobar");
这就是正在发生的事情:
我们尝试从 LoggerFactory
class 中声明的 getLogger
方法中获取 Logger
:
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
}
...
}
所以神奇的事情就发生在那个陈述上:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
因为类路径知道你实现的原因,StaticLoggerBinder
实现is provided by log4j
。
我们可以注意到,log4j
提供了自己的实现:
private final ILoggerFactory loggerFactory;
...
private StaticLoggerBinder() {
loggerFactory = new Log4jLoggerFactory();
}
就是这样!
坚持
对于 JPA/Hibernate 部分,您必须包括 hibernate-jpa-api
和 hibernate-*
(核心、实体管理器等)。
假设您要创建一个 EntityManagerFactory
:
import javax.persitence.EntityManagerFactory
import javax.persitence.Persistence;
...
private static EntityManagerFactory EMF = Peristence.createEntityManagerFactory("foobar", null);
至于 List
和 ArrayList
,您的类路径由您导入的 JAR 提供接口和实现。
EntityManagerFactory
来自 hibernate-jpa-api
,我们有一个 Persistence
class。
我们可以注意到 createEntityManagerFactory
方法首先列出所有 providers 并且对于其中的每一个,一个 createEntityManagerFactory
被触发。
这就是 hibernate
的来源。它提供了一个 HibernatePersistenceProvider
即 implements the PersistenceProvider
class.
这就是 Hibernate
的注入方式。