首页IT科技dayone的中文意思是(day08-AOP-01)

dayone的中文意思是(day08-AOP-01)

时间2025-06-21 02:00:08分类IT科技浏览4589
导读:AOP 1.官方文档...

AOP

1.官方文档

AOP讲解:下载的spring文件-->spring-framework-5.3.8/docs/reference/html/core.html#aop

AOP APIs:下载的spring文件-->spring-framework-5.3.8/docs/reference/html/core.html#aop-api

2.动态代理

2.1案例说明

需求说明:

有Vehicle(交通工具接口            ,有一个run方法)                    ,下面有两个实现类Car      ,Ship

当运行Car对象的run()方法和Ship对象的run()方法时         ,输出如下内容                    ,注意观察前后有统一的输出             。

请思考如何完成?

2.2传统方式解决

Vehicle接口:

package com.li.proxy; /** * @author 李 * @version 1.0 * 接口         ,定义了run方法 */ public interface Vehicle { public void run(); }

Ship类      ,实现Vehicle接口:

package com.li.proxy; /** * @author 李 * @version 1.0 */ public class Ship implements Vehicle { @Override public void run() { System.out.println("交通工具开始运行了..."); System.out.println("大轮船在水上 running..."); System.out.println("交通工具停止运行了..."); } }

Car类                    ,实现Vehicle接口:

package com.li.proxy; /** * @author 李 * @version 1.0 */ public class Car implements Vehicle { @Override public void run() { System.out.println("交通工具开始运行了..."); System.out.println("小汽车在公路 running..."); System.out.println("交通工具停止运行了..."); } }

TestVehicle测试类:

