spring AOP切面编程

spring AOP切面编程

AOP 讲解:

​ 感兴趣的可以看看官方说明:spring-framework-5.3.8/docs/reference/html/core.html#aop

动态代理小案例

需求:一个接口有两个实现类,两个实现类实现接口的方法时,只有输出对象不同,其他操作一致。

//接口
public interface Vehicle {
	public void run();
}

//实现类
public class Car implements Vehicle {
	@Override
    public void run() {
            System.out.println("交通工具开始运行了...");
            System.out.println("小汽车在公路running..");
            System.out.println("交通工具停止运行了...");
        }
}

public class Ship implements Vehicle {
    @Override
    public void run() {
        System.out.println("交通工具开始运行了...");
        System.out.println("大轮船在水上running...");
        System.out.println("交通工具停止运行了...");
    }
}

​ 用传统的方法实现,单个方法简单直接,就是直接在单个的方法之间的调用,不够灵活。多个方法代码冗余,操作一多就很麻烦,不能够很好的解决问题。日志代码维护不方便,代码复用性差。

Vehicle vehicle = new Car();//可以切换成new Ship()
vehicle.run();

​ 我们可以使用动态代理的方式解决。动态代理解决思路,就是在调用方法时,使用反射机制,根据方法去决定调用哪个对象方法。

package com.nlc.spring.aop.proxy;
import com.nlc.spring.aop.proxy.SmartAnimalable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class VehicleProxyProvider {
    //设置一个将来要运行的对象,只要是实现了Vehicle 接口的
    private Vehicle target_vehicle;
    public VehicleProxyProvider(Vehicle target_vehicle) {
    	this.target_vehicle = target_vehicle;
    }
    //编写代码,返回一个Vehicle接口 的代理对象
    public Vehicle getProxy() {
        // 1.获取类加载对象
        ClassLoader loader = target_vehicle.getClass().getClassLoader();
        // 2.获取接口类型数组, 为什么是接口信息,不是方法,因为我们都是走接口的.
        Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();
        // 3. 创建InvocationHandler 对象- 以匿名内部类的方式方式来获取 InvocationHandler
        // 这个对象有一个方法是invoke 到时可以通过反射,动态调用目标对象的方法
        InvocationHandler invocationHandler = new InvocationHandler() {
        @Override
            //传入代理对象,目标方法,方法参数
            public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable
            {
                System.out.println("交通工具开始运行了...");
                //这个地方的方法,就是你调用时,动态传入的可能是run , 可能是hi 等
                Object result = method.invoke(target_vehicle, args);
                System.out.println("交通工具停止运行了...");
                return result;
            }
        };
        //将上面的loader, interfaces, invocationHandler 构建一个Vehicle的代理对象.
        Vehicle proxy = (Vehicle)Proxy.newProxyInstance(loader, interfaces,invocationHandler);
        //将创建的代理对象返回
        return proxy;
    }
}

测试

package com.nlc.spring.aop.proxy;
import com.nlc.spring.aop.proxy.MyProxyProvider;
public class Test {
    public static void main(String[] args) {
        //切换Vehicle 的实现类(对象)
        Vehicle vehicle = new Car();
        VehicleProxyProvider vehicleProxyProvider = new VehicleProxyProvider(vehicle);
        //获取对应的代理对象
        Vehicle proxy = vehicleProxyProvider.getProxy();
        System.out.println("proxy 编译类型是Vehicle");
        System.out.println("proxy 运行类型" + proxy.getClass());
        proxy.run();
        //动态代理的动态体现
        //1. proxy 运行类型是com.sun.proxy.$Proxy0 该类型被转型成Vehicle,因此可以调用Vehicle 的接口方法
        //2. 当执行run() 的时候会调用, 根据Java 的动态绑定机制, 这时直接调用Car 的run(),而是proxy 对象的invocationHandler 的invoke 方法(!!!!!!)
        //3. invoke 方法使用反射机制来调用run()方法注意这个run 方法也可以是 Vehicle 的其它方法, 这时就可以在调用run()方法前,进行前置处理和后置处理
        //4. 也就是说proxy 的target_vehicle 运行类型只要是实现了Vehicle 接口 ,就可以去调用不同的方法, 是动态的,变化的,底层就是使用反射完成的.     
    }
}

动态代理深入案例

需求说明

​ 有一个SmartAnimal 接口,可以完成简单的加减法, 要求在执行getSum()和getSub()时,输出执行前,执行过程,执行后的日志输出,请思考如何实现.

传统的解决思路,在各个方法的[前,执行过程, 后]输出日志

定义接口

public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}

