[Primavera desde la entrada al tutorial de combate real] Capítulo 3 Explicación detallada de Spring AOP

3. Primavera AOP

3.1 Preludio a AOP

3.1.1 Requisitos

  • logs, para realizar un seguimiento de las actividades que se están llevando a cabo durante la ejecución del programa;

  • Validación, procesamiento de datos legítimos durante la ejecución del programa;

3.1.2 Código

Interfaz de UserService y clase de implementación:

public interface UserService {
    
    void insertUser();

    void updateUser();

    void deleteUser(int id);

    List<String> selectUser();
}

public class UserServiceImpl implements UserService {
    @Override
    public void insertUser() {
        //在方法种主要业务逻辑代码开始之前,加入日志记录
        System.out.println("日志:用户新增...");

        System.out.println("用户新增的业务逻辑...");
    }

    @Override
    public void updateUser() {
        System.out.println("日志:用户修改...");

        System.out.println("用户修改的业务逻辑...");
    }

    @Override
    public void deleteUser(int id) {
        System.out.println("日志:用户删除...");

        System.out.println("用户删除的业务逻辑...");
    }

    @Override
    public List<String> selectUser() {
        System.out.println("日志:用户查询...");

        System.out.println("用户查询的业务逻辑...");
        return null;
    }
}

prueba:

public class ProxyTest {
    @Test
    public void test() {
        UserService userService = new UserServiceImpl();

        userService.insertUser();
        System.out.println("----------------------------------");

        userService.updateUser();
        System.out.println("----------------------------------");

        userService.deleteUser(10);
        System.out.println("----------------------------------");

        List<String> list = userService.selectUser();
        System.out.println(list);
    }
}

3.1.3 Problemas

  • Confusión de código: después de que se agregan más y más requisitos no comerciales (registro y verificación, etc.), los métodos comerciales originales se expanden dramáticamente. Cada método también debe ocuparse de muchas otras preocupaciones al manejar la lógica central;

  • Dispersión de código: tome el requisito de registro como ejemplo, solo para cumplir con este único requisito, debe repetir el mismo código de registro varias veces en varios módulos (métodos). Si cambian los requisitos de registro, se deben modificar todos los módulos;

3.2 Modo proxy

Utilice el modo proxy para resolver los problemas anteriores:

     El principio del patrón de diseño de proxy: use un proxy para envolver el objeto y luego reemplace el objeto original con el objeto proxy. Cualquier llamada al objeto original debe pasar a través del proxy. El objeto proxy decide si transferir el método y cuándo hacerlo. llamar al objeto original.
    
    Por lo general, el modo proxy se usa para tratar dos problemas:
        1. Controlar el acceso al objeto base
        2. Agregar funciones adicionales al acceder al objeto base;

3.2.1 Proxy estático

    Cada interfaz comercial debe tener una clase de proxy correspondiente e implementar la interfaz comercial;

/**
 * 静态代理
 * 要求:代理类与目标类必须实现同一个接口
 */
public class UserServiceProxy implements UserService {
    private UserService userService;

    /**
     * 目的:将代理类和目标类绑定
     * 在创建代理类的同时传入目标类对象
     */
    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void insertUser() {
        //1、代理功能
        System.out.println("日志记录:用户新增...");
        //2、调用目标类的方法,让核心业务逻辑执行
        userService.insertUser();
    }

    @Override
    public void updateUser() {
        System.out.println("日志记录:用户修改...");
        userService.updateUser();
    }

    @Override
    public void deleteUser(int id) {
        System.out.println("日志记录:用户删除..." + id);
        userService.deleteUser(id);
    }

    @Override
    public List<String> selectUser() {
        System.out.println("日志记录:用户查询...");
        return userService.selectUser();
    }
}

Elimine el código de registro de la capa empresarial y pruebe:

@Test
public void testStaticProxy() {
    UserService userService = new UserServiceProxy(new UserServiceImpl());

    userService.insertUser();
    System.out.println("----------------------------------");

    userService.updateUser();
    System.out.println("----------------------------------");

    userService.deleteUser(10);
    System.out.println("----------------------------------");

    List<String> list = userService.selectUser();
    System.out.println(list);
}
  • Ventajas: simple y fácil de entender;

  • Desventajas: después de que el proyecto es enorme, hay demasiadas interfaces comerciales, lo que da como resultado demasiadas clases de proxy;

