Resumo Este artigo discutirá com você como combinar o poder das anotações e aspectos para fornecer serviços declarativos às empresas de maneira compatível com o EJB 3.0, enquanto ainda fornece independência de contêineres.
1. Introdução
Em nossa busca conjunta de maneiras de melhorar ainda mais o desempenho da produção do desenvolvimento de software, nós - como membros da comunidade Java - geralmente recorrem ao J2EE para fornecer questões técnicas mais desafiadoras no desenvolvimento da empresa, como gerenciamento de transações distribuídas, concorrência e soluções de distribuição de objetos. A ideologia orientadora por trás dele - esses serviços corporativos complexos podem ser implementados pelos fornecedores de servidores de aplicativos e equilibrados por desenvolvedores comerciais - é realmente uma boa idéia. O J2EE, especificamente o EJB, forneceu com sucesso uma plataforma na qual os aplicativos Java Enterprise são criados.
Parte desse sucesso se deve à capacidade de executar a programação declarativa - uma maneira de desenvolver um programa - no qual você pode declarar serviços de infraestrutura em vez de codificar explicitamente com a lógica de negócios para que o código seja espalhado por toda parte. O EJB provou o valor dessa abordagem de programação - permitindo que problemas corporativos, como transações e segurança, sejam declarados com um descritor de publicação e tratados por um contêiner.
No entanto, nos últimos anos, mais e mais desenvolvedores perceberam que o EJB traz muitos novos desafios na produtividade de uma equipe - cada EJB deve ser acompanhado por várias interfaces, com uma descrição do descritor de publicação, acessada via JNDI, etc. O teste de unidade no EJB fora do contêiner também traz dificuldades adicionais.
Observe que, para ler este artigo, você precisa ter as seguintes ferramentas:
· Java 2 SDK 1.5
· Maven 2.0 beta 2
O objetivo do EJB 3.0 é facilitar o desenvolvimento da empresa nos seguintes aspectos:
· Implementar solicitações declarativas de serviços corporativos introduzindo anotações de metadados
· Implementar injeção de dependência/recurso por anotação
· Implementar a dissociação de feijões corporativos e interfaces específicas do EJB
· Simplificação do armazenamento contínuo por meio de mapeamento leve-relacional de objetos
É como uma brisa de primavera para desenvolvedores de EJB - eles estão trabalhando duro para desenvolver, testar e manter o EJB. Agora é fácil escrever um feijão corporativo usando o EJB 3.0, assim como criar um pojo (objeto Java tradicional) com anotações específicas para marcá -lo como um EJB e solicitar serviços corporativos. Aqui está um exemplo do EJB no EJB 3.0 Public Draft:
@stado
Public Class CartBean implementa ShoppingCart
{
Total de flutuação privada;
Códigos de produtos vetoriais privados;
public int SomeshoppingMethod () {...};
...
}
A instrução EJB 3.0 afirma essencialmente que o que os desenvolvedores precisam não é uma solução pesada, "uma liberação satisfazendo tudo", mas uma solução leve e fácil de usar-fornecendo aos desenvolvedores uma certa gama de serviços corporativos. Para esse fim, um dos métodos mais importantes fornecidos pelo EJB 3.0 é dissociar feijões corporativos e APIs EJB. E esta solução também traz derivados interessantes - o EJB agora pode ser executado não apenas em diferentes contêineres EJB, mas também dentro de qualquer estrutura de aplicativos - essas estruturas devem ser capazes de reconhecer o EJB 3.0 (JSR 220) e a anotação normal para declarar serviços corporativos (JSR 250) .
Este artigo não fornece uma exploração aprofundada da programação declarativa, EJBS, aspectos ou anotações. Em vez disso, basta analisar as inter -relações entre essas tecnologias e discutir como combiná -las de uma nova maneira para simplificar o desenvolvimento de aplicativos.
Neste artigo, você aprenderá como escrever um feijão compatível com EJB 3.0 e criará vários aspectos simples para torná -lo gerenciamento declarativo de transações, segurança e injeção de recursos. Espero que você possa se beneficiar deste exercício:
· Três aplicações práticas no aprendizado (injeção de dependência, segurança e transação).
· Familiarizado com o EJB 3.0 e as idéias por trás disso.
Reconheça como implementar a dissociação do EJB de APIs específicas para permitir que os serviços compatíveis com EJB 3.0 sejam implementados com apenas leve e não fornecido pelo EJB.
2. EXEMPLE
Ao longo da discussão, você aprenderá sobre uma implementação de um sistema de pedidos de vôo - que usa aspectos e anotações para implementar a injeção de dependência, a segurança e o gerenciamento de transações. O aplicativo executa apenas duas funções: permite que os usuários pesquisem voos (Figura 1) e, em seguida, solicite uma viagem (Figura 2). Ambas as operações serão processadas com segurança para permitir que apenas os usuários identificados as executem. Além disso, como a operação de "viagens de ordem" envolve encomendar dois voos (fora e de retorno), a operação precisa ser criada como transacional - por exemplo, ambos os pedidos terão sucesso ou falharão como uma unidade de trabalho.
Figura 1. Consulta de voo: Primeiro, os usuários procuram voos que atendam aos critérios especificados.
Figura 2. Pedido de voo: Em seguida, o usuário ordena um voo de saída e um voo de volta. Ambos os pedidos são bem -sucedidos ou falham.
Este aplicativo Web simples contém vários servlets, uma aparência de serviço e uma camada DAO (veja a Figura 3).
Preocupações cruzadas, como configuração de recursos, segurança e gerenciamento de transações, serão fornecidas por aspectos (implementados com o aspecto 1.5 m3) para implementar o comportamento da injeção declarado nas anotações Java 5.
Figura 3. Arquitetura do sistema de pedidos de voo: Este sistema de pedidos de vôo consiste em três componentes principais - eles se juntam para concluir as solicitações de usuário. 3. Injeção de recursos
A declaração EJB 3.0 PROJETO permite que os recursos sejam declarados através da anotação @Resource (esta decisão é definida na declaração de anotação ordinária) e injetada no seu EJB por contêiner. A injeção de dependência é uma técnica - usando essa técnica, uma entidade fora de um objeto em vez de uma entidade criada explicitamente para esse objeto pode fornecer (injetar) a dependência de um objeto. Às vezes, é descrito como um princípio de Hollywood - que piadas como "Não nos chame, nós ligaremos para você".
Pegue a classe TravelAGencyServiceImpl como um exemplo - para armazenar continuamente alguns dados, essa classe precisa encontrar uma implementação da interface iflightdao. Tradicionalmente, isso é alcançado por meio de uma fábrica, singleton, localizador de serviços ou alguma outra solução personalizada. Entre eles, uma solução possível é assim:
Classe pública TravelAgencyServiceImpl implementa ItTravelaGencyService
{
public iflightdao Flightdao;
Public TravelAgencyServiceImpl ()
{Flightdao = FlightDaofactory.getInstance (). Getflightdao ();
Public Void BookTrip (Long Outboundflightid, Long Returnflightid, Int Seats)
lança inválidoSeatsexception
{
Reservassateds (outboundflightid, assentos);
Reservassateds (Returnflightid, assentos);
}
}
Você viu que essa implementação envolve a criação de uma classe de fábrica específica - é provável que as informações de configuração sejam armazenadas em algum lugar para entender como a implementação para criar IflightDao deve ser criada. Se o serviço não estiver criando explicitamente suas dependências injetadas pelo contêiner, os detalhes da configuração e a criação de objetos serão proxiados para o contêiner. Isso permite que os componentes de um aplicativo sejam facilmente conectados juntos - com diferentes configurações e elimina muitos singleton e código de fábrica à moda antiga.
Uma implementação da classe - que depende de uma implementação do IFLightDAO declarado com a anotação de recursos do JSR 250 - pode parecer assim:
Classe pública TravelAgencyServiceImpl implementa ItTravelaGencyService
{
@Resource (name = "Flightdao")
public iflightdao Flightdao;
Public Void BookTrip (Long Outboundflightid, Long Returnflightid, Int Seats)
lança inválidoSeatsexception
{
Reservassateds (outboundflightid, assentos);
Reservassateds (Returnflightid, assentos);
}
}
Nesse caso, o contêiner fornecerá a implementação correta de um recurso chamado "Flightdao" para a classe de serviço. Mas e se você quiser aproveitar a injeção de recursos agora, em vez de esperar a versão EJB 3.0? OK, você pode tomar um contêiner leve - ele é capaz de fornecer injeções de dependência, como o recipiente de mola ou pico. No entanto, não entendo que existe um contêiner leve - ele é capaz de usar anotações de recursos JSR 250 para especificar requisitos de injeção (embora eu esteja ansioso por alguns nesse sentido).
Uma solução é usar aspectos para implementar a injeção de dependência. Se você usar a anotação @Resource para isso, sua implementação será consistente com o EJB 3.0 Way e a Forward compatível com a implementação EJB 3.0 - e isso não é uma coisa muito difícil de implementar. A lista a seguir mostra um aspecto criado com os aspectos - injeta campos anotados com @Resource Anotation:
@aspecto
injeção de classe pública
{
Gerenciador privado dependencyManager = new DependencyManager ();
@Before ("Get (@Resource * *. *)")
Public Void BeforeFieldAccesss (Junção ThisJoinPoint)
lança ilegalArgumentException, ilegalaraccessException
{
assinatura de fieldsignature = (fieldsignature) thisJoinPoint.getSignature ();
Recurso injectannotation = Signature.getfield (). GetAnnotation (Resource.class);
objeto dependência = gerenciador.Resolve dependência (Signature.getfieldType (), injectannotation.name ());
Signature.getfield (). set (thisJoinPoint.getThis (), dependência);
}
}
Todo esse aspecto simples faz é consultar a classe de implementação de um arquivo de propriedade (essa lógica é encapsulada no objeto DependEncyManager) e injete -a nos campos anotados com a anotação @Resource antes de acessar o campo. Obviamente, essa implementação não está completa, mas ilustra como você pode fornecer injeção de recursos de uma maneira compatível com JSR 250 sem EJB.
4. Segurança
Além da injeção de recursos, o JSR 250 e o EJB 3.0 também fornecem uma representação segura de metadados por anotação. O pacote javax.annotation.security define cinco anotações - Runas, Roleswed, Permitall, Denyall e Rolescida Referenciado - todos os quais podem ser aplicados a métodos para definir os requisitos de segurança. Por exemplo, se você deseja declarar que o método do Livroflight listado acima só pode ser executado pelos chamadores com a função "Usuário", você pode anotar esse método com as seguintes restrições de segurança:
Classe pública TravelAgencyServiceImpl implementa ItTravelaGencyService
{
@Resource (name = "Flightdao")
public iflightdao Flightdao;
@RolesClowed ("Usuário")
Public Void BookTrip (Long Outboundflightid, Long Returnflightid, Int Seats)
lança inválidoSeatsexception
{
Reservassateds (outboundflightid, assentos);
Reservassateds (Returnflightid, assentos);
}
}
Esta anotação afirmará que o contêiner é responsável por garantir que apenas o chamador da função especificado possa executar esse método. Então agora vou mostrar outro aspecto simples - ele fortalecerá ainda mais as restrições de segurança no aplicativo:
@aspecto
Public Class SecurityAspec
{
@around ("Execução (@javax.annotation.security.rolesallowed * *. *(..))")
Objeto público em torno dos métodos (prosseguir
lances lançáveis
{
boolean calanerauthorized = false;
ROLES PERALEDED ROUSALLOWED = ROLES OLLABLEDFORJOINPOINT (thisJoinPoint);
para (Função da String: Rolescida.Value ())
{
if (CallerinRole (função))
{callerauthorized = true;
}
se (CHANERAUTORIDED)
{return thisJoinPoint.Proced ();
outro
{
lançar a nova RunTimeException ("Caller não autorizado a executar a função especificada");
}
}
ROLOS PRIVADOS ROUNSALLOWEDFORJOINPOINT (ProceedingJoinPoint ThisJoinPoint)
{
MethodSignature MethodSignature = (MethodSignature) thisJoinPoint.getSignature ();
Método TargetMethod = MethodSignature.getMethod ();
return TargetMethod.getAnnotation (Rolescida.class);
}
private boolean callerinRole (função de string)
{...}
}
Esse aspecto inclui a execução de todos os métodos - verificando que o chamador é uma das funções especificadas na anotação, anote com a anotação @Rolescabeded e garantindo que o chamador esteja autorizado a chamar o método. Obviamente, você também pode usar qualquer algoritmo que deseja autorizar e recuperar seu papel, como JAAS ou uma solução personalizada. Neste programa de exemplo, por conveniência, optei por procurar o contêiner do servlet.
V. Assuntos
As transações se tornam uma parte importante do desenvolvimento da empresa - porque facilitam a integração de dados em um ambiente simultâneo. De um nível alto, as transações podem garantir isso através de operações múltiplas ou completas ou incompletas.
Ao contrário das anotações de injeção e segurança de recursos, as anotações para transações são específicas para o EJB 3.0 e não são definidas nas anotações normais do JSR 250. O EJB 3.0 define duas anotações associadas a transações: TransactionManagement e transactionAttribute. A anotação do TransactionManager especifica se a transação é gerenciada pelo contêiner ou pelo feijão. No EJB 3, se esta anotação não for especificada, a transação gerenciada pelo contêiner será usada. A anotação transactionAttribute é usada para especificar o nível de propagação da transação do método. Valores válidos - incluindo obrigatórios, exigidos, necessários novos, suportados, não suportados e nunca suportados - são usados para definir se é necessária uma transação existente ou uma nova transação é iniciada etc.
Como a operação do Bookflight consiste em duas etapas - solicitando um voo de saída e um voo de volta, empacotando -o em uma transação, você pode garantir a consistência da operação. Ao usar anotações de transação EJB 3.0, isso será assim:
Classe pública TravelAgencyServiceImpl implementa ItTravelaGencyService
{
@Resource (name = "Flightdao")
public iflightdao Flightdao;
@RolesClowed ("Usuário")
@TransactionAttribute (transactionAttributEType.Required)
Public Void BookTrip (Long Outboundflightid, Long Returnflightid, Int Seats)
lança inválidoSeatsexception
{
Reservassateds (outboundflightid, assentos);
Reservassateds (Returnflightid, assentos);
}
}
E você pode aplicar um aspecto simples para definir automaticamente os limites da transação:
@aspecto
transação de classe públicaAspect
{
@PointCut ("Execução (@javax.ejb.transactionAttribute * *. *(..))")
public void transactionalmethods () {}
@Before ("transactionalMethods ()")
public void beforetransactionMethods ()
{hibernateutil.begintransaction ();
@afterReturning ("transactionalmethods ()")
Public Void After RecurningTransactionMethods ()
{Hibernateutil.Committransaction ();
@afterwrowing ("transactionalmethods ()")
public void depois de arremesso de cálculos ()
{hibernateutil.rollbacktransaction ();
}
Essa implementação é baseada no pressuposto de que o padrão local de hibernação e threads onipresentes são usados para gerenciar sessões de hibernação e objetos de transação;
6. Resumo
Ao usar os conjuntos de anotações EJB 3.0 e JSR 250, este artigo mostrou como preocupações cruzadas, como gerenciamento de recursos, segurança e transações, são implementadas como aspectos. Obviamente, existem muitos outros conteúdos que precisamos aprender mais. A primeira coisa a aprender é o plano fornecido pelas preocupações cruzadas modulares, implementando esses aspectos de exemplo usando as aspectos. Em segundo lugar, vimos algumas novas idéias e conceitos por trás da declaração EJB 3.0 que agora está surgindo. Finalmente, também vemos de uma maneira dramática a liberdade que deve ser fornecida para dissociar nossos objetos de negócios da API EJB. Neste ponto, tudo o que você deseja tornar o TravelAgencyServiceImpl uma sessão sem estado é fazer é adicionar uma última nota:
@stado
Classe pública TravelAgencyServiceImpl implementa ItTravelaGencyService
{...}
Finalmente, espero realmente que essa abordagem gratuita para fornecer serviços corporativos traga concorrência e inovação no setor de estrutura/contêiner.