方法实现

public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        System.out.println("日志--方法名--getSum 方法开始--参数:" + i + "," + j);
        float result = i + j;
        System.out.println("方法内部打印:result=" + result);
        System.out.println("日志--方法名--getSum 方法结束--结果:result=" + result);
        return result;
    }
    @Override
    public float getSub(float i, float j) {
        System.out.println("日志--方法名--getSub 方法开始--参数:" + i + "," + j);
        float result = i - j;
        System.out.println("方法内部打印:result=" + result);
        System.out.println("日志--方法名--getSub 方法结束--结果:result=" + result);
        return result;
    }
}

测试

public class AopTest {
@Test
public void smartDogTest() {
        SmartDog smartDog = new SmartDog();
        smartDog.getSum(5.23f, 6.89f);
        smartDog.getSub(5.23f, 6.89f);
    }
}
  1. 优点:实现简单直接
  2. 缺点:日志代码维护不方便,代码复用性差
动态代理
public class MyProxyProvider {
    private SmartAnimalable target_obj;
    // 构造器
    public MyProxyProvider(SmartAnimalable target_obj) {
   		 this.target_obj = target_obj;
        }
    public SmartAnimalable getProxy() {
        // 1.获取类加载对象
        ClassLoader loader = target_obj.getClass().getClassLoader();
        // 2.获取接口类型数组
        Class<?>[] interfaces = target_obj.getClass().getInterfaces();
        // 3.获取InvocationHandler 以匿名内部类的方式方式来获取InvocationHandler
        InvocationHandler h = new InvocationHandler() {
            // 4.以动态代理的方式调用目标对象的目标方法
            public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable {
                Object result = null;
                String methodName = method.getName();
                try {
                    // 1. 在调用目标方法之前打印“方法开始”日志
                    System.out.println("日志--方法名:" + methodName + "--方法开始--参数:"+ Arrays.asList(args));
                    // 2. 调用目标方法并接收返回值
                    result = method.invoke(target_obj, args);
                    // 3. 在目标方法结束后打印“方法结束”日志
                    System.out.println("日志--方法名:" + methodName + "--方法正常结束--结果:result=" + result);
                } catch (Exception e) {
                    // 4.如果目标方法抛出异常,打印“方法异常”日志
                    e.printStackTrace();
                    System.out.println("日志--方法名:" + methodName + "--方法抛出异常--异常类型:" + e.getClass().getName());
                } finally {
                    // 5.在finally 中打印“方法最终结束”日志
                    System.out.println("日志--方法名:" + methodName + "--方法最终结束");
                }
                // 6. 返回目标方法的返回值
                return result;
            }
        };
        //生成SmartAnimaleable 的代理对象
        //需要三个参数,
        //1.就是loader(获取类加载对象)
        //2.接口数组
        //3.InvocationHandler 对象[这个相对麻烦..]
        SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance( loader, interfaces, h);
        return proxy;
    }
}

实现接口

public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        return result;
    }
    @Override
    public float getSub(float i, float j) {
        float result = i - j;
        return result;
    }
}

测试

@Test
public void smartDogTestByProxy() {
    SmartAnimalable smartDog = new SmartDog();
    //1.使用动态代理的方式来调用
    //2.当使用动态代理调用是SmartDog 的各个函数的日志输出就可以不写了,有代理对象帮我们完成.
    MyProxyProvider provider = new MyProxyProvider(smartDog);
    smartDog = provider.getProxy();
    smartDog.getSum(5.23f, 6.89f);
    smartDog.getSub(5.23f, 6.89f);
}

在上面的代码中,我们的输出语句功能比较弱,在实际开发中,使用方法嵌入目标方法会更方便。