3.2.2 Proxy dinámico JDK

    Proxy dinámico de JDK (API de proxy dinámico), que es una característica introducida en JDK1.3, la API central es la clase Proxy y la interfaz InvocationHandler. Su principio es utilizar el mecanismo de reflexión para generar el código de bytes de la clase proxy en tiempo de ejecución.

  • Proxy dinámico JDK: use el proxy dinámico en el mecanismo de reflexión de Java para implementar la interfaz InvocationHandler;

  • Requisitos: la clase de destino debe tener la forma de interfaz + clase de implementación;

/**
 * JDK动态代理
 * 必须实现InvocationHandler接口
 */
public class LoggerProxy implements InvocationHandler {
    /**
     * 执行目标(最终要执行业务逻辑类)方法
     *
     * @param proxy  代理类对象
     * @param method 目标方法对象
     * @param args   目标方法的参数值列表
     * @return 目标方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //日志记录的代码
        System.out.println("代理日志:目标方法--前置" + method.getName() + "....");
        
        //执行目标方法(业务逻辑方法)
        Object result_val = null;
        try {
            result_val = method.invoke(targetClass.newInstance(), args);
            System.out.println("代理日志:目标方法--返回" + method.getName() + "....");
        } catch (Exception e) {
            System.out.println("代理日志:目标方法--异常" + method.getName() + "....");
        }
        
        System.out.println("代理日志:目标方法--后置" + method.getName() + "....");
        return result_val;
    }

    /**
     * 目标对象的Class类型
     */
    private Class targetClass;

    /**
     * 获取代理对象:就是动态实现目标类接口的实现类
     * newProxyInstance(): 获取实现目标接口的代理对象
     * 注意:JDK中的动态代理,要求业务类必须是接口+实现类的形式
     */
    public Object getProxy(Class targetClass) {
        this.targetClass = targetClass;
        return Proxy.newProxyInstance(targetClass.getClassLoader(), targetClass.getInterfaces(), this);
    }
}

Interfaz PersonService y clase de implementación:

public interface PersonService {
    void insertPerson();
}

public class PersonServiceImpl implements PersonService {
    @Override
    public void insertPerson() {
        System.out.println("新增Person的业务逻辑...");
    }
}

prueba:

public class ProxyTest {
    @Test
    public void testDynamicProxy() {
        //获取代理对象
        UserService userService = (UserService) new LoggerProxy().getProxy(UserServiceImpl.class);
        System.out.println(userService);

        //通过代理对象调用指定的方法:具体执行流程,代理类调用指定的方法,然后通过invoke调用目标类的方法
        userService.insertUser();
        System.out.println("----------------------------------");

        userService.updateUser();
        System.out.println("----------------------------------");

        userService.deleteUser(10);
        System.out.println("----------------------------------");

        List<String> list = userService.selectUser();
        System.out.println(list);
        System.out.println("----------------------------------");

        PersonService personService = (PersonService) new LoggerProxy().getProxy(PersonServiceImpl.class);
        personService.insertPerson();
    }
}
  • Ventajas: Una clase puede representar todos los servicios;

  • Desventajas: Se implementa utilizando el mecanismo de reflexión, que es difícil de entender y no es fácil de mantener Proxy dinámico JDK: la clase de negocio debe tener la forma de interfaz + clase de implementación;

3.2.3 Proxy dinámico Cglib

    El proxy dinámico Cglib implementa la interfaz MethodInterceptor bajo el paquete cglib Actualmente, Spring está usando el proxy dinámico Cglib.

public class CglibProxy implements MethodInterceptor {
    /**
     * @param obj    CGLib动态生成的代理类对象
     * @param method 目标方法对象
     * @param args   目标方法的参数值列表
     * @param proxy  代理类对方法的代理对象
     * @return 目标方法的返回值
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Cglib动态代理...");
        
        //执行目标方法(业务逻辑方法)
        Object result_val = null;
        try {
            result_val = method.invoke(targetClass.newInstance(), args);
        } catch (Exception e) {
        }
        return result_val;
    }

    /**
     * 目标对象的Class类型
     */
    private Class targetClass;

