AOP切面编程
- 💗AOP-官方文档
- 🍝AOP 讲解
- 🍝AOP APIs
- 💗动态代理
- 🍝初探动态代理
- 🍝动态代理深入
- 🍝AOP问题提出
- 📗使用土方法解决
- 📗 对土方法解耦-开发最简单的AOP类
- 📗 土方法缺点
上文中, 我们学习到了 Spring系列三:基于注解配置bean
接下来我们学习, AOP切面编程
Spring项目
💗AOP-官方文档
🍝AOP 讲解
AOP 讲解: spring-framework-5.3.8\docs\reference\html/core.html
🍝AOP APIs
AOP APIs: spring-framework-5.3.8\docs\reference\html/core.html
spring-framework-5.3.8/docs/javadoc-api/index.html
💗动态代理
🍝初探动态代理
需求说明
1.由Vehicle (交通工具接口, 有一个run方法), 下面有两个类 Car 和 Ship
2.当运行Car对象的 run 方法 和 ship对象的 run 方法时, 输入如下内容
交通工具开始运行了…
轮船在海上航行…
交通工具停止运行了…
交通工具开始运行了…
小汽车在路上跑…
交通工具停止运行了…
解决方案一: 传统方案
1.新建com.zzw.spring.aop.proxy.Vehicle
接口
//接口, 该接口有run方法
public interface Vehicle {void run();
}
2.新建com.zzw.spring.aop.proxy.Car
public class Car implements Vehicle {@Overridepublic void run() {System.out.println("交通工具开始运行了....");System.out.println("小汽车在路上 running....");System.out.println("交通工具停止运行了....");}
}
3.新建com.zzw.spring.aop.proxy.Ship
public class Ship implements Vehicle {@Overridepublic void run() {System.out.println("交通工具开始运行了....");System.out.println("大轮船在路上 running....");System.out.println("交通工具停止运行了....");}
}
4.新建com.zzw.spring.aop.proxy.TestVehicle
public class TestVehicle {@Testpublic void run() {//OOP基础=>java基础Vehicle vehicle = new Ship();//动态绑定vehicle.run();}
}
来思考一下, 这个解决方案好吗? ====> 代码冗余, 其实就是单个对象的调用, 并没有很好的解决.
解决方案二: 动态代理方式
动态代理解决思路: 在调用方法时, 使用反射机制, 根据方法去决定调用哪个对象方法
1.新建com.zzw.spring.aop.proxy.VehicleProxyProvider
public class VehicleProxyProvider {//定义一个属性//target_vehicle 表示真正要执行的对象//该对象实现了Vehicle接口private Vehicle target_vehicle;//构造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle = target_vehicle;}//编写一个方法, 可以返回一个代理对象public Vehicle getProxy() {//得到类加载器ClassLoader classLoader =target_vehicle.getClass().getClassLoader();//得到要代理对象/被执行对象 的接口信息, 底层是通过接口来完成调用Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();//创建InvocationHandler 对象//因为 InvocationHandler 是接口, 所以我们可以通过匿名对象的方式来创建该对象/*** public interface InvocationHandler {* public Object invoke(Object proxy, Method method, Object[] args)* throws Throwable;* }* invoke 方法是将来执行target_vehicle的方法时, 会调用到*/InvocationHandler invocationHandler = new InvocationHandler() {/*class VehicleProxyProvider$01 implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("交通工具开始运行了....");//这里是我们的反射基础 => OOPObject result = method.invoke(target_vehicle, args);System.out.println("交通工具停止运行了....");return result;}}InvocationHandler invocationHandler = new VehicleProxyProvider$01();*//*** invoke 方法是将来执行我们的target_vehicle的方法时, 会调用到** @param proxy 表示代理对象* @param method 就是通过代理对象调用方法时, 的那个方法 代理对象.run()* @param args 表示调用 代理对象.run(xx) 传入的参数* @return 表示 代理对象.run(xx) 执行后的结果.* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("交通工具开始运行了....");//这里是我们的反射基础 => OOP//method 是 public abstract void com.zzw.spring.aop.proxy.Vehicle.run()//target_vehicle 是 Ship对象//args 是 null//这里通过反射+动态绑定机制, 就会执行到被代理对象的方法//执行完毕就返回Object result = method.invoke(target_vehicle, args);System.out.println("交通工具停止运行了....");return result;}};/*public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)解读1.Proxy.newProxyInstance() 可以返回一个代理对象2.ClassLoader loader: 类加载器,3.Class<?>[] interfaces 就是将来要代理的对象的接口信息4.InvocationHandler h 调用处理器/对象, 有一个非常重要的方法invoke*/Vehicle proxy =(Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}
2.修改com.zzw.spring.aop.proxy.Vehicle
public interface Vehicle {void run();public String fly(int height);
}
3.修改com.zzw.spring.aop.proxy.Ship
public class Ship implements Vehicle {@Overridepublic void run() {//System.out.println("交通工具开始运行了....");System.out.println("大轮船在路上 running....");//System.out.println("交通工具停止运行了....");}@Overridepublic String fly(int height) {return "轮船可以飞行 高度=" + height + "米";}
}
4.com.zzw.spring.aop.proxy.TestVehicle
public class TestVehicle {@Testpublic void proxyRun() {//创建Ship对象Ship ship = new Ship();//创建VehicleProxyProvider对象, 并且我们要传入代理的对象VehicleProxyProvider vehicleProxyProvider= new VehicleProxyProvider(ship);//获取代理对象, 该对象可以代理执行方法//解读//1.proxy 编译类型Vehicle,//2.运行类型 是代理类型, 即 class com.sun.proxy.$Proxy8Vehicle proxy = vehicleProxyProvider.getProxy();System.out.println("proxy的编译类型是 Vehicle");System.out.println("proxy的运行类型是" + proxy.getClass());//下面解读/debug怎么执行到 代理对象的 public Object invoke(Object proxy, Method method, Object[] args)//梳理完毕, proxy的编译类型是Vehicle, 运行类型是Proxy class com.sun.proxy.$Proxy8//所以当执行run方法时, 会执行到 代理对象的invoke//如果体现动态 [1.被代理的对象 2.方法]//proxy.run();String result = proxy.fly(10000);System.out.println("result=" + result);System.out.println("ok");}
5.debug
🍝动态代理深入
需求说明
1.有一个SmartAnimal
接口, 可以完成简单的加减法, 要求在执行 getSum()
和 getSub()
时, 输出执行前, 执行过程, 执行后的日志结果. 输出内容如下:
日志-方法名-getSum-参数 1.5 4.5
方法内部打印result = 6.0
日志-方法名-getSum-结果result= 6.0
=================================
日志-方法名-getSub-参数 1.4 3.3
方法内部打印result = -1.9
日志-方法名-getSub-结果result= -1.9
解决方案一: 传统方案
1.新建com.zzw.spring.aop.proxy2.SmartAnimalAble
接口
public interface SmartAnimalAble {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}
2.com.zzw.spring.aop.proxy2.SmartCat
public class SmartCat implements SmartAnimalAble {@Overridepublic 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= " + (i + j));return result;}@Overridepublic 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= " + (i - j));return result;}
}
3.com.zzw.spring.aop.proxy2.AopTest
public class AopTest {@Testpublic void run() {SmartAnimalAble smartAnimalAble = new SmartCat();smartAnimalAble.getSum(1.5f, 4.5f);System.out.println("=================================");smartAnimalAble.getSub(1.4f, 3.3f);}
}
解决方案二: 动态代理方式
考虑代理对象调用方法(底层是反射调用)时, 可能出现的异常
- [横切关注点]
1.新建com.zzw.spring.aop.proxy2.MyProxyProvider
//可以返回一个动态代理对象, 可以执行SmartCat对象的方法
public class MyProxyProvider {//这是一个属性, 是我们要执行的目标对象//该对象实现了SmartAnimalAble接口private SmartAnimalAble target_obj;//构造器MyProxyProvider(SmartAnimalAble target_obj) {this.target_obj = target_obj;}//编写一个方法, 可以返回一个代理对象//该代理对象可以执行目标方法public SmartAnimalAble getProxy() {//1.得到类加载器ClassLoader classLoader =target_obj.getClass().getClassLoader();//2.得到要执行的目标对象的接口信息Class<?>[] interfaces = target_obj.getClass().getInterfaces();//3.创建InvocationHandler 对象InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;try {System.out.println("方法执行前-日志-方法名-" + name + "-参数 "+ Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知//使用反射调用方法result = method.invoke(target_obj, args);System.out.println("方法执行正常结束-日志-方法名-" + name + "-结果result= "+ result);//这里从aop的角度看, 也是一个横切关注点-返回通知return result;} catch (Exception e) {e.printStackTrace();//如果反射执行方法时, 出现异常, 就会进入到catch{}System.out.println("方法执行异常-日志-方法名-" + name + "-异常类型="+ e.getClass().getName());//这里从aop的角度看, 又是一个横切关注点-异常通知} finally {//不管你是否出现了异常, 最终都会执行到 finally {}//这里从aop的角度看, 还是一个横切关注点-最终通知System.out.println("方法最终结束-日志-方法名-" + name);}return result;}};//创建代理对象SmartAnimalAble proxy =(SmartAnimalAble) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}
2.修改com.zzw.spring.aop.proxy2.SmartCat
public class SmartCat implements SmartAnimalAble {@Overridepublic 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= " + (i + j));return result;}@Overridepublic 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= " + (i - j));return result;}
}
3.com.zzw.spring.aop.proxy2.AopTest
public class AopTest {@Testpublic void smartCatTestProxy() {//创建SmartCat对象SmartAnimalAble smartAnimalAble = new SmartCat();MyProxyProvider myProxyProvider= new MyProxyProvider(smartAnimalAble);//获取代理对象, 该对象可以代理执行方法SmartAnimalAble proxy = myProxyProvider.getProxy();System.out.println("proxy的编译类型是 SmartAnimalAble");System.out.println("proxy的运行类型是 " + proxy.getClass());//proxy的编译类型是SmartAnimalAble, 运行类型是 Class com.sun.proxy.$Proxy8//所以当执行getSum方法时, 会执行到 代理对象的invokeproxy.getSum(1.2f, 2.4f);System.out.println("=================================");proxy.getSub(1.3f, 4.5f);System.out.println("ok");}
}
🍝AOP问题提出
在MyProxyProvider.java
中, 我们的输出语句功能比较弱, 在实际开发中, 我们希望是以一个方法的形式, 嵌入到真正执行的目标方法前.
如图分析
📗使用土方法解决
需求分析
使用土方法解决前面的问题, 后面使用Spring的AOP组件完成
1.先建一个包, 把相关文件拷贝过来, 进行修改完成. ----这里只是模拟, 并没有真的新建包
//我们的一个方法, 在目标对象执行前执行
public void before(Method method, Object[] args) {System.out.println("before方法执行前-日志-方法名-" + method.getName() + "-参数 "+ Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知
}//我们的一个方法, 在目标对象执行后执行
public void after(Method method, Object result) {System.out.println("after方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "+ result);//这里从aop的角度看, 也是一个横切关注点-返回通知
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;before(method, args);//使用反射调用方法result = method.invoke(target_obj, args);after(method, result);return result;
}
2.该方法问题分析: 耦合度高
📗 对土方法解耦-开发最简单的AOP类
1.新建com.zzw.spring.aop.proxy2.ZzwAOP
public class ZzwAOP {//我们的一个方法, 在目标对象执行前执行public static void before(Method method, Object[] args) {System.out.println("ZzwHsp-方法执行前-日志-方法名-" + method.getName() + "-参数 "+ Arrays.asList(args));//这里从aop的角度看,就是一个横切关注点-前置通知}//我们的一个方法, 在目标对象执行后执行public static void after(Method method, Object result) {System.out.println("ZzwHsp-方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "+ result);//这里从aop的角度看, 也是一个横切关注点-返回通知}
}
2.修改com.zzw.spring.aop.proxy2.MyProxyProvider
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();//方法名Object result = null;try {//before(method, args);ZzwAOP.before(method, args);//使用反射调用方法result = method.invoke(target_obj, args);//after(method, result);ZzwAOP.after(method, result);return result;} catch (Exception e) {}
}
📗 土方法缺点
土方法 不够灵活;
土方法 复用性差;
土方法 是一种硬编码 (因为没有注解和反射支撑)
Spring AOP 闪亮登场 - 底层是ASPECTJ