public class MyProxyProvider {
    private SmartAnimalable target_obj;
    // 构造器
    public MyProxyProvider(SmartAnimalable target_obj) {
    	this.target_obj = target_obj;
    }
    public void before(Object proxy, Method method, Object[] args) {
    // 1. 在调用目标方法之前打印“方法开始”日志
        System.out.println("before 日志--方法名:" + method.getName() + "--方法开始--参数:"+ Arrays.asList(args));
    }
    public void after(Method method, Object result) {
        // 3. 在目标方法结束后打印“方法结束”日志
        System.out.println("after 日志--方法名:" + method.getName() + "--方法正常结束--结果:result=" + result);
    }
    //可以更多方法
    public SmartAnimalable getProxy() {
        // 1.获取类加载对象
        ClassLoader loader = target_obj.getClass().getClassLoader();
        // 2.获取接口类型数组, 为什么是接口信息,而不是方法,是因为我们都是走接口.
        Class<?>[] interfaces = target_obj.getClass().getInterfaces();
        // 3. 创建InvocationHandler 对象- 以匿名内部类的方式方式来获取InvocationHandler
        InvocationHandler h = new InvocationHandler() {
        // 4.以动态代理的方式调用目标对象的目标方法
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                String methodName = method.getName();
                try {
                    before(proxy,method,args);//切入到目标方法前
                    // 2. 调用目标方法并接收返回值
                    result = method.invoke(target_obj, args);
                    after(method, result);//切入到目标方法后
                } catch (Exception e) {
                    // 4.如果目标方法抛出异常,打印“方法异常”日志
                    e.printStackTrace();
                    System.out.println("日志--方法名:" + methodName+ "-- 方法抛出异常-- 异常类型: " +
                    e.getClass().getName());
                } finally {
                    // 5.在finally 中打印“方法最终结束”日志
                    System.out.println("日志--方法名:" + methodName + "--方法最终结束");
                }
                // 6. 返回目标方法的返回值
                  return result;
            }
        };
        //生成SmartAnimaleable 的代理对象
        //需要三个参数,
        //1.就是loader(获取类加载对象)
        //2.接口数组
        //3.InvocationHandler 对象[这个相对麻烦..]
        SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }
}

这样写耦合度高。

优化:

自己写的一个切入类

public class nAOP {
    public static void before(Object proxy, Method method, Object[] args) {
        // 1. 在调用目标方法之前打印“方法开始”日志
        System.out.println("自己的切入类的before 日志--方法名:" + method.getName()+ "--方法开始--参数:"+ Arrays.asList(args));
    }
    public static void after(Method method, Object result) {
        // 3. 在目标方法结束后打印“方法结束”日志
        System.out.println("自己的切入类的after 日志--方法名:" + method.getName() + "--方法正常结束--结果:result=" + result);
    }
}

核心

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = null;
    String methodName = method.getName();
    try {
        //切入到目标方法前
        nAOP.before(proxy,method,args);
        // 2. 调用目标方法并接收返回值
        result = method.invoke(target_obj, args);
        //切入到目标方法后
        nAOP.after(method,result);
    } catch (Exception e) {
        // 4.如果目标方法抛出异常,打印“方法异常”日志
        e.printStackTrace();
        System.out.println("日志--方法名:" + methodName+ "--方法抛出异常--异常类型:" + e.getClass().getName());
    } finally {
        // 5.在finally 中打印“方法最终结束”日志
        System.out.println("日志--方法名:" + methodName + "--方法最终结束");
    }
    // 6. 返回目标方法的返回值
    return result;
}

还是不够灵活,复用性差。是一种硬编码(没有注解和反射支撑)。

spring AOP 入门

​ spring aop的底层是ASPECTJ。AOP 的全称(aspect oriented programming) ,面向切面编程。就是切面类的方法可以插入到任意类、任意方法前、后、异常时、最终进行使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yyTtS2k6-1688871244832)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707150731509.png)]

使用aop前有一些准备工作:

  1. 需要引入核心的aspect 包

  2. 在切面类中声明通知方法:

    ​ 前置通知:@Before

    ​ 返回通知:@AfterReturning

    ​ 异常通知:@AfterThrowing

    ​ 后置通知:@After

    ​ 环绕通知:@Around

举例

SmartAnimalAspect 作用就是去实现切面编程,原来的MyProxyProvider 类就可以去掉了.

