1. Introdução ao AOP
1. Introdução básica ao AOP
Outro ponto importante no Spring é o AOP. O nome completo de AOP é "Programação Orientada a Aspectos", ou seja, programação orientada a aspectos, que isola várias partes da lógica de negócios, para que os desenvolvedores possam se concentrar no negócio principal ao escrever a lógica de negócios, melhorando assim a eficiência do desenvolvimento. Simplificando, significa adicionar novas funções à função principal sem modificar o código-fonte.
No código de processamento de negócios tradicional, geralmente executamos operações como processamento de transações e registro. No passado, por meio do uso de ideias OOP, a reutilização de código pode ser alcançada em uma combinação ou forma de herança, mas se uma determinada função (como registro) for implementada, o mesmo código será espalhado em cada método neste momento . Quando você deseja fechar ou modificar uma função, você deve modificar todos os métodos relacionados. Isso não apenas aumenta a carga de trabalho dos desenvolvedores, mas também aumenta a taxa de erro do código.
AOP adota um mecanismo de extração horizontal para substituir o código repetitivo do sistema de herança vertical tradicional . Esse mecanismo de extração horizontal pode encapsular os comportamentos comuns que afetam várias classes em um módulo reutilizável e nomeá-lo "Aspect", que é o que chamamos de aspecto. Aspectos referem-se às lógicas ou responsabilidades que não estão relacionadas ao negócio, mas são comumente chamadas por módulos de negócios. Eles são encapsulados, reduzindo assim o código repetitivo no sistema e reduzindo o acoplamento entre os módulos, o que favorece a operacionalidade e a manutenção subsequentes .
Por exemplo: em um sistema, o registro é uma função básica e todos os processos de negócios no sistema precisam ser registrados. Se a função de registro for escrita em cada processo de negócios, ela causará redundância de código e aumentará a dificuldade de manutenção. Supondo que a lógica de registro mude neste momento, o código de registro em cada processo de negócios precisa ser modificado, o que obviamente não é aconselhável. Ao contrário, se a função de registro de log for extraída como um módulo independente, quando o processo de negócios exigir, o sistema automaticamente corta a função de registro de log no processo de negócios, o que torna a manutenção muito mais fácil. E este AOP formal pode alcançar. Como mostrado abaixo:
2. Termos relacionados ao AOP
o termo | Descrição |
---|---|
Preocupações transversais |
Quais métodos são interceptados e o que fazer após a interceptação, essas questões são chamadas de questões transversais |
Aspecto |
Geralmente é uma classe na qual os pontos de entrada e notificações podem ser definidos. Um aplicativo pode ter vários aspectos |
JointPoint |
O ponto claro no processo de execução do programa, ou seja: a localização específica do processo de negócio que precisa ser inserido no aspecto durante o processo de operação, que geralmente é a chamada do método. Como o Spring suporta apenas pontos de conexão de tipos de método, os pontos de conexão no Spring referem-se a métodos que são interceptados.Na verdade, os pontos de conexão também podem ser campos ou construtores. |
Conselho |
O método de implementação específico do aspecto, o que fazer após interceptar o Joinpoint, ou seja, o conteúdo do aprimoramento do pointcut |
Pointcut |
É o ponto de conexão com notificação, que é usado para definir quais pontos de conexão a notificação deve cortar. Notificações diferentes geralmente precisam cortar em pontos de conexão diferentes. No programa, isso se reflete principalmente na escrita da expressão do ponto de entrada. |
Weave (tecido em) |
Indica cut-in, também conhecido como tecelagem. O processo de aplicação de aspectos ao objeto de destino para criar um novo objeto proxy. Este processo pode ocorrer durante a compilação, carregamento de classe e tempo de execução |
Introdução |
Sob a premissa de não modificar o código, a introdução pode adicionar dinamicamente alguns métodos ou campos à classe em tempo de execução |
Proxy |
Representa o objeto proxy. Um objeto que é criado dinamicamente após a notificação ser aplicada ao objeto de destino. Pode ser simplesmente entendido que o objeto proxy é o objeto formado pela função lógica de negócios do objeto de destino mais a superfície de corte. O proxy AOP no Spring pode ser um proxy dinâmico para JDK ou um proxy CGLIB.O primeiro é baseado em interfaces e o último é baseado em subclasses. |
Objeto Alvo |
O objeto que contém o ponto de conexão é o objeto notificado por um ou mais aspectos. Também conhecido como objeto notificado ou proxy |
3. Tipos de notificações em AOP
Tipo de notificação | Descrição |
---|---|
Aviso prévio (antes) |
Faça o processamento de aprimoramento antes que o método de destino seja chamado, @Before só precisa especificar a expressão pointcut |
Notificação após retorno (AfterReturning) |
Depois que o método de destino é concluído normalmente, ele é aprimorado. Além de especificar a expressão de pointcut, @AfterReturning também pode especificar um nome de parâmetro de valor de retorno retornando, que representa o valor de retorno do método de destino |
Notificação depois que uma exceção é lançada (AfterThrowing) |
É usado principalmente para tratar exceções não tratadas no programa.Além de especificar a expressão pointcut, @AfterThrowing também pode especificar um nome de parâmetro de valor de retorno de lançamento, que pode ser usado para acessar o objeto de exceção lançado no método de destino. |
Após a notificação (após) |
O aprimoramento é feito após a conclusão do método de destino, independentemente de quando o método de destino foi concluído com êxito. @After pode especificar uma expressão de ponto de entrada |
Around Notification (Around) |
Aprimore o processamento antes e depois da conclusão do método de destino, a notificação surround é o tipo mais importante de notificação, como transações, registros, etc. são notificações surround |
4. Expressão do ponto de entrada
Indicador de ponto de corte é usado para indicar o propósito da expressão de ponto de corte Atualmente, há apenas um ponto de conexão do método de execução no Spring AOP. O formato da expressão pointcut é o seguinte:
execução ([visibilidade] tipo de retorno [tipo de declaração]. nome do método (parâmetro) [exceção]), onde [] é opcional , outros também suportam o uso de curingas:
*: corresponde a todos os caracteres
..: geralmente usado para combinar vários pacotes, vários parâmetros
+: indica a classe e suas subclasses
Os operadores também podem ser usados em expressões de pointcut, como: &&, || ,!
Aqui está um exemplo para ilustrar o uso de expressões pointcut:
(1) Escrita de expressão padrão: public void com.yht.controller.UserController.findUser ()
(2) Todas as gravações de curinga: * .. *. * (..)
(3) Omitir modificador de acesso: void com.yht.controller.UserController.findUser ()
(4) O valor de retorno usa curinga *: * com.yht.controller.UserController.findUser ()
(5) O nome do pacote usa curingas *: * *. *. *. UserController.findUser () // Se houver vários níveis de pacotes, escreva alguns *
(6) Use curinga * para o nome da classe e o nome do método: * .. *. * ()
(7) Lista de parâmetros
Se houver parâmetros, adicione * —— (*) ao () do método
Com ou sem parâmetros: adicione o () do método ..—— (..)
(8) Normalmente escrito: * com.yht.controller.UserController. * (..)
Para outras introduções sobre expressões pointcut, consulte o seguinte blog:
https://blog.51cto.com/lavasoft/172292
https://www.cnblogs.com/sjqq/p/10241781.html
Em segundo lugar, o modo proxy no Spring
Introduzido anteriormente: O proxy AOP no Spring pode ser um proxy dinâmico para JDK ou um proxy CGLIB.O primeiro é baseado em interfaces e o último é baseado em subclasses. Em seguida, implemente esses dois métodos de proxy.
1. Proxy dinâmico JDK
(1) Crie a interface IOderDao e sua subclasse OrderDaoImpl.
public interface IOrderDao {
void addOrder();
void deleteOrder();
void updateOrder();
void searchOrder();
}
public class OrderDaoImpl implements IOrderDao {
@Override
public void addOrder() {
System.out.println("添加订单");
}
@Override
public void deleteOrder() {
System.out.println("删除订单");
}
@Override
public void updateOrder() {
System.out.println("更新订单");
}
@Override
public void searchOrder() {
System.out.println("查询订单");
}
}
(2) Crie uma classe de aspecto MyAspect
public class MyAspect {
public void before(){
System.out.println("方法执行之前");
}
public void after(){
System.out.println("方法执行之后");
}
}
(3) Crie uma classe de proxy MyBeanFactory
public class MyBeanFactory {
public static IOrderDao getBean() {
// 准备目标类
final IOrderDao customerDao = new OrderDaoImpl();
// 创建切面类实例
final MyAspect myAspect = new MyAspect();
// 使用代理类,进行增强
//Proxy 的 newProxyInstance() 方法的第一个参数是当前类的类加载器,第二参数是所创建实例的实现类的接口,第三个参数就是需要增强的方法
return (IOrderDao) Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),
new Class[] { IOrderDao.class }, new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
myAspect.before(); // 前增强
Object obj = method.invoke(customerDao, args);
myAspect.after(); // 后增强
return obj;
}
});
}
}
(4) Faça um teste.
@Test
public void test() {
// 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
IOrderDao orderDao = MyBeanFactory.getBean();
// 执行方法
orderDao.updateOrder();
System.out.println("=====================");
orderDao.searchOrder();
}
Os resultados são os seguintes:
2. proxy dinâmico cglib
(1) Importe o pacote do jar
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1</version>
</dependency>
(2) Crie a classe de destino Bens
public class Goods{
public void addGoods() {
System.out.println("添加货物");
}
public void deleteGoods() {
System.out.println("删除货物");
}
public void updateGoods() {
System.out.println("更新货物");
}
public void searchGoods() {
System.out.println("查询货物");
}
}
(3) Crie uma classe de proxy GoodsBeanFactory
public class GoodsBeanFactory {
public static Goods getBean() {
// 准备目标类
final Goods goodsDao = new Goods();
// 创建切面类实例
final MyAspect myAspect = new MyAspect();
// 生成代理类,CGLIB在运行时,生成指定对象的子类,增强
Enhancer enhancer = new Enhancer();
// 确定需要增强的类
enhancer.setSuperclass(goodsDao.getClass());
// 添加回调函数
enhancer.setCallback(new MethodInterceptor() {
// intercept 相当于 jdk invoke,前三个参数与 jdk invoke—致
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
myAspect.before(); // 前增强
Object obj = method.invoke(goodsDao, args); // 目标方法执行
myAspect.after(); // 后增强
return obj;
}
});
// 创建代理类
Goods goodsDaoProxy = (Goods) enhancer.create();
return goodsDaoProxy;
}
}
(4) Teste
@Test
public void testGoods() {
// 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
Goods goods = GoodsBeanFactory.getBean();
// 执行方法
goods.deleteGoods();
System.out.println("=====================");
goods.searchGoods();
}
Os resultados são os seguintes: