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);
}
注意
- 切入表达式可以使用模糊配置
@Before(value="execution(* com.nlc.aop.proxy.SmartDog.*(..))")
- 表示所有访问权限,所有包的下所有有类的所方法,都会被执行该前置通知方法
@Before(value="execution(* *.*(..))")
-
切面类方法是由程序员自己命名的,可按照规范命名
-
当spring 容器开启了 aop:aspectj-autoproxy/ , 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了!
-
当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 ======
注意事项和细节
- 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效。
- 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效。
- 切入表达式也可以对没有实现接口的类,进行切入。
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("-----------------");
}
}