package com.nlc.spring.aop.springaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// 使用切面编程替代原来的动态代理类,机制是一样的.
@Aspect //表示这个类是一个切面类,采用注解@Aspect 一定要记得配置开启基于注解的AOP<aop:aspectj-autoproxy/>,不然不会实现
@Component //声明这个组件需要加入到IOC 容器
public class SmartAnimalAspect {
    //这个就对应动态代理类的
    //System.out.println(" 日志-- 方法名: "+methodName+"-- 方法开始-- 参数:"+Arrays.asList(args));
    @Before(value = "execution(public float com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法前执行
    public void showBeginLog(JoinPoint joinPoint) {
        //JoinPoint joinPoint表示连接点
        System.out.println("前置通知");
        Signature signature = joinPoint.getSignature();
        // 1. 在调用目标方法之前打印“方法开始”日志
        System.out.println("日志--方法名:" + signature.getName() + "--方法开始--参数: "+ Arrays.asList(joinPoint.getArgs()));
    }
        //这个就对应动态代理类的
    //System.out.println("日志--方法名: "+methodName+"--方法正常结束--结果: result="+result);
    @AfterReturning(value = "execution(public float com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法执行结束后执行
    public void showSuccessEndLog(JoinPoint joinPoint) {
        System.out.println("返回通知");
        Signature signature = joinPoint.getSignature();
        // 3. 在目标方法结束后打印“方法结束”日志
        System.out.println("日志--方法名:" + signature.getName() + "--方法正常结束--~");
    }
    //这个就对应动态代理类的
    //System.out.println("日志--方法名: "+methodName+"--方法抛出异常--异常类型:"+e.getClass().getName());
    @AfterThrowing(value = "execution(public float  com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法异常时执行
    public void showExceptionLog() {
    	System.out.println("异常通知");
    }
    //这个就对应动态代理类的
    //System.out.println("日志--方法名:"+methodName+"--方法最终结束");
    @After(value = "execution(public float  com.nlc.spring.aop.springaop.SmartDog.getSum(float ,float))")
    //表示要在符合路径和方法参数类型的方法最后执行
    public void showFinallyEndLog() {
   		 System.out.println("最终通知");
    }
}

配置beans.xml

<?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: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/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">
    <!-- 配置自动扫描的包,根据实际情况配置即可-->
    <context:component-scan base-package="com.nlc.spring.aop.aspectj"/>
    <!-- 开启基于注解的AOP 功能-->
    <aop:aspectj-autoproxy/>
</beans>

测试

@Test
public void smartDogTestByProxy() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans6.xml");
    //通过接口来
    SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
    float sum = bean.getSum(101.0f, 11.2f);
    System.out.println("sum= " + sum);
}
注意
  1. 切入表达式可以使用模糊配置
@Before(value="execution(* com.nlc.aop.proxy.SmartDog.*(..))")
  1. 表示所有访问权限,所有包的下所有有类的所方法,都会被执行该前置通知方法
@Before(value="execution(* *.*(..))")
  1. 切面类方法是由程序员自己命名的,可按照规范命名

  2. 当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了!

  3. 当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 也可以通过id 来获取, 但是也要转成接口类型.

AOP-切入表达式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y4oYbpel-1688871244833)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707153712902.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WtfzqXD-1688871244833)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707153732989.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uNfwZUZz-1688871244833)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707153744950.png)]

AOP-切入点表达式重用

为了统一管理切入点表达式,可以使用切入点表达式重用技术。

//=====AOP-切入点表达式重用start ======
/*
* 这样定义的一个切入点表达式,就可以在其它地方直接使用
*/
@Pointcut(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")
public void myPointCut() {
}
// @Before(value="execution(public floatcom.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")没有用表达式重用
@Before(value = "myPointCut()")//采用表达式重用
public void showBeginLog(JoinPoint joinPoint) { //前置方法
    //得到方法的签名
    // 调用前置通知对应的方法签名:public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float)
    Signature signature = joinPoint.getSignature();
    //得到方法名.
    String method_name = signature.getName();
    //得到参数
    Object[] args = joinPoint.getArgs();
    System.out.println("前置通知" + "--调用的方法是" + method_name + "--参数是--" + Arrays.asList(args));
}
  //@After(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")
@After(value = "myPointCut()")
public void showFinallyEndLog() {
	System.out.println("最终通知-- AOP-切入点表达式重用");
}
/**
* returning = "res", Object res 名称保持一致
* @param joinPoint
* @param res 调用getSum() 返回的结果
*/
    @AfterReturning(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
	System.out.println("返回通知" + "--结果是--" + res);
}
@AfterThrowing(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
	System.out.println("异常通知-- 异常信息--" + throwable);
}
//=====AOP-切入点表达式重用end ======
注意事项和细节
  1. 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效。
  2. 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效。
  3. 切入表达式也可以对没有实现接口的类,进行切入。

AOP-JoinPoint

通过JoinPoint 可以获取到调用方法的信息。

public void beforeMethod(JoinPoint joinPoint){
        joinPoint.getSignature().getName(); // 获取目标方法名
        joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
        joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
        joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
        Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
        joinPoint.getTarget(); // 获取被代理的对象
        joinPoint.getThis(); // 获取代理对象自己
}

AOP-返回通知获取结果

查看@AfterReturning注解的源码可以发现里面有一个属性returning,我们只要把returning属性加到表达式后面就可以接收方法的返回值。

@AfterReturning(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
	System.out.println("返回通知" + "--结果是--" + res );
}

AOP-异常通知中获取异常

查看@AfterThrowing注解的源码可以发现里面有一个属性throwing,我们只要把throwing属性加到表达式后面就可以接收方法的异常情况。

@AfterThrowing(value = "execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
	System.out.println("异常通知-- 异常信息--" + throwable);
}

AOP-环绕通知

环绕通知可以完成其它四个通知要做的事情。

@Aspect //表示这个类是一个切面类
@Component //需要加入到IOC 容器
public class SmartAnimalAspect {
    //=====环绕通知start=====
    @Around(value="execution(public float com.nlc.spring.aop.joinpoint.SmartDog.getSum(float, float))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
            Object result = null;
            String methodName = joinPoint.getSignature().getName();
            try {
                        //1.相当于前置通知完成的事情
                        Object[] args = joinPoint.getArgs();
                        List<Object> argList = Arrays.asList(args);
                        System.out.println("AOP 环绕通知--" + methodName + "方法开始了--参数有:  " + argList);
                        //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
                        result = joinPoint.proceed();
                        //2.相当于返回通知完成的事情
                        System.out.println("AOP 环绕通知" + methodName + "方法结束了--结果是:"+ result);	
            } catch (Throwable throwable) {
                //3.相当于异常通知完成的事情
                System.out.println("AOP 环绕通知" + methodName + "方法抛异常了--异常对象:" + throwable);
            } finally {
                //4.相当于最终通知完成的事情
                System.out.println("AOP 后置通知" + methodName + "方法最终结束了...");
            }
            return result;
   	 }
}
//=====环绕通知end=====

AOP-切面优先级问题

基本语法:@order(value=n) 来控制n 值越小,优先级越高.

直接在方法上写好注解就可以使用。切面类方法执行和返回信息顺序类似Filter 的过滤链式调用机制,但是前面和返回相反,后执行的会被先执行的包裹起来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QaXjjPmk-1688871244833)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20230707160803527.png)]