    /**
     * 获取代理对象
     * <p>
     * 注意:JDK中的动态代理,要求业务类必须是接口+实现类的形式
     */
    public Object getProxy(Class targetClass) {
        //为目标对象target赋值
        this.targetClass = targetClass;
        Enhancer enhancer = new Enhancer();
        //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(targetClass);
        //设置回调
        enhancer.setCallback(this);
        //创建并返回代理对象
        Object result = enhancer.create();
        return result;
    }
}

prueba:

public class ProxyTest {
   @Test
    public void testCglibProxy() {
        UserService userService = (UserService) new CglibProxy().getProxy(UserServiceImpl.class);
        System.out.println(userService);

        userService.insertUser();
        System.out.println("----------------------------------");

        userService.updateUser();
        System.out.println("----------------------------------");

        userService.deleteUser(10);
        System.out.println("----------------------------------");

        List<String> list = userService.selectUser();
        System.out.println(list);
    }
}

3.3 Resumen de AOP

    El nombre completo de AOP es "Programación Orientada a Aspectos", traducido como "Programación Orientada a Aspectos", similar a OOP (Programación Orientada a Objetos), también es una idea de programación.
    
    Por lo general, usamos el pensamiento OOP (orientado a objetos) de acuerdo con el negocio y dividimos la aplicación en múltiples módulos comerciales diferentes. Las funciones principales de cada módulo solo brindan servicios para áreas comerciales específicas, como pedidos en el sistema de comercio electrónico. El módulo, el módulo de productos básicos y el módulo de inventario sirven para mantener la información de pedidos, la información de productos básicos y la información de inventario del sistema de comercio electrónico, respectivamente.

    Pero además, a menudo hay algunas funciones generales no comerciales en la aplicación, como la gestión de registros, la gestión de autoridades, la gestión de transacciones, la gestión de excepciones, etc. Aunque estas funciones generales no tienen nada que ver con el negocio de la aplicación, casi todos los módulos comerciales las utilizarán, por lo que estos códigos de funciones generales solo se pueden integrar en varios módulos comerciales diferentes de manera distribuida horizontalmente. Sin duda, esto generará una gran cantidad de código repetitivo, lo que no es propicio para la reutilización de varios módulos.

    Puede pensar, ¿pueden estos códigos repetitivos encapsularse en funciones públicas y luego llamarse explícitamente en módulos comerciales, también puede reducir los códigos repetitivos? Sí, hacerlo puede reducir el código repetitivo hasta cierto punto, pero también aumenta el acoplamiento entre el código comercial y las funciones públicas. Cualquier modificación a las funciones públicas afectará a todos los códigos comerciales relacionados.

    A diferencia de la relación de herencia vertical padre-hijo en OOP, AOP se realiza a través de un mecanismo de extracción horizontal. Extrae algunas funciones comunes no comerciales en la aplicación y las mantiene por separado, y define de manera declarativa (como archivos de configuración, anotaciones, etc.) cómo se deben usar estas funciones en esa aplicación, en lugar de en el código de la Llamar directamente al modulo de negocios.

    Aunque el diseño de las funciones públicas es algo similar, las funciones públicas tradicionales no tienen otros medios que las llamadas directas en el código. AOP proporciona un conjunto de métodos de implementación flexibles y diversos para este problema (como proxy proxy, interceptor, tecnología de traducción de bytecode, etc.), que pueden completar la llamada y modificación de estas funciones generales sin modificar ningún código comercial.

    Los objetivos de la programación AOP y la programación OOP son los mismos, ambos son reducir el código repetitivo en el programa, para que los desarrolladores puedan concentrarse más en el desarrollo de la lógica comercial, pero los métodos de implementación de los dos son bastante diferentes.

    OOP es como una "aguja de bordar", que es una elección elegante. Utiliza la herencia y la composición para compilar cuidadosamente un conjunto de clases y objetos para todos los módulos que involucran funciones comunes, a fin de reducir el código repetitivo. El objetivo. AOP, por otro lado, es más como un "cuchillo de hacha", una opción audaz y sin restricciones, con regulaciones drásticas, todos los métodos bajo un determinado paquete y categoría se procesan juntos.

    AOP no se usa para reemplazar OOP, sino una extensión de OOP para resolver los problemas encontrados en la programación OOP.

  • El principal objeto de programación de AOP es el aspecto, y el aspecto es una preocupación transversal modular;

    • Foco: Donde se coloca el código de mejora. Por ejemplo, al agregar registros a la capa de servicio de la gestión de la información de los estudiantes, la capa de servicio es el centro de atención;

    • Transversal: El proceso comercial de un módulo regular es Web -> Servicio -> Dao.Si queremos iniciar sesión en la capa de Servicio de todos los módulos, entonces esto es transversal;

    • Modularización: Extraer el código mejorado que antes estaba disperso en cada módulo y concentrarlo en un determinado módulo o clase, entonces esto es modularización;

  • Al aplicar la programación AOP, aún necesita definir funciones públicas, pero puede definir claramente dónde y cómo se aplica esta función, y no tiene que modificar las clases afectadas. De esta manera, las preocupaciones transversales se modularizan en objetos especiales (aspectos);

  • Los beneficios de AOP: la lógica de cada cosa se encuentra en un solo lugar, el código no está disperso y es fácil de mantener y actualizar. El módulo comercial es más conciso y solo contiene el código comercial central;

