Kevin
Kevin

Reputation: 81

How SLF4J/ JPA / JAX-RS find their implementation?

I'm learning Java and I find that there are many functionalities that are standardized :

Let's take the Sl4j example : to use it correctly with log4j, we have to import the sl4j api, the sl4j/log4j bridge and the log4j implementation.

Question : In my class, I only communicate with the Slf4j API.

How my application knows about the log4j implementation ? Can someone explains what's happening exactly under the hood ?

Regards

Upvotes: 7

Views: 905

Answers (4)

Zakaria
Zakaria

Reputation: 15070

The OP asks a general question about how implementation is injected in some different cases.

Logging

As sated in many answers, the SLF4J gives the interface and log4j-slf4j gives the implementation.

When you use the following statement:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    ...
    private static final Logger LOG = LoggerFactory.getLogger(FooBarClass.class);
    ...
    LOG.debug("Foobar");

This is what is Happening:

We try to get the Logger from the getLogger method declared in the LoggerFactory class :

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();
        }
        ...
    }

So the magic happens at that statement:

return StaticLoggerBinder.getSingleton().getLoggerFactory();

Because the classpath knows you implemented why, the StaticLoggerBinder implementation is provided by log4j. As we can notice, log4j provides a with its own implementation:

private final ILoggerFactory loggerFactory;
...
private StaticLoggerBinder() {
    loggerFactory = new Log4jLoggerFactory();
}

And that's it !

Persistence

For JPA/Hibernate part, you have to include hibernate-jpa-api and hibernate-* (core, entitymanager, etc).

Let's say you want to create an EntityManagerFactory:

    import javax.persitence.EntityManagerFactory
    import javax.persitence.Persistence;
    ...
    private static EntityManagerFactory EMF = Peristence.createEntityManagerFactory("foobar", null);

As for List and ArrayList, your classpath is fed with the interface and the implementation thanks to the JARs you import.

The EntityManagerFactory comes from the hibernate-jpa-api where we have a Persistence class. We can notice that the createEntityManagerFactory method first lists all the providers and for each one of them, an createEntityManagerFactory is fired. This is where the hibernate comes. It provides an HibernatePersistenceProvider that implements the PersistenceProvider class.

This is how Hibernate is injected.

Upvotes: 5

davidxxx
davidxxx

Reputation: 131346

The SLF4J manual refers to how under the hoop SLF4J finds the implementation to use : Binding with a logging framework at deployment time.

SLF4J refers the thing that allows to use an implementation (Logback, Log4J, etc...) as "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.

You have as many SLF4J bindings as implementations of SLF4J. And of course, an implementation API may have distinct "SLF4J bindings" according to its version :

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.

The binding with the implementation is not performed at runtime but at compile-time : each SLF4J binding is hardwired at compile time to use one and only one specific logging framework.
So, you have just to include the SLF4J binding in the classpath (for example slf4j-jdk14-1.7.22.jar) so that SLF4J uses it :

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.

That's why it is generally advised to never place more than one SLF4J binding on the classpath as SLF4J is not designed to choose the implementation at runtime.

Upvotes: 1

Andremoniy
Andremoniy

Reputation: 34900

If you are talking about dealing with slf4j loggers, like:

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FooClass.class);

then it is pretty simple: org.slf4j.Logger is just an interface, which has several implementations. In case of usage library slf4j-log4j12, this interface is implemented by class org.slf4j.impl.Log4jLoggerAdapter which internally contains

final transient org.apache.log4j.Logger logger;

So it is simple adapter which wraps your logging requests and invoke them on log4j logger object:

public void debug(String msg) {
    logger.log(FQCN, Level.DEBUG, msg, null);
}

More specifically, proper Logger implementation is produced by LoggerFactory which firstly creates Log4jLoggerFactory via

StaticLoggerBinder.getSingleton().getLoggerFactory()

, latter creates needed Log4jLoggerAdapter instance.


Generally it works via adaptation level like pictured on img from documentation:

enter image description here

Upvotes: 2

Vikas Sachdeva
Vikas Sachdeva

Reputation: 5813

Slf4j can be used with log4j or any other underlying logging library.

In case of log4j, it uses log4j-slf4j-impl.jar which contains necessary classes for communicating with log4j library.

As per the 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-log4j Integration

Upvotes: 2

Related Questions