Notas de estudo da primavera-04 AOP

1. Modelo de agência

O cenário aplicável do modo proxy é que às vezes queremos aprimorar os métodos de algumas classes de entidade, mas não é conveniente modificar as definições de método correspondentes nas classes de entidade. Neste momento, podemos escrever a parte aprimorada no proxy através do modo.

1) proxy estático

Suponha que queremos vender telefones celulares, para que possamos escrever a classe de entidade da seguinte maneira:

Person.java

public class Person {
    public void sellPhone(){
        System.out.println("卖手机");
    }
}

teste:

public class TestStaticProxy {
    public static void main(String[] args) {
        Person person = new Person();
        person.sellPhone();
    }
}

Agora descobrimos que precisamos encontrar a próxima casa antes de vender telefones celulares, mas definimos o método de venda de telefones celulares. No momento, podemos aprimorá-lo por meio de proxy estático:

1) Use especificações de definição de interface

public interface ISellPhone {
    public void sellPhone();
}

2) Método de definição

public class Person implements ISellPhone {
    public void sellPhone(){
        System.out.println("卖手机");
    }
}

3) Aprimorar métodos por meio de agentes

public class SellPhoneProxy implements ISellPhone {
    private Person person = new Person();
    public void sellPhone() {
        System.out.println("寻找买家");
        person.sellPhone();
    }
}

4) Nós apenas usamos o proxy diretamente

public class TestStaticProxy {
    public static void main(String[] args) {
        ISellPhone iSellPhone = new SellPhoneProxy();
        iSellPhone.sellPhone();
    }
}

2) proxy dinâmico JDK

A desvantagem dos agentes estáticos é que para quase todos os negócios, eles devem ser feitos adicionando um agente, mesmo que seus métodos de aprimoramento sejam quase os mesmos; também podemos usar agentes dinâmicos para aprimorar diretamente o método sem escrever muitos agentes de classe .

Adicionamos uma empresa de pós-venda para telefones celulares:

public interface ISellPhone {
    public void sellPhone();
    public void serviceAfterSelling();
}

Person.java

public class Person implements ISellPhone {
    public void sellPhone(){
        System.out.println("卖手机");
    }

    public void serviceAfterSelling() {
        System.out.println("售后服务");
    }
}

Use a interface de proxy dinâmica do JDK:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class SellPhoneProxy implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前");
        Object result = method.invoke(new Person(),args);
        System.out.println("调用后");
        return result;
    }
}

Vendo o nome do pacote, também devemos saber que o proxy dinâmico é implementado por meio de mecanismo de reflexão. A propriedade Object é usada para armazenar o objeto sendo proxy.

teste:

import java.lang.reflect.Proxy;

public class TestDynamicProxy {
    public static void main(String[] args) {
        Person person = new Person();
        ISellPhone iSellPhone = (ISellPhone) Proxy.newProxyInstance(ISellPhone.class.getClassLoader(),new Class[]{ISellPhone.class},new SellPhoneProxy());
        iSellPhone.serviceAfterSelling();
    }
}

[Nota]: Seja um proxy estático ou um proxy dinâmico, a classe de proxy e a classe de proxy precisam ter uma interface comum.

3) Agente dinâmico CGLib

Claro, você também pode usar o método de herança, deixar a classe proxy herdar a classe proxy e aprimorar o método correspondente para proxy (cglib) sobrescrevendo o método.

A. Importar dependências

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

B. Use o proxy dinâmico cglib

import java.lang.reflect.Method;

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        enhancer.setCallback(new InvocationHandler() {
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                System.out.println("执行前");
                Object invoke = method.invoke(new Person(),objects);
                System.out.println("执行后");
                return invoke;
            }
        });
        Person personAgent = (Person) enhancer.create();
        personAgent.sellPhone();
        System.out.println("============");
        personAgent.serviceAfterSelling();
    }
}

3. Resultados de execução

2. Programação orientada a aspectos AOP

A programação orientada a aspectos AOP (Aspect Oriented Programming) é usada principalmente para resolver alguns problemas de nível de sistema no processo de desenvolvimento do programa, como logs, transações, permissões, etc.

