如何在枚举中注入记录器

How to inject a Logger in an enum

当我尝试在枚举中注入我的 Logger 生成器时,我遇到了 NPE。如何在枚举中注入记录器?

示例:

public enum MyEnum {

    HI("Hi there!"),
    HELLO("Hello mister!");

    @Inject
    private Logger log;

    private final String greeting;

    private MyEnum(String greeting) {
        this.greeting = greeting;
//        this.log = LoggerFactory.getLogger(this.getClass());
    }

    public String getGreeting() {
        log.debug("Method getGreeting called");
        return this.greeting;
    }

}

这个 class 在 log.debug() 行给我一个 NPE。当我删除 @Inject 并取消注释 this.log 行时,它起作用了。

测试用例如下所示:

@RunWith(Arquillian.class)
public class CoverKindTest {

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addClass(MyEnum.class)
                .addClass(LoggerProducer.class)
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    public void testEnum() {
        MyEnum myEnum = MyEnum.HI;
        String greeting = myEnum.getGreeting();
        assertThat("Should give the greeting.", greeting, is("Hi there!"));
    }

}

这个问题的完整可测试项目可以在这里找到,MyEnum.class 是原始问题,MyEnum1.class 是没有注入的解决方案(有效,但不是我要找的)和 MyEnum2.class 是建议的答案。

编辑:使用可行的解决方案更新了 GitHub 存储库。 https://github.com/martijnburger/how-to-inject-a-logger-in-an-enum

由于枚举是静态的,因此这种直接注入不起作用。 您可以在枚举 class

中创建一个新的记录器
private static final Logger log = Logger.getLogger(Myenum.class.getName());

无法注入枚举,因为它们是静态的。

如果您仍然想要注入(而不是仅仅创建记录器),那么您需要在您的枚举中创建一个静态 class,它具有 setter 或构造函数注入。当 DI 框架调用 setter 或构造函数时,获取它为您提供的值并将其自己分配给枚举中的静态值。

枚举现在可以根据需要访问它。但请注意,如果您的 class 尚未注入,该值将为 null。

像这样:

  public enum MyEnum {

      HI("Hi there!"),
      HELLO("Hello mister!");

      private static Logger log;

      private final String greeting;

      private MyEnum(String greeting) {
          this.greeting = greeting;
      }

      public String getGreeting() {
          log.debug("Method getGreeting called");
          return this.greeting;
      }

      @Component
      public static class InjectionHelper {
        @Inject
        public InjectionHelper(Logger log) {
          MyEnum.log = log;
        }
      }
  }

您可以创建一个单例服务 class(由您各自的 DI 框架创建 - 例如 Spring),将此日志注入 class 并在您的枚举中使用它。

这是一个有效的示例代码。 (如果您使用 XML 方法,请将 XML 中此 class 的服务注释替换为 bean 标记。您也可以忽略 lombok @Getter 注释并将其替换为静态 getter)

// Service which is actually a Utility class but have DI Managed Beans.
@Service("staticService")
public class StaticService {

    @Getter
    private static Logger log;

    @Inject
    StaticService(Logger log) {
        StaticService.log = log;
    }
}

现在在你对应的枚举中:

public String getGreeting() {
        StaticService.getLog().debug("Method getGreeting called");
        return this.greeting;
    }

我对我的一个 classes(在 Spring 中)使用了类似的模式,并且注入成功了。

逻辑:

  1. 我们首先创建一个 class 的实例(单例)并在构造函数中注入所需的变量。
  2. 由于在 spring 初始化期间(在应用程序启动期间)调用了它,因此初始化了日志,我们手动将日志的静态变量分配给构造函数中的注入对象。

注:

  1. 不要忘记构造函数 Args 中的 Inject 注解。
  2. 最好不要为此日志对象提供 setter 方法。由于它是静态的和共享的,我们不希望人们替换这个值 post-construction。 (使其成为最终版本不是一个选项,因为它是静态的)。

找到有效的解决方案!

我创建了一个助手 class 如下:

public class LoggerHelper {

    private static Logger logger;

    private void injectLogger(@Observes @Initialized(ApplicationScoped.class) Object context, 
            Logger logger) {
        LoggerHelper.logger = logger;
    }

    public static Logger getLogger() {
        return logger;
    }

}

现在我可以使用以下方法在 Enum 中注入记录器:

private final Logger log = LoggerHelper.getLogger();