Java CDI Неверная зависимость DependsOn

Я пытаюсь обновить некоторый контент в базе данных при запуске приложения. Для этого я создал класс A, который будет это делать.

@Singleton
@Startup
@DependsOn({ "B", "C" })
public class A{
    @Inject B b;
    @Inject C c; 
    ...
}

B — это класс, который считывает некоторые значения конфигурации из базы данных. Б:

@Singleton
@Startup
public class B {
     @PersistenceContext
     EntityManager em; 
     ...
}


C – это древовидная структура, которая использует данные классов D и E для построения правильно отформатированного дерева.
C:

@Singleton
@Startup
@DependsOn({ "D", "E" })
public class C{
     @Inject
     D d;
     @Inject
     E e; 
     ...
 }

D и E являются листовыми одиночками в том смысле, что они не зависят от других одиночек; Они предоставляют данные (которые считываются из файлов в БД):
D:

@Singleton
@Startup
public class D { ... }


E:

@Singleton
@Startup
public class E { ... }

Основываясь на документации по аннотации DependsOn, я предположил, что CDI создаст граф зависимостей для синглтонов и инициализирует их в указанном порядке (B, D и E будут инициализированы до того, как C, и до того, как, наконец, будет инициализирован A). Однако, когда я пытаюсь развернуть приложение, я получаю сообщение об исключении:
Исключение во время обработки жизненного цикла java.lang.RuntimeException: неверная зависимость DependsOn 'C' для EJB ContentUpdater.

Полная трассировка стека:

java.lang.RuntimeException: Invalid DependsOn dependency 'C' for EJB A
at org.glassfish.ejb.deployment.util.EjbBundleValidator.checkDependsOn(EjbBundleValidator.java:602)
at org.glassfish.ejb.deployment.util.EjbBundleValidator.accept(EjbBundleValidator.java:300)
at org.glassfish.ejb.deployment.descriptor.EjbDescriptor.visit(EjbDescriptor.java:2823)
at org.glassfish.ejb.deployment.descriptor.EjbDescriptor.visit(EjbDescriptor.java:2811)
at org.glassfish.ejb.deployment.util.EjbBundleValidator.accept(EjbBundleValidator.java:115)
at com.sun.enterprise.deployment.BundleDescriptor.visit(BundleDescriptor.java:625)
at org.glassfish.ejb.deployment.descriptor.EjbBundleDescriptorImpl.visit(EjbBundleDescriptorImpl.java:757)
at com.sun.enterprise.deployment.util.ApplicationValidator.accept(ApplicationValidator.java:121)
at com.sun.enterprise.deployment.BundleDescriptor.visit(BundleDescriptor.java:625)
at com.sun.enterprise.deployment.archivist.ApplicationFactory.openArchive(ApplicationFactory.java:190)
at org.glassfish.javaee.core.deployment.DolProvider.processDOL(DolProvider.java:203)
at org.glassfish.javaee.core.deployment.DolProvider.load(DolProvider.java:227)
at org.glassfish.javaee.core.deployment.DolProvider.load(DolProvider.java:96)
at com.sun.enterprise.v3.server.ApplicationLifecycle.loadDeployer(ApplicationLifecycle.java:881)
at com.sun.enterprise.v3.server.ApplicationLifecycle.setupContainerInfos(ApplicationLifecycle.java:821)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:377)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:539)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:535)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:360)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:534)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:565)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:557)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:360)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:556)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1464)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1300(CommandRunnerImpl.java:109)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1846)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1722)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:534)
at com.sun.enterprise.v3.admin.AdminAdapter.onMissingResource(AdminAdapter.java:224)
at org.glassfish.grizzly.http.server.StaticHttpHandlerBase.service(StaticHttpHandlerBase.java:189)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
at java.lang.Thread.run(Thread.java:745)

Кто-нибудь знает, почему это исключение возникает с учетом структуры, описанной выше?

Когда я пропускаю класс C в аннотации @DependsOn в классе A, я получаю еще одно исключение, поэтому, к сожалению, это не решение. Приложение развернуто на GlassFish 4.1


person Charles Greiner    schedule 10.06.2016    source источник
comment
@DependsOn — это аннотация EJB, а не CDI. Какой контейнер вы развертываете, включая версию? Можете ли вы также предоставить полную трассировку стека, а не только часть?   -  person John Ament    schedule 10.06.2016
comment
@JohnAment Я добавил полную трассировку стека. Контейнер GlassFish 4.1   -  person Charles Greiner    schedule 13.06.2016


Ответы (2)


Я предлагаю вам переосмыслить свой дизайн и создать только один «StartUpController», который запускает методы инициализации на всех остальных ejb в нужном вам порядке.