O AOP do Spring é na verdade um aprimoramento do proxy do método. Após a análise acima, já sabemos que o proxy baseado em interface usa o JDK; se não houver interface, o proxy é usado pelo cglib.

Vamos dar uma olhada na programação orientada a objetos OOP (Object Oriented Programming) primeiro. Todas as coisas são consideradas como objetos. Java é uma linguagem OOP. A extensão das classes é realizada por herança. O sistema construído com Java mostra uma relação vertical.

No entanto, a herança também tem deficiências. Embora a herança possa completar o aprimoramento do método da classe pai, cada aprimoramento da classe requer uma herança, o que levará a muitas classes em todo o sistema, o que não é fácil de manter, especialmente para todos tipos como logs. Quando o método e o método de aprimoramento são quase os mesmos, se for implementado por meio de herança, a carga de trabalho é muito grande.

Em Sring, é definida uma tecnologia de "corte transversal", que é aprimorada pela inserção de uma seção transversal no método sem herança. Todo o sistema apresenta uma relação horizontal, e a camada inferior é realizada através de um agente, que possui uma cruz -section. O efeito é o seguinte:

A. Terminologia relacionada ao AOP:

1. Notificação, processamento avançado (conselho)

O código ou lógica escrita para implementar melhorias funcionais é a segurança, transação, log, etc. mencionados acima.

2. Ponto de conexão (JoinPoint)

O Spring permite a inserção de notificações, incluindo frente, verso, surround (antes e depois) de cada método, e quando uma exceção é lançada, o Spring suporta apenas o ponto de conexão do método.

3. PointCut

Ou seja, nós realmente definimos aqueles pontos de conexão a serem usados, ou seja, aqueles pontos de conexão que realmente cortam a notificação.

4. Aspecto (Aspecto)

O aspecto é a combinação de notificação e ponto de entrada. A notificação é responsável por explicar o que fazer e quando fazer, e o ponto de entrada mostra onde fazer, que juntos constituem o aspecto.

5. Introdução

Introdução nos permite introduzir aspectos na classe de destino.

6. Alvo

A classe alvo mencionada na introdução, ou seja, o objeto a ser notificado, possui uma lógica de negócio real, não precisa se preocupar com o que é cortado, mas apenas com sua própria lógica de negócio.

7. Tecelagem

O processo de aplicação do aspecto ao objeto de destino (recortando a notificação para o ponto de contato) e de construção de um novo objeto proxy de acordo.

B. Sinta o uso de AOP (anotação):

1. Adicionar arquivos de cabeçalho

<?xml version="1.0" encoding="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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    
</beans>

2. Importar dependências tecidas

        <!--引入织入相关的依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

3. Adicione dois métodos a serem aprimorados

AdminService.java

package com.zt.Service;

import org.springframework.stereotype.Service;

@Service
public class AdminService {

    public Integer getAdmin(){
        System.out.println("-----getAdmin-----");
        return 100;
    }

}

UserService.java

package com.zt.Service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void getUser(){
        System.out.println("-----getUser-----");
    }

}

4. Escreva a classe de aspecto

package com.zt.Aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAdvice {

    @Before("execution(* com.zt.Service.*.*(..))")
    public void before(){
        System.out.println("-----方法执行前 before -----");
    }

    @After("execution(* com.zt.Service.*.*(..))")
    public void after(){
        System.out.println("-----方法执行后 after -----");
    }

    @AfterReturning("execution(* com.zt.Service.*.*(..))")
    public void afterReturning(){
        System.out.println("-----方法执行返回后 afterReturning -----");
    }

    @Around("execution(* com.zt.Service.*.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("-----环绕前-----");
        System.out.println("方法名:" + joinPoint.getSignature());
        Object proceed = joinPoint.proceed();
        System.out.println("-----环绕后-----");
        System.out.println(proceed);
    }

    @AfterThrowing("execution(* com.zt.Service.*.*(..))")
    public void afterThrow(){
        System.out.println("-----有异常-----");
    }

}

[Nota]: A expressão de execução é usada para especificar quais métodos são aprimorados.

