2017-01-21 3 views
0

Когда я пытаюсь ввести моего производителя Logger в перечисление, я получаю NPE. Как я могу ввести Logger в перечисление?Как вставить регистратор в перечисление

Пример:

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; 
    } 

} 

Этот класс дает мне NPE на log.debug() линии. Когда я удаляю @Inject и раскомментирую линию this.log, она работает.

TestCase выглядит следующим образом:

@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

+1

Я думаю, что причина заключается в использовании CDI впрыснуть Logger и его не работает, потому что ваше перечисление не является bean => ваш логгер остается нулевым. –

+1

Как вы называете перечисление? Для Inject для работы класс, в котором его используется, должен быть созданным прокси-объектом. Я предполагаю, что вы прямо вызываете 'MyEnum.HI.getGreeting()', и в этом случае соответствующая инфраструктура DI не будет знать, что ей нужно вставлять некоторые переменные. Можете ли вы вставить код того, как вы это вызвали? –

+0

@BandiKishore Да, именно так я его использую. Я добавил тестовый файл. Есть ли способ использовать «LoggerProducer» для входа в эту ситуацию? –

ответ

1

Обнаружено решение, которое работает!

Я создал вспомогательный класс следующим образом:

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(); 
2

Эта прямая инъекция не будет работать, поскольку перечислитель является статическим. Вы можете либо создать новый регистратор в вашем перечислений класса

private static final Logger log = Logger.getLogger(Myenum.class.getName()); 
+0

Да. Я это понимаю, и я знаю, что это работает. Вопрос, однако, заключается в том, как пользовательская CDI-инъекция для регистрации в перечислении. –

+0

Вы можете создать внутренний класс LogInjector в своем классе enum и ввести этот логгер в свой внутренний класс. – shubham

+0

Можете ли вы привести мне пример? –

0

Enums не может быть введен, так как они статичны.

Если вам все еще нужна инъекция (вместо создания регистратора), вам необходимо создать статический класс внутри вашего перечисления, который имеет либо установку, либо инсталляцию конструктора. Когда сеттер или конструктор вызываются каркасом DI, возьмите значение, которое оно дает вам, и назначьте его самому статическому значению в перечислении.

Перечислитель может получить к нему доступ по мере необходимости. Однако обратите внимание, что значение будет равно 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; 
     } 
     } 
    } 
+0

Спасибо. Я все еще получаю NPE. Посмотрите мой обновленный вопрос.Что не так? Что вы подразумеваете под «если класс еще не был введен»? –

+0

Ты не сделал то, что я сказал. – john16384

+0

Я действительно не сделал бы это только для регистратора, хотя ... но это ваш код. – john16384

-1

Вы можете создать класс Singleton Service (созданный вашим соответствующим DI Framework - скажем, Spring), Вводят этот журнал внутри этого класса и использовать его в перечислении.

Вот пример кода, который работает. (Заменить Service аннотацию с бобовой тег для этого класса в XML, если вы используете XML способ сделать это. Кроме того, вы можете пренебречь Ломбки @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; 
    } 
} 

сейчас в вашем соответствующем Enum:

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

Я использовал подобный образец для одного из моих классов (весной) и инъекция работала.

Логика:

  1. Сначала мы создаем экземпляр класса (Singleton, например) и ввести необходимую переменную в конструкторе.
  2. Поскольку это вызвано во время инициализации весны (во время запуска приложения), журнал инициализируется, и мы вручную назначаем статическую переменную журнала с инжектированным объектом в конструкторе.

Примечание:

  1. Не забудьте аннотацию Inject в конструкторе Args.
  2. Лучше не предоставлять метод setter для этого объекта журнала. Как статические и общие, мы не хотим, чтобы люди заменяли это значение пост-строительство. (Сделать его окончательным не вариант, как его статический).
+0

Я использую CDI, а не пружину. но я думаю, что понимаю, что вы говорите, и я попробовал. Он по-прежнему дает мне NPE метод getLog(). –

+0

Странно. Если CDI знает об объекте Logger, этот метод должен работать. Как это похоже на любой другой уровень Injection класса. Я не уверен в CDI, но может ли быть какая-то аннотация, например @Service или XML-запись, для создания экземпляра bean для класса StaticService? Может быть, вы можете добавить это и посмотреть. –

+0

Я думаю, вы имеете в виду https://docs.oracle.com/javaee/7/api/javax/ejb/Singleton.html –

Смежные вопросы