@Singleton
public class A {
    public void init() {}
}

@Singleton
public class B {
    public void init() {}
}

@Singleton
public class C {
    public void init() {}
}



@Startup
@Singleton
public class StartUpController {

    @Inject
    private A a;

    @Inject
    private B b;

    @Inject
    private C c;

    @PostConstruct
    protected void setup() {
        a.init();
        b.init();
        c.init();
    }

}

@DependsOn на самом деле не является «элементом управления зависимостями», это просто элемент управления порядком инициализации, и он имеет смысл только вместе с методом @PostConstruct. (я не вижу никаких методов инициализации в вашем примере), поэтому @DependsOn не нужен. Вы можете подключить ejb в цепочку без этой аннотации, посмотрите это и этот пример .

Я не могу точно ответить, почему ваш код не работает, возможно, это просто опечатка. Например, у вас нет C.class в коде, опубликованном выше, вместо этого у вас есть QuestionHandler.class, и снова я не вижу никаких методов @PostConstruct в вашем коде. Другой причиной может быть конфигурация glashfish. Я предлагаю вам попробовать код на сервере wildfly.

person Sergej Samsonow    schedule 10.06.2016
comment
Если я правильно понимаю, вы говорите, что в основном самостоятельно разрешаете зависимости между модулями. Конечно, это может решить проблему, но не отвечает на вопрос, почему моя структура проблематична‹br/›Это потому, что фреймворк (EJB, CDI) не может справиться с созданной мной иерархией? - person Charles Greiner; 13.06.2016
comment
@CharlesGreiner У вас нет зависимостей модулей, которые нужно разрешить. Я обновил свой ответ и добавил несколько примеров для лучшего понимания. - person Sergej Samsonow; 13.06.2016
comment
Спасибо за разъяснение вашего ответа. Я реструктурировал свой код в соответствии с вашим предложением, что решает проблему с инициализацией моего приложения. У меня были аннотированные методы @PostConstruct для классов A, B, D и E, но не для C. Скорее всего, это было проблемой. - person Charles Greiner; 14.06.2016
comment
@CharlesGreiner, пожалуйста, не могли бы вы отметить вопрос решенным :) - person Sergej Samsonow; 14.06.2016
comment
извините за позднее принятие ответа. Я потерял эту проблему после того, как решил ее (с вашей помощью). - person Charles Greiner; 05.02.2019

В названии ошибка. Здесь ожидаемый bean-компонент с именем "C" не существует, вместо этого имя bean-компонента Singleton - "QuestionHandler", а также D - дублированный класс с именами Singleton D и E. Это ошибка в вопросе, а не обязательно причина проблемы.

Итак, как же избежать этих «орфографических» ошибок.

Одним из решений может быть использование статических полей для соединения точек.

A:

@Singleton(name = A.BEAN_NAME)
@Startup
@DependsOn({ B.BEAN_NAME, C.BEAN_NAME })
public class A {
    public static final BEAN_NAME = "A";

    @Inject B b;
    @Inject C c; 
    ...
}

B:

@Singleton(name = B.BEAN_NAME)
@Startup
public class B {
    public static final BEAN_NAME = "B";

    @PersistenceContext
    EntityManager em; 
    ...
}

C:

@Singleton(name = QuestionHandler.BEAN_NAME)
@Startup
@DependsOn({ D.BEAN_NAME, E.BEAN_NAME })
public class QuestionHandler {
     public static final BEAN_NAME = "C";
     @Inject
     D d;
     @Inject
     E e;
     ...
}

D:

 @Singleton(name = D.BEAN_NAME)
 @Startup
 public class D { 
     public static final BEAN_NAME = "D";
     ...
 }

E:

 @Singleton(name = E.BEAN_NAME)
 @Startup
 public class E { 
     public static final BEAN_NAME = "E";
     ...
 }
person Martin    schedule 04.02.2019
comment
Вы правы, в вопросе были ошибки. Спасибо, что указали на это. Я изменил вопрос, чтобы исправить их. Кстати, QuestionHandler — это фактическое имя класса C. Я опустил фактические имена, потому что они не добавляют ценности моему вопросу. В реальном коде не было такого несоответствия в именовании. Таким образом, ваше решение не решило проблему, поэтому я не могу принять его как ответ. - person Charles Greiner; 05.02.2019
comment
Мое решение все еще в силе. Вы все еще подвержены орфографическим ошибкам в @DependsOn, и вы также предполагаете, что имя зарегистрированного компонента Singleton будет таким же, как имя класса, что может быть не так. Если у вас есть класс com.packagename.one.A и com.packagename.two.A, у одного из ваших классов не будет ожидаемого имени компонента. - person Martin; 06.02.2019