Claro, também podemos especificar qual método melhorar: execução (void com.zt.Service.UserService.getUser (..))

 

5. Como usamos o método de classe de aspecto, precisamos ativar a varredura de pacote e deixar o contêiner Spring gerenciar o Bean usado para configuração

<context:component-scan base-package="com.zt"/>

6. Ative o suporte de anotação para proxy automático AOP

<aop:aspectj-autoproxy/>

7. Use classes de configuração e teste

package com.zt.Config;

import com.zt.Service.AdminService;
import com.zt.Service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class JavaConfig {

    @Autowired
    private UserService userService;

    @Autowired
    private AdminService adminService;

    @Test
    public void TestAop(){
        userService.getUser();
        System.out.println("-------------------------------------------");
        adminService.getAdmin();
    }

}

[Nota]: Spring pode apenas aprimorar o método Bean no contêiner, e o objeto instanciado fora do contêiner não será aprimorado.

C. Uso de AOP (método de arquivo de configuração)

1. Defina a classe de aspecto

package com.zt.Aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

@Component
public class XMLAdvice {

    public void before(){
        System.out.println("-----方法执行前 before -----");
    }

    public void after(){
        System.out.println("-----方法执行后 after -----");
    }

    public void afterReturning(){
        System.out.println("-----方法执行返回后 afterReturning -----");
    }

    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("-----环绕前-----");
        System.out.println("方法名:" + joinPoint.getSignature());
        Object proceed = joinPoint.proceed();
        System.out.println("-----环绕后-----");
        System.out.println("方法执行结果的返回值:" + proceed);
    }

    public void afterThrow(){
        System.out.println("-----有异常-----");
    }
    
}

2. No arquivo de configuração, use pointcuts e consulte os aspectos para tecer

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.zt.Service.*.*(..))"/>
        <aop:aspect ref="XMLAdvice">
            <aop:before pointcut-ref="pointcut" method="before"/>
            <aop:after pointcut-ref="pointcut" method="after"/>
        </aop:aspect>
    </aop:config>

<aop: pointcut>: usado para definir o pointcut.

<aop: aspect>: Usado para introduzir classes de aspecto.

<aop: before>: define o modo de execução como antes da execução. Para introduzir um pointcut pode usar ref para se referir a um pointcut existente (pointcut-ref), ou diretamente usar uma expressão de execução para definir um pointcut. método é usado para especificar de qual método no aspecto a notificação vem, e outros métodos de execução são semelhantes.


        <aop:aspect ref="XMLAdvice">
            <aop:before pointcut="execution(* com.zt.Service.*.*(..))" method="before"/>
            <aop:after pointcut="execution(* com.zt.Service.*.*(..))" method="after"/>
        </aop:aspect>

D. Uso de AOP (usar notificação diretamente, implementando algumas interfaces específicas)

LogBefore.java

package com.zt.Aop;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class LogBefore implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----before------" + method.getName() + "-----");
    }
}

LogAfter.java

package com.zt.Aop;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class LogAfter implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----after-----" + method.getName() + "------");
    }
}

Referência no arquivo de configuração:

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.zt.Service.*.*(..))"/>
        <aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>
    </aop:config>

[Nota]: Use a anotação @Component para fazer o Spring instanciar e gerenciar automaticamente este bean, e o nome padrão é minúsculo, e os outros permanecem inalterados (nomenclatura camel case). Obviamente, para facilitar a leitura, também podemos especificar o ID do bean injetado:

LogBefore.java

package com.zt.Aop;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component("Before")
public class LogBefore implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----before------" + method.getName() + "-----");
    }
}

LogAfter.java

package com.zt.Aop;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component("After")
public class LogAfter implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----after-----" + method.getName() + "------");
    }
}

Referência no arquivo de configuração:

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.zt.Service.*.*(..))"/>
        <aop:advisor advice-ref="Before" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="After" pointcut-ref="pointcut"/>
    </aop:config>

 

 

Acho que você gosta

Origin blog.csdn.net/qq_39304630/article/details/112392223
Recomendado
Clasificación