Onde usar o EJB 3.1 e o CDI?

120

Estou criando um produto baseado em Java EE no qual estou usando o GlassFish 3 e o EJB 3.1.

Meu aplicativo possui beans de sessão , um planejador e usa serviços da web. Recentemente, conheci o Apache TomEE , que oferece suporte a Contextos e Injeção de Dependência (CDI) . O contêiner GlassFish também suporta CDI.

Posso substituir os beans de sessão nos quais não preciso de nenhum recurso que o CDI também ainda não fornece? E se sim, quais são os benefícios que posso obter?

Dhrumil Shah
fonte

Respostas:

408

Sim, você pode misturar livremente CDI e EJB e obter ótimos resultados. Parece que você está usando @WebServicee @Schedule, que são boas razões para adicionar EJB ao mix.

Há muita confusão por aí, então aqui estão algumas informações gerais sobre EJB e CDI, pois elas se relacionam entre si.

EJB> = CDI

Observe que os EJBs são beans CDI e, portanto, têm todos os benefícios do CDI. O inverso ainda não é verdadeiro. Portanto, definitivamente, não adquira o hábito de pensar "EJB vs CDI", pois essa lógica realmente se traduz em "EJB + CDI vs CDI", que é uma equação ímpar.

Nas versões futuras do Java EE, continuaremos alinhando-os. O que significa alinhar está permitindo que as pessoas façam o que eles já podem fazer, apenas sem o @Stateful, @Statelessou @Singletonanotação no topo.

EJB e CDI em termos de implementação

Por fim, o EJB e o CDI compartilham o mesmo design fundamental de serem componentes de proxy. Quando você obtém uma referência a um bean EJB ou CDI, não é o bean real. Em vez disso, o objeto que você recebe é falso (um proxy). Quando você invoca um método nesse objeto falso, a chamada vai para o contêiner que enviará a chamada por interceptadores, decoradores etc., além de cuidar de qualquer transação ou verificação de segurança. Feito tudo isso, a chamada finalmente vai para o objeto real e o resultado é passado de volta pelo proxy para o chamador.

A diferença ocorre apenas em como o objeto a ser chamado é resolvido. Por "resolvido", queremos dizer simplesmente onde e como o contêiner procura a instância real para invocar.

No CDI, o contêiner procura um "escopo", que basicamente será um hashmap que dura um período específico de tempo (por solicitação @RequestScoped, por sessão HTTP @SessionScoped, por aplicativo @ApplicationScoped, conversação JSF @ConversationScopedou por sua implementação de escopo personalizado).

No EJB, o contêiner também pesquisará um hashmap se o bean for do tipo @Stateful. Um @Statefulbean também pode usar qualquer uma das anotações de escopo acima, fazendo com que ele viva e morra com todos os outros beans no escopo. No EJB @Statefulé essencialmente o bean "qualquer escopo". O @Statelessbasicamente é um pool de instâncias - você obtém uma instância do pool pela duração de uma chamada. O @Singletoné essencialmente@ApplicationScoped

Portanto, em um nível fundamental, qualquer coisa que você possa fazer com um bean "EJB", deverá poder fazer com um bean "CDI". Sob as cobertas, é muito difícil diferenciá-los. Todo o encanamento é o mesmo, com exceção de como as instâncias são resolvidas.

Atualmente, eles não são os mesmos em termos de serviços que o contêiner oferecerá ao fazer esse proxy, mas como eu disse, estamos trabalhando nele no nível de especificação do Java EE.

Nota de desempenho

Desconsidere qualquer imagem mental "leve" ou "pesada" que você possa ter. Isso é tudo de marketing. Eles têm o mesmo design interno para a maior parte. A resolução da instância CDI é talvez um pouco mais complexa porque é um pouco mais dinâmica e contextual. A resolução da instância EJB é relativamente estática, burra e simples em comparação.

Posso dizer a você, do ponto de vista da implementação no TomEE, que há uma diferença de desempenho zero entre invocar um EJB e invocar um CDI bean.

Padrão para POJOs, CDI e EJB

Obviamente, não use CDI ou EJB quando não houver benefício. Coloque o CDI quando começar a querer injeção, eventos, interceptadores, decoradores, rastreamento do ciclo de vida e coisas assim. Essa é a maior parte do tempo.

Além desses princípios, há uma série de serviços de contêiner úteis que você só tem a opção de usar se você fizer seu bean CDI também um EJB, adicionando @Stateful, @Statelessou @Singletonsobre ele.

Aqui está uma pequena lista de quando eu desmembrar os EJBs.

Usando JAX-WS

Expondo um JAX-WS @WebService. Sou preguiçosa. Quando @WebServicetambém é um EJB, você não precisa listá-lo e mapeá-lo como um servlet no web.xmlarquivo. Isso é trabalho para mim. Além disso, tenho a opção de usar qualquer outra funcionalidade mencionada abaixo. Portanto, é um acéfalo para mim.