3.4 Terminología AOP

nombre ilustrar
punto de unión El concepto central de AOP se refiere a un punto bien definido durante la ejecución del programa, como la invocación de métodos, la inicialización de clases, la creación de instancias de objetos, etc. En Spring, un punto de unión es un método de una clase de destino que puede ser interceptado por un proxy dinámico.
Punto de corte (punto de entrada) También conocido como punto de corte, se refiere a qué Joinpoints se van a interceptar, es decir, los puntos de conexión interceptados.
Consejo Hace referencia al código que se ejecutará después de interceptar el Joinpoint, es decir, el contenido mejorado del punto de entrada.
Objetivo Hace referencia al objeto de destino del agente, también conocido como el objeto aconsejado.
Costura Hace referencia al proceso de aplicar el código mejorado al objeto de destino para generar un objeto proxy.
Apoderado Hace referencia al objeto proxy generado.
Aspecto Consejos y cortes de puntos juntos forman un aspecto.

Ejemplo:

Tipo de notificación:

notificar ilustrar
antes (pre-notificación) El método de asesoramiento se ejecuta antes de la llamada al método de destino
después (post notificación) El método de asesoramiento se llama después de que el método de destino devuelve o una excepción
después de la devolución (notificar después de la devolución) El método de notificación se llamará después de que el método de destino regrese
after-throwing (notificación de excepción de lanzamiento) Se llamará al método de notificación después de que el método de destino arroje una excepción.
alrededor (alrededor de notificación) El método de asesoramiento envolverá el método de destino.

3.5 Pasos de desarrollo de AOP

3.5.1 Agregar el paquete jar correspondiente

Método 1: Importar paquete jar

 Método 2: configuración de dependencia maven

<dependencies>
    <!-- spring核心包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springbean包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springcontext包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- spring表达式包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springAOP包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springAspects包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3.5.2 Espacio de nombres y especificación de etiquetas

Agregue el espacio de nombres aop y la especificación de la etiqueta aop al archivo de configuración de Spring:

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

3.5.3 Escritura de clases de aspecto

Defina una clase ordinaria de Java y agréguele métodos funcionales:

/**
 * 日志切面类:日志记录功能
 */
public class LogAspect {

    /**
     * 前置通知:在目标方法执行之前实现的功能
     */
    public void beforeMethod() {
        System.out.println("AOP日志记录:前置通知......");
    }
}

Código completo seccional:

/**
 * 日志切面类:日志记录功能
 */
public class LogAspect {

