设为首页 加入收藏

TOP

day08-AOP-01(二)
2023-07-25 21:43:03 】 浏览:58
Tags:day08-AOP-01
*/ @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();
}
image-20230123184229401

2.4动态代理机制debug

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

image-20230123185920781

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

image-20230123190225615

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

image-20230123191254182 image-20230123193834072

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

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

image-20230123194558675

在这行点击step into:

image-20230123194851798

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

image-20230123195041084

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

image-20230123195220582

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

image-20230123195616219

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

image-20230123195841465

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

image-20230123200609276

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

image-20230123200844246

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

总结梳理:

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

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

image-20230123210027227

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的输出
  1. 请使用传统方法完成
  2. 请使用动态代理方式完成,并要求考虑代理对象调用方法(底层是反射调用)时,可能出现的异常

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 + &quo
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇推荐一个分布式单点登录框架XXL-S.. 下一篇读函数式编程思维笔记05_现实应用

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目