Este artigo é compartilhado pela Huawei Cloud Community " Spring Master's Road 18 - Understanding Spring AOP da perspectiva da configuração XML ", autor: Zhuan Yeyang__.
1. Spring AOP e proxy dinâmico
1.1 O relacionamento entre Spring AOP e proxy dinâmico
Spring AOP
Implemente programação orientada a aspectos usando proxies dinâmicos como mecanismo principal. Este mecanismo permite Spring
a criação dinâmica de objetos proxy em tempo de execução. Esses objetos proxy envolvem o objeto de destino (ou seja, componente de negócios) para inserir comportamentos adicionais (como verificações de segurança, gerenciamento de transações, registro em log, etc.) antes e depois de chamar os métodos do objeto de destino. .
-
Proxy dinâmico JDK
Spring AOP
: O proxy dinâmico usado por padrão quando o objeto de destino implementa uma ou mais interfacesJDK
.JDK
O proxy dinâmico usa o mecanismo de reflexão para criar um objeto proxy para a interface. Este objeto proxy interceptará todas as chamadas para o método da interface de destino. -
Proxy CGLIB : Se o objeto de destino não implementar nenhuma interface,
Spring AOP
ele voltará a usarCGLIB
a biblioteca para gerar uma subclasse da classe de destino.CGLIB
(Code Generation Library
) é uma biblioteca poderosa de geração de código de alto desempenho que estendeJava
classes em tempo de execução e substitui métodos em subclasses para implementar a interceptação de métodos.
Independentemente do método proxy utilizado, o objetivo é inserir comportamentos adicionais em diferentes estágios de execução do método por meio de notificações definidas por aspectos, sem alterar o código da lógica de negócio original.
1.2 Terminologia básica do AOP
Aspecto : Aspecto é o núcleo da programação orientada a aspectos. É uma construção que modulariza preocupações em várias classes (como registro, gerenciamento de transações, etc.). Um aspecto pode conter vários tipos de conselhos ( Advice
) e um ou mais pontos de corte ( Pointcut
) que definem onde e quando esses conselhos são executados.
Ponto de junção : um ponto de junção representa um local específico durante a execução do programa, Spring AOP
limitando esses locais a chamadas de métodos. Simplificando, um ponto de junção é um ponto onde conselhos de aspecto podem ser inseridos.
Conselho : Conselho define a ação a ser executada pelo aspecto no ponto de conexão. Dependendo do tipo de notificação, essas ações podem ser executadas antes, depois que o método é chamado, depois que um resultado é retornado ou quando uma exceção é lançada. Os tipos de notificação incluem:
- Pré-notificação (
Before advice
): executada antes da execução do método. - Pós-notificação (
After advice
): Executada após a execução do método, independentemente de seus resultados. - Notificação pós-retorno (
After-returning advice
): Executada após o método ser executado com sucesso. - Notificação pós-exceção (
After-throwing advice
): executada após o método lançar uma exceção. - Conselho surround (
Around advice
): executado antes e depois da execução do método, fornecendo controle total sobre as chamadas de método.
Pointcut (Pointcut) : Pointcut é uma expressão. As expressões Pointcut permitem combinar pontos de conexão por meio de nomes de métodos, modificadores de acesso e outras condições, que determinam quais métodos devem ser acionados quando as notificações são executadas.
Objeto alvo : um objeto que é notificado por um ou mais aspectos. Também chamado de objeto com proxy.
Proxy AOP : AOP
Um objeto criado pela estrutura para implementar contratos de aspecto (definidos por conselhos e pontos). Em Spring AOP
, AOP
um proxy pode ser JDK
um proxy dinâmico ou CGLIB
um proxy.
Introdução : A introdução permite adicionar novos métodos ou propriedades a uma classe existente. Isto é Introduction interfaces
conseguido definindo uma ou mais interfaces adicionais ( ), e AOP
a estrutura cria um proxy para o objeto de destino que implementa essas interfaces.
Se ainda parecer abstrato, vamos dar outro exemplo de produção cinematográfica como analogia.
Aspecto
Imagine que alguém está filmando um filme e os efeitos especiais do filme (como explosões e efeitos especiais de iluminação) são como preocupações transversais que precisam ser tratadas no aplicativo (como registro ou gerenciamento de transações). Esses efeitos especiais aparecem em muitas cenas diferentes do filme, não apenas em uma cena específica. Em AOP
Python, esses “efeitos” são aspectos e podem ser aplicados a várias partes do programa sem alterar a cena (ou código) real.
Ponto de adesão
Continuando com a metáfora do filme, um momento específico de cada cena, como o momento em que ocorre uma explosão, pode ser visto como um ponto de ligação. Na programação, isso geralmente corresponde a uma chamada de método.
Conselho
As notificações são como instruções específicas do diretor para a equipe de efeitos especiais, como “Adicionar um efeito de explosão antes do início da cena” ou “Mostrar o efeito de dissipação de fumaça após o término da cena”. Estas instruções informam à equipe de efeitos especiais quais efeitos específicos devem ser adicionados em momentos específicos do filme. No AOP
, essas "instruções" são notificações, especificando que aspectos (efeitos especiais) devem ser executados antes, depois ou próximo a pontos de junção (momentos específicos de execução de código).
Corte pontual
Se o aviso for uma instrução do diretor para a equipe de efeitos especiais, então o ponto de corte serão as condições específicas contidas na instrução, como "cenas noturnas". Os pointcuts definem quais pontos de conexão (como quais chamadas de métodos específicos) devem receber notificações (instruções de efeito).
Objeto alvo
Os objetos alvo são aquelas cenas onde efeitos especiais precisam ser adicionados. Em nossa metáfora de programação, eles são aqueles objetos afetados pela lógica de aspecto (como classes que requerem registro).
Proxy AOP
AOP
Um proxy é como uma cópia virtual e controlável de uma cena fornecida pela equipe de efeitos especiais. Esta cópia parece ao público igual à cena original, mas na verdade adiciona automaticamente efeitos especiais quando o diretor precisa deles. Na programação, um proxy é um AOP
objeto criado automaticamente pelo framework. Ele envolve o objeto de destino e garante que as notificações (instruções de efeitos especiais) sejam executadas no momento correto.
Introdução
A introdução é como adicionar um novo personagem ou cena a um filme que não existe no roteiro original. Em AOP
, a introdução nos permite adicionar novos métodos ou propriedades a uma classe existente, o que é como estender o conteúdo de um filme sem alterar o roteiro original.
2. Implemente Spring AOP por meio de configuração XML
Spring
Fornece AOP
suporte avançado e pode XML
definir aspectos, notificações ( advice
) e pontos de corte ( pointcuts
) por meio da configuração. Isso permite adicionar comportamentos adicionais (como registro em log, gerenciamento de transações etc.) sem modificar o código-fonte.
Etapas de implementação:
-
Adicionar dependências do Spring
pom.xml
: adicioneSpring
estrutura eAOP
dependências relacionadas ao projeto . -
Definir interfaces de negócios e classes de implementação : Crie interfaces lógicas de negócios e suas implementações, como uma classe de serviço simples.
-
Definir classes de aspecto : crie uma classe de aspecto para definir notificações pré, pós e finais.
-
Configuração XML :
applicationContext.xml
Configure aspectos, negóciosbean
eAOP
tags relacionadas em XML.
2.1 Adicionar dependência Spring
No pom.xml
arquivo, adicione as seguintes dependências
<dependências> <dependência> <groupId>org.springframework</groupId> <artifactId>contexto de primavera</artifactId> <versão>5.3.10</versão> </dependency> <dependência> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <versão>5.3.10</versão> </dependency> <dependência> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <versão>1.9.6</versão> </dependency> </dependências>
2.2 Definir interfaces de negócios e classes de implementação
Primeiro, definimos uma interface lógica de negócios MyService
e sua implementação MyServiceImpl
.
MeuServiço.java:
pacote com.example.demo.aop; interface pública MeuServiço { String performAction (String input) lança exceção; }
MeuServiceImpl.java:
pacote com.example.demo.aop; classe pública MyServiceImpl implementa MyService { @Sobrepor public String performAction(String ação) lança exceção { System.out.println("Executando ação em MyService: " + action); if ("lançar".equals(ação)) { throw new Exception("Exceção do MyService"); } return "Ação realizada: " + ação; } }
2.3 Definir classes de aspecto
A seguir, definimos uma classe de aspecto MyAspect
que conterá um pré-conselho ( advice
) que é executado antes da execução MyService
do método.performAction
MeuAspect.java:
pacote com.example.demo.aop; importar org.aspectj.lang.ProceedingJoinPoint; classe pública MeuAspecto { //pré-notificação public void beforeAdvice() { System.out.println("Antes que o conselho seja executado!"); } //postar notificação public void afterAdvice() { System.out.println("Depois que o conselho estiver em execução!"); } //Notifica após retorno public void afterReturningAdvice(Object retVal) { System.out.println("Após retornar o conselho está em execução! Valor de retorno: " + retVal); } //Notificação após exceção public void afterThrowingAdvice(Throwable ex) { System.out.println("Depois de lançar o conselho está em execução! Exceção: " + ex.getMessage()); } //notificação surround objeto público aroundAdvice (ProceedingJoinPoint joinPoint) lança Throwable { System.out.println("Sobre conselhos: Antes da execução do método"); Resultado do objeto = null; tentar { resultado = joinPoint.proceed(); } finalmente { System.out.println("Sobre conselhos: Após a execução do método"); } resultado de retorno; } }
2.4 XML de configuração
Finalmente, precisamos configurar o conteúdo acima e relacionado no Spring
arquivo de configuração .applicationContext.xml
bean
AOP
aplicaçãoContext.xml:
<?xml versão="1.0" codificação="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- O texto acima é a declaração de cabeçalho do arquivo XML, que define a versão e o tipo de codificação do arquivo e apresenta o namespace dos beans Spring e AOP. Através desses namespaces, podemos usar tags <bean> e <aop:*> em XML. --> <!-- Definições de feijão --> <bean id="myService" class="com.example.demo.aop.MyServiceImpl"/> <bean id="myAspect" class="com.example.demo.aop.MyAspect"/> <!-- Configuração AOP--> <aop:config> <!-- Definir aspectos e suas notificações --> <aop:aspect id="myAspectRef" ref="myAspect"> <!-- Defina pointcuts e especifique notificações que devem ser acionadas quando os métodos são executados --> <aop:pointcut id="serviceOperation" expressão="execution(* com.example.demo.aop.MyService.performAction(..))"/> <!-- Aplicar pré-notificação, especificar operações antes da execução do método --> <aop:before method="beforeAdvice" pointcut-ref="serviceOperation"/> <!-- Aplicar pós-notificação, especificar a operação após a execução do método, se o método foi executado com sucesso ou se uma exceção foi lançada --> <aop:after method="afterAdvice" pointcut-ref="serviceOperation"/> <!-- Notificação após o retorno do aplicativo, operações após o método especificado ser executado e retornado com sucesso --> <aop:after-returning method="afterReturningAdvice" pointcut-ref="serviceOperation" return="retVal"/> <!-- Notificação após exceção do aplicativo, ação após o método especificado lançar exceção --> <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="serviceOperation" throw="ex"/> <!-- Aplique notificações circundantes para fornecer controle completo antes e depois da execução do método --> <aop:around method="aroundAdvice" pointcut-ref="serviceOperation"/> </aop:aspecto> </aop:config> </feijão>
myService : Esta é a lógica de negócios bean
e aponta para MyServiceImpl
uma instância da classe.
myAspect : Este é o aspecto bean
, apontando para MyAspect
uma instância da classe.
<aop:config>
: Este é AOP
o elemento raiz da configuração. Todas AOP
as configurações, incluindo definições de aspecto, pointcuts e métodos de notificação, precisam ser definidas dentro deste elemento.
Aspecto : Definido através <aop:aspect>
de elementos, contém uma série de conselhos ( advice
) e um ou mais pontos de corte ( pointcut
). Este elemento associa classes de aspecto (classes que contêm lógica de notificação) a operações específicas (como e quando aprimorar o objeto alvo).
Pointcut : por meio <aop:pointcut>
da definição do elemento, pointcut é especificado pela expressão. Quando você precisa controlar com precisão quais métodos irão disparar notificações quando executados, você precisa definir pointcut. Expressões pointcut podem especificar métodos com muita precisão, por exemplo, por nome de método, tipos de parâmetros, anotações, etc. expression
Ele define a expressão do pointcut e especifica as regras de correspondência do pointcut. A expressão aqui execution(* com.example.demo.aop.MyService.performAction(..))
significa que o pointcut corresponde à execução do método MyService
na interface performAction
e o pointcut é usado para especificar em quais pontos de conexão ( Join Point
como chamadas de método) a notificação é aplicada.
Sobre análise de expressõesexecution(* com.example.demo.aop.MyService.performAction(..))
execução : é a função pointcut mais comumente usada, usada para combinar os pontos de conexão da execução do método.
*: Indica que o tipo de retorno do método é arbitrário.
com.example.demo.aop.MyService.performAction : especifica o caminho completo do nome da interface e do nome do método.
(…) : Indica que os parâmetros do método são arbitrários e corresponderão independentemente de quantos parâmetros o método possui.
-
Ponto de junção : um ponto de junção refere-se a um determinado ponto durante a execução do programa, como uma chamada de método. Os pontos de junção são identificados e correspondidos por expressões pointcut( ), que definem um pointcut que especifica um conjunto explícito de pontos de junção - ou seja, todas as invocações de métodos da interface . Neste exemplo, as chamadas de método de interface são pontos de conexão potenciais. Cada vez que um método é chamado, um ponto de junção é alcançado . Este ponto de conexão é onde o aplicativo é notificado sobre seu tempo.
Pointcut
execution(* com.example.demo.aop.MyService.performAction(..))
MyService
performAction
MyService
performAction
performAction
-
Conselho : Esta é uma
AOP
ação que aprimora a execução de um método realizando ações em momentos específicos.method
O atributo especifica o nome do método do aspecto que deve ser executado quando o pointcut corresponder,pointcut-ref
referenciando o pointcut definido acima. Por exemplo, aqui está o método que é chamado antes da execuçãobeforeAdvice
do método de destino .performAction
Isso significa que sempre queMyService.performAction(..)
um método for chamado,beforeAdvice
o método será executado primeiro.
Resumindo em uma frase: Spring AOP
Ao definir regras (pontos de corte) em aspectos para especificar quando (pontos de conexão) e como (notificações) aprimorar métodos específicos, a modularização do código e a separação de interesses são alcançadas sem modificar a lógica de negócios original .
Dessa forma, Spring AOP
você pode definir uma lógica personalizada a ser inserida antes, depois ou próximo à execução de um método específico sem modificar o código da lógica de negócios original. Este é um mecanismo poderoso para alcançar a separação de interesses, especialmente para interesses transversais que abrangem múltiplas partes da aplicação (como registro, gerenciamento de transações, etc.).
Observe que se <aop:config>
definido como
<aop:config proxy-target-class="true"> <!--Outra configuração permanece inalterada--> </aop:config>
Definir isso proxy-target-class="true"
fará com que o proxy Spring AOP
seja usado preferencialmente, CGLIB
mesmo que o objeto de destino implemente a interface. Por padrão, proxy-target-class
a propriedade não precisa ser definida ou, se estiver definida false
, um proxy dinâmico será usado JDK
.
Programa principal:
DemoApplication.java:
pacote com.example.demo; importar com.example.demo.aop.MyService; importar org.springframework.context.support.ClassPathXmlApplicationContext; classe pública DemoApplication { public static void main(String[] args) { Contexto ClassPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); MeuServiço meuServiço = (MeuServiço) context.getBean("meuServiço"); tentar { System.out.println(myService.performAction("normal")); } catch (Exceção e) { e.printStackTrace(); } System.out.println("======================"); tentar { System.out.println(myService.performAction("throw")); } catch (Exceção e) { System.out.println("Exceção capturada em main: " + e.getMessage()); } contexto.close(); } }
resultado da operação:
Ao combinar a tecnologia de proxy dinâmico com esses AOP
conceitos, Spring AOP
os aplicativos podem receber suporte para preocupações transversais de uma forma não intrusiva, permitindo que os desenvolvedores modularizem essas preocupações e mantenham os componentes da lógica de negócios focados e simples.
Se você estiver interessado no proxy dinâmico, poderá depurá-lo novamente. JDK
O proxy dinâmico aqui é porque public class MyServiceImpl implements MyService
a interface é implementada da seguinte forma:
Vamos falar brevemente sobre as principais classes e interfaces que podem ser vistas aqui.
ProxyFactory : Esta é Spring AOP
uma classe de fábrica usada para criar objetos proxy. Ele pode decidir usar JDK
proxy dinâmico ou proxy com base no fato de o objeto de destino implementar a interface CGLIB
.
AopProxy : Esta interface define métodos para obter objetos proxy. Possui duas implementações principais: JdkDynamicAopProxy
(para JDK
proxies dinâmicos) e CglibAopProxy
(para CGLIB
proxies).
JdkDynamicAopProxy : implementa AopProxy
a interface e usa JDK
tecnologia de proxy dinâmico para criar um proxy. Ele implementa InvocationHandler
a interface e intercepta todas as chamadas de método para o objeto proxy.
CglibAopProxy : Também implementa AopProxy
a interface, mas usa CGLIB
uma biblioteca para criar objetos proxy. Para classes que não implementam a interface, Spring
este método será escolhido para criar o proxy.
Se quiser entender Spring AOP
o código-fonte em profundidade, você pode visualizar diretamente JdkDynamicAopProxy
a CglibAopProxy
implementação dessas duas classes. Este não é o foco deste artigo, apenas mencione brevemente:
Por exemplo, JdkDynamicAopProxy
veja a implementação do proxy dinâmico em:
-
JdkDynamicAopProxy
As classes implementamInvocationHandler
interfaces, que sãoJDK
o núcleo do proxy dinâmico. Em seuinvoke
método, haverá lógica para determinar se a chamada precisa ser interceptada, e as notificações correspondentes serão aplicadas antes e depois da chamada. -
O processo de criação de um proxy é feito principalmente
ProxyFactory
através da chamada decreateAopProxy()
um método, que retorna uma instância deJdkDynamicAopProxy
ou de acordo com a configuraçãoCglibAopProxy
. -
Uso de proxy: o código do cliente
ProxyFactory
obtém o objeto proxy e chama o método de destino por meio desse objeto proxy. O objeto proxy usaJdkDynamicAopProxy
or internamenteCglibAopProxy
para interceptar essas chamadas eAOP
realizar notificações com base na configuração. O processo de obtençãoProxyFactory
de um objeto proxySpring
geralmente é feito implicitamente na configuração e uso, principalmente quando se utilizaSpring
gerenciamento de contêineresAOP
. Este processo não exige que o desenvolvedor chameProxyFactory
a classe diretamente. QuandoSpring
um é definido na configuraçãobean
e um aspecto é aplicado a ele,Spring
o contêiner cuida automaticamente do processo de criação do agente e aplicação de notificações. Isto é conseguido através do suporteSpring
ao pós-processador eAOP
ao namespace, e os desenvolvedores geralmente só precisam configurar aspectos e conselhos de forma declarativa.
Se você quiser ver CGLIB
o agente, aqui 2
está uma maneira
O terceiro 1
método é remover a interface MyServiceImpl
implementada e, em seguida , alterar o local correspondente entre MyService
o programa principal e a expressão . A terceira maneira é definir explicitamente as propriedades do rótulo no arquivo de configuração para conseguir isso. do seguinte modo:expression
MyServiceImpl
2
Spring
<aop:config>
proxy-target-class="true"
<aop:config proxy-target-class="true"> <!--Outra configuração permanece inalterada--> </aop:config>
A depuração é a seguinte:
Bem-vindo à conexão tripla com um clique ~
Se você tiver alguma dúvida, deixe uma mensagem e vamos discutir e aprender juntos.
Clique para seguir e conhecer as novas tecnologias da Huawei Cloud o mais rápido possível~
RustDesk suspendeu o serviço doméstico Taobao (taobao.com) devido a fraude desenfreada, reiniciou o trabalho de otimização da versão web, a Apple lançou o chip M4, estudantes do ensino médio criaram sua própria linguagem de programação de código aberto como uma cerimônia de maioridade - Internautas comentaram: Confiando em a defesa, Yunfeng renunciou ao Alibaba e planeja produzir no futuro o destino para programadores de jogos independentes o Visual Studio Code 1.89, é oficialmente anunciado pela Huawei. O ajuste de trabalho de Yu Chengdong foi pregado no “Pilar da Vergonha FFmpeg. ” 15 anos atrás, mas hoje ele tem que nos agradecer - Tencent QQ Video vinga sua vergonha anterior? A estação espelho de código aberto da Universidade de Ciência e Tecnologia Huazhong está oficialmente aberta ao acesso à rede externa