    /**
     * 前置通知:在目标方法执行之前实现的功能
     * 参数:JoinPoint连接点对象,可获取当前要执行的目标方法的信息
     */
    public void beforeMethod(JoinPoint joinPoint){
        //通过连接点对象获取方法的名称
        String methodName = joinPoint.getSignature().getName();
        //方法的参数值列表
        Object[] args = joinPoint.getArgs();
        System.out.println("AOP日志记录:前置通知......" + methodName + Arrays.toString(args));
    }
    /**
     * 后置通知:在目标方法执行之后实现的功能
     */
    public void afterMethod(JoinPoint joinPoint){
        //通过连接点对象获取方法的名称
        String methodName = joinPoint.getSignature().getName();
        //方法的参数值列表
        Object[] args = joinPoint.getArgs();
        System.out.println("AOP日志记录:后置通知......" + methodName + Arrays.toString(args));
    }

    /**
     * 返回通知:在目标方法有返回值之后实现的功能
     * 通过方法的参数获取目标方法的返回值数据
     */
    public void afterReturnMethod(JoinPoint joinPoint, Object resultValue){
        //通过连接点对象获取方法的名称
        String methodName = joinPoint.getSignature().getName();
        //方法的参数值列表
        Object[] args = joinPoint.getArgs();
        System.out.println("AOP日志记录:返回通知......" + methodName + Arrays.toString(args));
        System.out.println("方法的返回值为:" + resultValue);
    }

    /**
     * 异常通知:在目标方法抛出异常之后实现的功能
     * 通过方法参数获取目标方法抛出的异常对象
     */
    public void afterThrowMethod(JoinPoint joinPoint, Exception ex){
        //通过连接点对象获取方法的名称
        String methodName = joinPoint.getSignature().getName();
        //方法的参数值列表
        Object[] args = joinPoint.getArgs();
        System.out.println("AOP日志记录:异常通知......" + methodName + Arrays.toString(args));
        System.out.println("方法抛出的异常对象:" + ex);
    }

    /**
     * 环绕通知:综合以上所有的通知
     * 环绕通知必须配置ProceedingJoinPoint参数
     */
    public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("AOP日志记录:环绕通知之前置......");
        try {
            //手动执行目标方法
            //proceed()就是在执行目标方法,其返回值为目标方法的返回值
            Object resultValue = proceedingJoinPoint.proceed();
            System.out.println("AOP日志记录:环绕通知之返回......" + resultValue);
        } catch (Throwable throwable) {
            System.out.println("AOP日志记录:环绕通知之异常......" + throwable);
            throwable.printStackTrace();
        }
        System.out.println("AOP日志记录:环绕通知之后置......");
    }
}

3.5.4 Configurar AOP

  • Configure tanto la clase de destino como la clase de aspecto en el contenedor IOC;

  • Configure AOP: configure expresiones pointcut, configure clases de aspecto y configure notificaciones;

