如何在枚举中注入记录器
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 中)使用了类似的模式,并且注入成功了。
逻辑:
- 我们首先创建一个 class 的实例(单例)并在构造函数中注入所需的变量。
- 由于在 spring 初始化期间(在应用程序启动期间)调用了它,因此初始化了日志,我们手动将日志的静态变量分配给构造函数中的注入对象。
注:
- 不要忘记构造函数 Args 中的 Inject 注解。
- 最好不要为此日志对象提供 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();
当我尝试在枚举中注入我的 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 中)使用了类似的模式,并且注入成功了。
逻辑:
- 我们首先创建一个 class 的实例(单例)并在构造函数中注入所需的变量。
- 由于在 spring 初始化期间(在应用程序启动期间)调用了它,因此初始化了日志,我们手动将日志的静态变量分配给构造函数中的注入对象。
注:
- 不要忘记构造函数 Args 中的 Inject 注解。
- 最好不要为此日志对象提供 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();