Quero saber o que realmente acontece quando você anota um método com @Transactional
? Obviamente, eu sei que o Spring envolverá esse método em uma transação.
Mas tenho as seguintes dúvidas:
- Ouvi dizer que o Spring cria uma classe proxy ? Alguém pode explicar isso com mais profundidade . O que realmente reside nessa classe de proxy? O que acontece com a classe real? E como posso ver a classe proxy criada pelo Spring
- Também li nos documentos do Spring que:
Nota: Como esse mecanismo é baseado em proxies, apenas as chamadas de método 'externas' que chegam pelo proxy serão interceptadas . Isso significa que a 'auto-invocação', ou seja, um método dentro do objeto de destino que chama outro método do objeto de destino, não levará a uma transação real em tempo de execução, mesmo que o método invocado esteja marcado com
@Transactional
!
Fonte: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Por que apenas chamadas de método externo estarão em Transação e não os métodos de auto-invocação?
Respostas:
Este é um grande tópico. O documento de referência do Spring dedica vários capítulos a ele. Eu recomendo a leitura sobre Programação e transações orientadas a aspectos , pois o suporte a transações declarativas do Spring usa o AOP em sua base.
Mas em um nível muito alto, o Spring cria proxies para classes que declaram @Transactional na própria classe ou em membros. O proxy é quase invisível no tempo de execução. Ele fornece uma maneira de o Spring injetar comportamentos antes, depois ou em torno das chamadas de método para o objeto que está sendo proxy. O gerenciamento de transações é apenas um exemplo dos comportamentos que podem ser conectados. As verificações de segurança são outro. E você também pode fornecer o seu próprio para coisas como registro em log. Então, quando você anota um método com @Transactional , o Spring cria dinamicamente um proxy que implementa as mesmas interfaces que a classe que você está anotando. E quando os clientes fazem chamadas para o seu objeto, as chamadas são interceptadas e os comportamentos injetados pelo mecanismo de proxy.
As transações no EJB funcionam da mesma forma, a propósito.
Como você observou, o mecanismo de proxy funciona apenas quando as chamadas são recebidas de algum objeto externo. Quando você faz uma chamada interna dentro do objeto, está realmente fazendo uma chamada através da referência " this ", que ignora o proxy. Existem maneiras de contornar esse problema, no entanto. Eu explico uma abordagem nesta postagem do fórum em que eu uso um BeanFactoryPostProcessor para injetar uma instância do proxy em classes de "auto-referência" em tempo de execução. Salvei essa referência em uma variável de membro chamada " eu ". Então, se eu precisar fazer chamadas internas que exijam uma alteração no status da transação do encadeamento, direciono a chamada através do proxy (por exemplo, " me.someMethod ()".) A postagem do fórum explica com mais detalhes. Observe que o código BeanFactoryPostProcessor seria um pouco diferente agora, como foi escrito no período de tempo do Spring 1.x. Mas espero que tenha uma idéia. Tenho uma versão atualizada que Eu provavelmente poderia disponibilizar.
fonte
BeanFactoryPostProcessor
. No entanto, existe um método (na minha opinião) muito semelhante descrito nesta resposta: stackoverflow.com/a/11277899/3667003 ... e outras soluções em todo o segmento também.Quando o Spring carrega suas definições de bean e é configurado para procurar
@Transactional
anotações, ele cria esses objetos proxy em torno de seu bean real . Esses objetos de proxy são instâncias de classes geradas automaticamente em tempo de execução. O comportamento padrão desses objetos proxy quando um método é chamado é apenas para invocar o mesmo método no bean "target" (ou seja, seu bean).No entanto, os proxies também podem ser fornecidos com interceptores e, quando presentes, esses interceptores serão chamados pelo proxy antes de invocar o método do seu bean de destino. Para os beans de destino anotados com
@Transactional
, o Spring criará umTransactionInterceptor
e o passará para o objeto proxy gerado. Portanto, quando você chama o método a partir do código do cliente, está chamando o método no objeto proxy, que primeiro chama oTransactionInterceptor
(que inicia uma transação), que por sua vez chama o método no seu bean de destino. Quando a chamada termina, aTransactionInterceptor
consolidação / reverte a transação. É transparente para o código do cliente.Quanto à coisa "método externo", se o seu bean chamar um de seus próprios métodos, ele não o fará através do proxy. Lembre-se, o Spring envolve seu bean no proxy, seu bean não o conhece. Somente chamadas de "fora" do seu bean passam pelo proxy.
Isso ajuda?
fonte
Como pessoa visual, eu gosto de pesar com um diagrama de seqüência do padrão de proxy. Se você não sabe ler as setas, eu li a primeira assim:
Client
executaProxy.method()
.(Pude postar a foto com a condição de mencionar suas origens. Autor: Noel Vaes, site: www.noelvaes.eu)
fonte
A resposta mais simples é:
Em qualquer método, você declara que
@Transactional
o limite da transação é iniciado e o limite termina quando o método é concluído.Se você estiver usando a chamada JPA, todas as confirmações estarão nesse limite de transação .
Digamos que você esteja salvando entidade1, entidade2 e entidade3. Agora, ao salvar a entidade3 , ocorre uma exceção ; então, como enitiy1 e entity2 entram na mesma transação, a entidade1 e a entidade2 serão revertidas com a entidade3.
Transação:
Qualquer exceção resultará na reversão de todas as transações JPA com DB. As transações JPA internamente são usadas pelo Spring.
fonte
Pode ser tarde, mas me deparei com algo que explica sua preocupação com o proxy (apenas chamadas de método 'externas' que chegam pelo proxy serão interceptadas) muito bem.
Por exemplo, você tem uma classe que se parece com isso
e você tem um aspecto que se parece com isso:
Quando você o executa assim:
}
Resultados da chamada do kickOff acima, conforme o código acima.
mas quando você altera seu código para
Veja bem, o método chama internamente outro método para que não seja interceptado e a saída fique assim:
Você pode ignorar isso fazendo isso
Trechos de código obtidos em: https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/
fonte
Todas as respostas existentes estão corretas, mas acho que não posso dar apenas esse tópico complexo.
Para uma explicação prática e abrangente, você pode dar uma olhada neste guia do Spring @Transactional In-Depth , que tenta o melhor para cobrir o gerenciamento de transações em ~ 4000 palavras simples, com muitos exemplos de código.
fonte