package com.li.proxy; import org.testng.annotations.Test; /** * @author 李 * @version 1.0 */ public class TestVehicle { @Test public void run() { Vehicle vehicle = new Car();//Vehicle vehicle = new Ship(); vehicle.run();//动态绑定            ,根据实际运行类型调用run方法 } }

上面的方式   ,代码冗余                    ,其实就是单个对象的调用                ,并没有很好的解决问题                  。

2.3动态代理方式解决

解决思路:在调用方法的时候,使用反射机制                ,根据方法去决定调用哪个对象方法

Vehicle接口不变:

package com.li.proxy; /** * @author 李 * @version 1.0 * 接口                    ,定义了run方法 */ public interface Vehicle { public void run(); }

Ship:

package com.li.proxy; /** * @author 李 * @version 1.0 */ public class Ship implements Vehicle { @Override public void run() { System.out.println("大轮船在水上 running..."); } }

Car:

package com.li.proxy; /** * @author 李 * @version 1.0 */ public class Car implements Vehicle { @Override public void run() { System.out.println("小汽车在公路 running..."); } }

创建VehicleProxyProvider   ,该类返回一个代理对象:

package com.li.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author 李 * @version 1.0 * VehicleProxyProvider类可以返回一个代理对象 */ public class VehicleProxyProvider { //定义一个属性 //target_vehicle 表示真正要执行的对象 //要求该对象的类实现了Vehicle接口 private Vehicle target_vehicle; //构造器 public VehicleProxyProvider(Vehicle target_vehicle) { this.target_vehicle = target_vehicle; } //编写一个方法            ,可以返回一个代理对象 public Vehicle getProxy() { //(1)得到类加载器 ClassLoader classLoader = target_vehicle.getClass().getClassLoader(); //(2)得到要代理的对象/被执行的对象 的接口信息                    ,底层通过接口来完成调用 Class<?>[] interfaces = target_vehicle.getClass().getInterfaces(); //(3)创建一个调用处理对象 /** * public interface InvocationHandler { * public Object invoke(Object proxy, Method method, Object[] args) * throws Throwable; * } * invoke 方法在将来执行我们的 target_vehicle的方法时      ,会调用到 */ //如上         ,因为InvocationHandler是接口                    ,不能直接实例化 // 以匿名内部类的方式来获取 InvocationHandler 对象 //这个对象有一个方法:invoke, 到时可以通过反射         ,动态调用目标对象的方法 InvocationHandler invocationHandler = new InvocationHandler() { /** * invoke()方法      ,在将来执行我们的target_vehicle的方法时会调用到 * @param proxy 表示代理对象 * @param method 就是通过代理对象调用方法时                    ,的哪个方法 * @param args 表示调用代理对象的方法时            ,传入方法的参数 * @return 表示代理对象.方法(xx) 执行后的结果 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("交通工具开始运行了..."); //这个地方的方法,就是你调用时动态传入的,可能是 run , 可能是 hi 等 Object result = method.invoke(target_vehicle, args); System.out.println("交通工具停止运行了..."); return result; } }; /*(4) public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 1.Proxy.newProxyInstance() 可以返回一个代理对象 2.ClassLoader loader :类加载器 3.Class<?>[] interfaces:要代理的对象的接口信息 4.InvocationHandler h:调用处理器/对象   ,该对象有一个非常重要的方法-invoke */ //将上面的 loader, interfaces, invocationHandler构建一个 Vehicle的代理对象. Vehicle proxy = (Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); return proxy; } }

TestVehicle测试类:

@Test public void proxyRun(){ //创建Ship对象 Vehicle vehicle = new Ship(); //创建VehicleProxyProvider对象                    ,并且传入要代理的对象 VehicleProxyProvider vehicleProxyProvider = new VehicleProxyProvider(vehicle); //获取代理对象                ,该对象可以代理执行方法 //1.proxy 编译类型是Vehicle //2.proxy 运行类型是 代理类型: class com.sun.proxy.$Proxy3 Vehicle proxy = vehicleProxyProvider.getProxy(); System.out.println("proxy 的运行类型是="+proxy.getClass()); proxy.run(); }

2.4动态代理机制debug

如上所示,当执行proxy.run();时                ,成功输出了目标信息                    ,那么代码是怎么执行到代理对象的invoke方法上的?

1.在proxy.run();旁打上断点   ,点击step into            ,光标跳转至VehicleProxyProvider的invoke方法:

2.点击step over                    ,光标跳转到下一行      ,此时method         ,target_vehicle代表了什么?

如上                    ,此时的method就是我们Vehicle接口中的run方法         ,target_vehicle对象的实际运行类型是Ship      ,因为我们没有传值进去所以args为null       。

3.在这行点击step into                    ,进入invoke方法的源码            ,点击step over跳到return ma.invoke(obj, args);:

在这行点击step into:

可以看到   ,底层最终执行了invoke0()方法:

这里可以发现method 就是Vehicle的run方法                    ,该方法会通过动态绑定                ,根据实际运行对象的类型(在这里即Ship),调用Ship中的run方法;var2就是我们调用代理对象的方法时传入的参数                ,这里为null:

4.在这行再次点击step into                    ,可以看到光标跳转到了Ship对象的run方法中   ,此时调用了Ship中的run方法:

5.点击step over            ,因为底层代码执行完毕          。光标层层返回到之前的method.invoke(target_vehicle,args)语句:

6.因为Ship的run方法返回void                    ,因此result的值为null:

7.当执行到return result;时      ,相当于使用代理对象执行run方法执行完毕         ,因此返回上一层:

至此                    ,总体的流程就执行完毕了                  。

总结梳理:

1.proxy的编译类型是Vehicle         ,因此可以调用run方法;proxy的运行类型是class com.sun.proxy.$Proxy3      ,所以当执行run方法时会执行到代理对象的invoke方法

2.invoke 方法使用反射机制来调用 run()方法(注意这个 run 方法也可以是Vehicle 的其它方法)                    ,这时就可以在调用 run()方法前            ,进行前置处理和后置处理

3.也就是说 proxy 的 target_vehicle 运行类型只要是实现了 Vehicle 接口   ,就可以去调用不同的方法                    ,是动态的                ,变化的,底层就是使用反射完成的          。

3.动态代理练习

3.1案例说明

需求说明:

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

方法执行开始-日志-方法名-getSub-参数 [10.0, 2.0] 方法内部打印 result = 8.0 方法执行正常结束-日志-方法名-getSub-结果 result= 8.0 //可能的异常信息 方法最终结束-日志-方法名-getSub//finally的输出 ====================== 方法执行开始-日志-方法名-getSum-参数 [10.0, 2.0] 方法内部打印 result = 12.0 方法执行正常结束-日志-方法名-getSum-结果 result= 12.0 //可能的异常信息 方法最终结束-日志-方法名-getSum//finally的输出 请使用传统方法完成 请使用动态代理方式完成                    ,并要求考虑代理对象调用方法(底层是反射调用)时      ,可能出现的异常

3.2传统方式解决

SmartAnimal接口:

package com.li.aop.proxy2; /** * @author 李 * @version 1.0 */ public interface SmartAnimal { //求和 float getSum(float a, float b); //求差 float getSub(float a, float b); }

SmartCat类实现接口SmartAnimal:

package com.li.aop.proxy2; /** * @author 李 * @version 1.0 */ public class SmartCat implements SmartAnimal { @Override public float getSum(float a, float b) { System.out.println("日志-方法名-getSum-参数 " + a + " " + b); float result = a + b; System.out.println("方法内部打印 result = " + result); System.out.println("日志-方法名-getSum-结果result= " + result); return result; } @Override public float getSub(float a, float b) { System.out.println("日志-方法名-getSub-参数 " + a + " " + b); float result = a - b; System.out.println("方法内部打印 result = " + result); System.out.println("日志-方法名-getSub-结果result= " + result); return result; } }

AopTest测试类:

package com.li.aop.proxy2; import org.testng.annotations.Test; /** * @author 李 * @version 1.0 */ public class AopTest { @Test public void testSmartAnimal() { SmartAnimal smartAnimal = new SmartCat(); smartAnimal.getSum(10, 2); System.out.println("======================="); smartAnimal.getSub(10, 2); } }

传统方法的特点:

优点:实现简单直接

缺点:日志代码维护不方便         ,代码复用性差

解决思路:

使用动态代理来更好地处理日志记录问题 其他比如封装函数                    ,或者类的继承在这里都不是特别合适

3.3动态代理方法解决

SmartAnimal接口不变       。

SmartDog实现SmartAnimal接口:

package com.li.aop.proxy2; /** * @author 李 * @version 1.0 */ public class SmartDog implements SmartAnimal { @Override public float getSum(float a, float b) { float result = a + b; System.out.println("方法内部打印 result = " + result); return result; } @Override public float getSub(float a, float b) { float result = a - b; System.out.println("方法内部打印 result = " + result); return result; } }

MyProxyProvider类:

package com.li.aop.proxy2; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; /** * @author 李 * @version 1.0 * 返回一个动态代理对象         ,可以执行被代理的对象的方法 */ public class MyProxyProvider { //定义要执行的目标对象      ,该对象需要实现 SmartAnimal接口 private SmartAnimal target_animal; //构造器 public MyProxyProvider(SmartAnimal target_animal) { this.target_animal = target_animal; } //定义方法返回代理对象                    ,该代理对象可以执行目标对象 public SmartAnimal getProxy() { //(1)先得到类加载器对象 ClassLoader classLoader = target_animal.getClass().getClassLoader(); //(2)得到要执行的目标对象的接口信息 Class<?>[] interfaces = target_animal.getClass().getInterfaces(); //(3)使用匿名内部类 创建 InvocationHandler对象 InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { System.out.println("方法执行开始-日志-方法名-" + method.getName() + "-参数 " + Arrays.toString(args));//这里从AOP的角度看            ,就是一个横切关注点-前置通知 //使用反射真正调用方法 result = method.invoke(target_animal, args); System.out.println("方法执行正常结束-日志-方法名-" + method.getName() + "-结果 result = " + result);//也是一个横切关注点-返回通知 } catch (Exception e) { //如果反射出现异常   ,就会进入到catch块 System.out.println("方法执行异常-日志-方法名" + method.getName() + "-异常类型=" + e.getClass().getName());//也是一个横切关注点-异常通知 e.printStackTrace(); } finally {//无论是否出现异常                    ,最终都会执行到 finally{} //也是一个横切关注点-最终通知 System.out.println("方法最终结束-日志-方法名-" + method.getName()); } return result; } }; //(4)创建代理对象 SmartAnimal proxy = (SmartAnimal) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); return proxy; } }

测试方法:

@Test public void testSmartAnimal() { SmartAnimal smartAnimal = new SmartDog(); MyProxyProvider myProxyProvider = new MyProxyProvider(smartAnimal); SmartAnimal proxy = myProxyProvider.getProxy(); proxy.getSum(10, 2); System.out.println("======================="); proxy.getSub(10, 2); }

3.4问题提出

在MyProxyProvider类中                ,我们的输出语句功能比较弱,在实际开发中                ,我们希望是以一个方法的形式                    ,嵌入到真正执行的目标方法前   ,怎么办?

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
微软官方windows10免费下载(【微软Windows7旗舰版】微软(Microsoft)Windows 7旗舰版 操作系统 【行情 报价 价格 评测】)