Disponível para @Statelesse @Singletonsomente.

Usando JAX-RS

Expondo um recurso JAX-RS via @Path. Eu ainda sou preguiçoso. Quando o serviço RESTful também é um EJB, você obtém novamente a descoberta automática e não precisa adicioná-lo a uma Applicationsubclasse JAX-RS ou algo assim. Além disso, posso expor exatamente o mesmo bean que um @WebServicese eu quiser ou usar qualquer uma das excelentes funcionalidades mencionadas abaixo.

Disponível para @Statelesse @Singletonsomente.

Lógica de inicialização

Carregar na inicialização via @Startup. Atualmente, não há equivalente a isso no CDI. De alguma forma, perdemos a adição de algo como um AfterStartupevento no ciclo de vida do contêiner. Se tivéssemos feito isso, você simplesmente poderia ter um @ApplicationScopedbean que o ouviu e que seria efetivamente o mesmo que um @Singletoncom @Startup. Está na lista do CDI 1.1.

Disponível @Singletonapenas para .

Trabalhando em Paralelo

@Asynchronousinvocação de método. Iniciar threads é um não-não em qualquer ambiente do lado do servidor. Ter muitos threads é um sério problema de desempenho. Essa anotação permite paralelizar as coisas que você faz usando o conjunto de encadeamentos do contêiner. Isso é incrível.

Disponível para @Stateful, @Statelesse @Singleton.

Agendamento de trabalho

@Scheduleou ScheduleExpressioné basicamente um cron ou Quartzfuncionalidade. Também é incrível. A maioria dos contêineres usa apenas quartzo sob as tampas para isso. A maioria das pessoas não sabe, no entanto, que o trabalho de agendamento no Java EE é transacional! Se você atualizar um banco de dados, agende algum trabalho e um deles falhará, ambos serão limpos automaticamente. Se a EntityManagerchamada persistir falhar ou houver um problema na liberação, não há necessidade de cancelar o agendamento do trabalho. Yay, transações.

Disponível para @Statelesse @Singletonsomente.

Usando EntityManagers em uma transação JTA

A nota acima sobre transações, é claro, exige que você use um JTAgerenciado EntityManager. Você pode usá-los com "CDI" simples, mas sem as transações gerenciadas por contêiner, pode ser realmente monótono duplicar a UserTransactionlógica de confirmação / reversão.

Disponível para todos os componentes Java EE, incluindo CDI, JSF @ManagedBean, @WebServlet, @WebListener, @WebFilter, etc. A @TransactionAttributeanotação, no entanto, está disponível para @Stateful, @Statelesse @Singletonúnica.

Mantendo o JTA gerenciado EntityManager

O EXTENDEDgerenciado EntityManagerpermite manter uma EntityManagerabertura entre as JTAtransações e não perder os dados em cache. Bom recurso para a hora e o local certos. Use com responsabilidade :)

Disponível @Statefulapenas para .

Fácil sincronização

Quando você precisa de sincronização, as anotações @Lock(READ)e @Lock(WRITE)são excelentes. Ele permite que você obtenha gerenciamento de acesso simultâneo gratuitamente. Ignore todo o encanamento ReentrantReadWriteLock. No mesmo depósito @AccessTimeout, você pode dizer quanto tempo um encadeamento deve esperar para obter acesso à instância do bean antes de desistir.

Disponível @Singletonapenas para beans.

David Blevins
fonte
32
Deus do céu David :) Eu acho que você cobriu.
LightGuard
7
Obrigado por esta resposta. Você limpou o entupimento na minha cabeça e conectou muitos pontos.
Thupten
7
Esta é de longe a melhor explicação sobre esse tópico que eu já li. Também abrange quase todos os aspectos importantes do EJB no uso na vida real. Ótimo trabalho!!
Nanoquack
3
Muito compreensível e Adam não está errado em termos legais estritos, mas a distinção é discutível. A especificação diz que a instância do EJB não é contextual, mas depois diz que a referência (proxy) ao EJB é contextual. O ciclo de vida de um bean com estado é controlado inteiramente por meio da referência (proxy), portanto, quando o contêiner CDI está controlando essa referência (proxy), a matemática é a mesma - os EJBs com estado podem ser efetivamente contextuais.
David Blevins
3
Você escreveu isso no seu horário de almoço no TESLA?
Edison
2

se você realmente não está usando nenhum dos recursos do ejb 3.1, a resposta é simples. mas suponha que sua pergunta indique que você suspeita que existem conceitos do ejb 3.1 dos quais você está se beneficiando sem estar ciente deles. um exemplo pode ser que o contêiner possa manter um conjunto de slsb pronto para ser usado, para que jms e conexões com o banco de dados não precisem ser injetados como parte da solicitação

Aksel Willgert
fonte