AOP-基于XML 配置AOP

创建SmartAnimalAspect类

public class SmartAnimalAspect {
    public void showBeginLog(JoinPoint joinPoint) { //前置通知
            //得到方法的签名
                Signature signature = joinPoint.getSignature();
            //得到方法名.
            String method_name = signature.getName();
            //得到参数
            Object[] args = joinPoint.getArgs();
            System.out.println("XML 方式: 前置通知" + "--调用的方法是" + method_name + "--参数是--" + Arrays.asList(args));
    }
    public void showFinallyEndLog() {
    	System.out.println("XML 方式: 最终通知-- AOP-切入点表达式重用");
    }
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
    	System.out.println("XML 方式: 返回通知" + "--结果是--" + res);
    }
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
    	System.out.println("XML 方式: 异常通知-- 异常信息--" + throwable);
    }
}

创建配置文件

<?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: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/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">
<!-- 配置自动扫描的包-->
<context:component-scan base-package="com.nlc.spring.aop.xml"/>
<!-- 开启基于注解的AOP 功能-->
<aop:aspectj-autoproxy/>
<!-- 配置SmartAnimalAspect bean -->
<bean id="smartAnimalAspect" class="com.nlc.spring.aop.xml.SmartAnimalAspect"/>
<!--配置SmartDog-->
<bean class="com.nlc.spring.aop.xml.SmartDog" id="smartDog"/>
    <aop:config>
        <!-- 配置统一切入点-->
        <aop:pointcut expression="execution(public float com.nlc.spring.aop.xml.SmartDog.getSum(float, float))"  id="myPointCut"/>
        <aop:aspect ref="smartAnimalAspect" order="1">
                <!-- 配置各个通知对应的切入点-->
                <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
                <aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" returning="res"/>
                <aop:after-throwing method="showExceptionLog"  pointcut-ref="myPointCut" throwing="throwable"/>
                <aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
        </aop:aspect>
   </aop:config>
</beans>

测试

public class AopXMLTest {
    /**
    * 切面编程aop:aspectj
    */
    @Test
    public void testAopJoinPoint() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("aop_ioc.xml");
        SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
        bean.getSum(10.0f, 11.2f);
        System.out.println("-----------------");
    }
}

=“showSuccessEndLog” pointcut-ref=“myPointCut” returning=“res”/>
<aop:after-throwing method=“showExceptionLog” pointcut-ref=“myPointCut” throwing=“throwable”/>
<aop:after method=“showFinallyEndLog” pointcut-ref=“myPointCut”/>
</aop:aspect>
</aop:config>


测试

```java
public class AopXMLTest {
    /**
    * 切面编程aop:aspectj
    */
    @Test
    public void testAopJoinPoint() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("aop_ioc.xml");
        SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
        bean.getSum(10.0f, 11.2f);
        System.out.println("-----------------");
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晨犀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值