<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       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/util
		http://www.springframework.org/schema/util/spring-util.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--1.配置bean-->
    <bean id="userService" class="com.newcapec.service.impl.UserServiceImpl"/>
    <bean id="personService" class="com.newcapec.service.impl.PersonServiceImpl"/>
    <bean id="log" class="com.newcapec.aspect.LogAspect"/>

    <!--2.配置AOP-->
    <aop:config>
        <!--
            公用切点表达式
                id: 切点表达式的名称
                expression: 切点表达式的内容
            注:
                aop:pointcut定义在aop:config中,是公用级别的切点表达式
                aop:pointcut定义在aop:aspect中,是切面级别的切点表达式
     -->
        <aop:pointcut id="exp1" expression="execution(* com.newcapec.service.impl.*Impl.*(..))"/>
        <aop:pointcut id="exp2" expression="execution(public void com.newcapec.service.impl.UserServiceImpl.insertUser())"/>
        <aop:pointcut id="exp3" expression="execution(* com.newcapec.service.impl.UserServiceImpl.selectUser(..))"/>
        <aop:pointcut id="exp4" expression="execution(* com.newcapec.service.impl.PersonServiceImpl.insertPerson(..))"/>

        <!--2.1 配置切面类-->
        <aop:aspect ref="log">
            <!--2.2 配置通知-->
            <!--
                通知类型:
                    <aop:before>:表示前置通知
                    <aop:after>:表示后置通知
                    <aop:after-returning>:表示返回通知
                    <aop:after-throwing>:表示异常通知
                    <aop:around>:表示环绕通知
                通知类型属性:
                    method: 切面对象中执行该通知的方法,通知的方法名称
                    pointcut: 切点表达式
                        告知spring,通知应用在哪些方法(目标方法,业务逻辑方法)上
                    pointcut-ref: 引用公共切点表达式
                    returning: 在返回通知中接收方法返回值的参数名称
                    throwing: 在异常通知中接收方法抛出的异常对象的参数名称
            -->
            <!--2.3 配置切点表达式-->
            <!--
                编写规则:以execution()包裹起来的内容
                通配符:
                    1. 星号 *
                    2. 双句号 ..
                        使用在包中表示该包下以及子包下
                        使用在参数类型列表中表示不限制方法的参数类型
                示例:
                    1.单个方法,具体方法
                    语法:execution(方法访问修饰符 返回值类型 方法所在的包名.类名.方法名(参数类型,...))
                    例如:execution(public void com.newcapec.dao.UserDaoImpl.insertUser())
                    2.多个方法,批量方法:*符号作为通配符
                    语法:execution(* 方法所在的包名.类名.*(..))
                    例如:execution(* com.newcapec.dao.UserDaoImpl.*(..))
                    例如:execution(* com.newcapec.*.dao.*.*(..))
                    3.通配,所有方法
                    语法:execution(* *.*.*(..))
            -->

            <!--精准匹配-->
            <!--<aop:before method="beforeMethod" pointcut="execution(public void com.newcapec.service.impl.UserServiceImpl.insertUser())" />-->
            <!--模糊匹配-->
            <!--<aop:before method="beforeMethod" pointcut="execution(* com.newcapec.service..*Impl.*(..))"/>-->

            <aop:before method="beforeMethod" pointcut-ref="exp1"/>
            <aop:after method="afterMethod" pointcut-ref="exp2"/>
            <aop:after-returning method="afterReturnMethod" pointcut-ref="exp3" returning="resultValue"/>
            <aop:after-throwing method="afterThrowMethod" pointcut-ref="exp4" throwing="ex"/>
            <!--<aop:around method="aroundMethod" pointcut-ref="exp1"/>-->
        </aop:aspect>
    </aop:config>
</beans>

3.5.5 Pruebas

public class AOPTest {

    @Test
    public void test() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserService userService = ac.getBean("userService", UserService.class);

        userService.insertUser();

        System.out.println("----------------------------------");

        userService.updateUser();
        System.out.println("----------------------------------");

        userService.deleteUser(1);
        System.out.println("----------------------------------");

        userService.selectUser();
        System.out.println("----------------------------------");

        PersonService personService = ac.getBean("personService", PersonService.class);
        personService.insertPerson();
    }
}

Nota: después de que ocurra una excepción en el método de destino, la notificación de devolución no se ejecutará. Sin embargo, cuando no se produce ninguna excepción en el método de destino, la notificación de excepción no se ejecutará.

3.5.6 Prioridad de aspecto

<aop:aspect>El atributo de pedido se configura en la etiqueta y el valor del atributo es un número. Cuanto menor sea el número, mayor será la prioridad y antes se ejecutará la función de aspecto

/**
 * 另一个切面类
 */
public class OtherAspect {

    public void beforeM(){
        System.out.println("OtherAspect的beforeM方法.....");
    }
}
<bean id="other" class="com.newcapec.aspect.OtherAspect"/>

<aop:config>
    <!--
        切面优先级:决定哪个切面中通知先执行,哪个后执行
        在前置通知中:优先级高的先执行,优先级低的后执行
        在后置通知中:优先级高的后执行,优先级低的先执行
        <aop:aspect>标签中的order属性决定了优先级的高低,其值越小优先级越高
    -->
    <aop:aspect ref="log" order="2">
    </aop:aspect>
    <aop:aspect ref="other" order="1">
        <aop:before method="beforeM" pointcut-ref="exp1"/>
    </aop:aspect>
</aop:config>

Supongo que te gusta

Origin blog.csdn.net/ligonglanyuan/article/details/124787652
Recomendado
Clasificación