loginject for HK2

The loginject library facilitates dependency injection for loggers, or, more generally, site-specific dependency injection.

License

License

Categories

Categories

HK2 Application Layer Libs Dependency Injection
GroupId

GroupId

org.loginject
ArtifactId

ArtifactId

loginject-hk2
Last Version

Last Version

1.1.0
Release Date

Release Date

Type

Type

jar
Description

Description

loginject for HK2
The loginject library facilitates dependency injection for loggers, or, more generally, site-specific dependency injection.

Download loginject-hk2

How to add to project

<!-- https://jarcasting.com/artifacts/org.loginject/loginject-hk2/ -->
<dependency>
    <groupId>org.loginject</groupId>
    <artifactId>loginject-hk2</artifactId>
    <version>1.1.0</version>
</dependency>
// https://jarcasting.com/artifacts/org.loginject/loginject-hk2/
implementation 'org.loginject:loginject-hk2:1.1.0'
// https://jarcasting.com/artifacts/org.loginject/loginject-hk2/
implementation ("org.loginject:loginject-hk2:1.1.0")
'org.loginject:loginject-hk2:jar:1.1.0'
<dependency org="org.loginject" name="loginject-hk2" rev="1.1.0">
  <artifact name="loginject-hk2" type="jar" />
</dependency>
@Grapes(
@Grab(group='org.loginject', module='loginject-hk2', version='1.1.0')
)
libraryDependencies += "org.loginject" % "loginject-hk2" % "1.1.0"
[org.loginject/loginject-hk2 "1.1.0"]

Dependencies

compile (3)

Group / Artifact Type Version
org.loginject : loginject-api jar 1.1.0
org.glassfish.hk2 : hk2 jar 2.4.0
javax.inject : javax.inject jar 1

test (1)

Group / Artifact Type Version
junit : junit jar 4.12

Project Modules

There are no modules declared in this project.

loginject   Build Status Maven Central

The loginject library facilitates dependency injection for loggers.

Nowadays, many Java-based software systems use JSR-330 dependency injection provided by Spring, Guice, HK2 or Dagger to achieve loose coupling between classes. Strangely though, when it comes to loggers, we still mostly see code like this:

    private Logger logger = Logger.getLogger(getClass());

or, even worse:

    private static Logger logger = Logger.getLogger(MyClass.class);

This can be observed even in projects that otherwise use dependency injection very consistently. Instead of manually obtaining a logger instance, why aren't loggers just injected like this?

    @Inject
    private Logger logger;

The answer, of course, is that loggers differ from other injected dependencies in a very fundamental way: the logger object that needs to be injected is different for every class, or, in other words, it depends on the injection point. Context-aware injection is not a very well supported concept for most JSR-330 implementations. For example, a singleton binding will inject the same object at each injection point, or a per-lookup binding will create a new object every time, but it is not possible to inject specific objects based on where they are injected. Also, there can only be one binding per logger type, so how could a binding describe that two different classes need to be injected with two different loggers?

loginject solves this exact problem, in a way that is logger-agnostic and supports different dependency injection systems through a common API.

To inject a logger, you can use standard JSR-330 annotations:

class A
{
    @Inject
    private Logger logger; // will inject a logger specific to class A
}

class B
{
    @Inject
    private Logger logger; // will inject a logger specific to class B
}

You also need to provide loginject with some more detailed instructions as to what logger should be injected. For example, if you are writing a binder for the HK2 dependency injection framework you could do it like this:

import java.util.logging.Logger;
import org.glassfish.hk2.utilities.Binder;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import static org.loginject.LogInject.*;
import static org.loginject.LogParameter.*;

public class HK2LogBinder extends AbstractBinder
{
    @Override
    protected void configure()
    {
        install(loginject(Logger::getLogger, currentClassName()).as(Binder.class));
    }
}

The install(...) refers to the standard method from HK2's AbstractBinder. The loginject(Logger::getLogger, currentClassName()) expression provides a generic logger binding for a java.util.logging.Logger that is obtained via Logger.getLogger(...) for the class into which it is injected. Note that currentClassName() is not the current class in which the expression appears (which would be HK2LogBinder in this case) but a placeholder for the class into which the logger will be injected. At injection time, this placeholder will evaluate to different classes for different injection points. Finally, by specifying as(Binder.class) you are telling loginject that you need a binding for HK2. If you were using Guice you would be using as(Module.class) instead.

FAQ

What Maven dependencies do I need for using loginject?

Assuming that you already have all the necessary dependencies for your dependency injection framework (e.g., Spring, Guice) and for your logging framework (e.g., Log4J, SLF4J), you will need two additional dependencies for loginject. You will always need the common loginject API (org.loginject:loginject-api), but you will also need its specific implementation that supports the dependency injection framework you use. For example, for using loginject in conjunction with Guice, you would add the following dependencies:

  <dependency>
   <groupId>org.loginject</groupId>
   <artifactId>loginject-api</artifactId>
   <version>1.1.0</version>
  </dependency>
  <dependency>
   <groupId>org.loginject</groupId>
   <artifactId>loginject-guice</artifactId>
   <version>1.1.0</version>
   <scope>runtime</scope>
  </dependency>

Note that the second dependency is always strictly a runtime dependency: you don't need it to compile your code, but you do need it to actually run your code. If you are using a dependency management system other than Maven (e.g., Gradle or SBT), please use the same dependency coordinates in conjunction with the appropriate syntax.

How can I use loginject with Spring?

In addition to org.loginject:loginject-api, you need to also include org.loginject:loginject-spring:

  <dependency>
   <groupId>org.loginject</groupId>
   <artifactId>loginject-spring</artifactId>
   <version>1.1.0</version>
   <scope>runtime</scope>
  </dependency>

In your @Configuration class, you need to add the loginject bindings as a BeanFactoryPostProcessor:

...
import java.util.logging.Logger;
import static org.loginject.LogInject.loginject;
import static org.loginject.LogParameter.currentClassName;
...

    @Bean
    static BeanFactoryPostProcessor injectLogger()
    {
        return loginject(Logger::getLogger, currentClassName()).as(BeanFactoryPostProcessor.class);
    }
...

How can I use loginject with Guice?

The necessary runtime and compile-time Maven dependencies can be found in the earlier section "What Maven dependencies do I need for using loginject".

At a code level, loginject is used as a Guice Module:

        Module module = loginject(LogManager::getLogger, currentClass()).as(Module.class);
        Injector injector = Guice.createInjector(module);

The module can also be combined with other pre-existing Guice modules.

What is new in loginject 1.1.0?

Several bugs and limitations were addressed in loginject 1.1.0:

  • Issue #17 - Spring Injection Service breaks resolution of other Spring dependencies
  • Issue #18 - Upgrade org.springframework:spring-core to the latest version
  • Issue #19 - Make LogInjectionService public

Versions

Version
1.1.0
1.0.0