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); } }
- 优点:实现简单直接
- 缺点:日志代码维护不方便,代码复用性差
动态代理
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前有一些准备工作:
需要引入核心的aspect 包
在切面类中声明通知方法:
前置通知:@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.*(..))")
- 2.表示所有访问权限,所有包的下所有有类的所方法,都会被执行该前置通知方法
@Before(value="execution(* *.*(..))")
3.切面类方法是由程序员自己命名的,可按照规范命名
4.当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了!
5.当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 也可以通过id 来获取, 但是也要转成接口类型.
AOP-切入表达式
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 ======
注意事项和细节
- 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效。
- 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效。
- 切入表达式也可以对没有实现接口的类,进行切入。
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("-----------------"); } }