IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring之AOP动态代理 -> 正文阅读

[Java知识库]Spring之AOP动态代理

1、jdk代理

  • 代理类没有源码,在运行期间直接生成字节码,通过传入的类加载器加载
  • 为了实现功能的增强,添加前置通知、后置通知,环绕通知等
  • 代理类和目标对象是兄弟关系,都是实现了某个接口
  • 目标类可以被final修饰,不可被继承,不影响兄弟对象(代理类)创建
public class JdkProxyDemo {
    public static void main(String[] param) throws IOException {
        // 目标对象
        Target target = new Target();
        // 用来加载在运行期间动态生成的字节码
        ClassLoader loader = JdkProxyDemo.class.getClassLoader();
        // p:代理类本身
        // method:代理类执行的方法
        // args:执行方法的参数
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (p, method, args) -> {
            System.out.println("before...");
            // 目标.方法(参数)
            // 方法.invoke(目标, 参数);
            Object result = method.invoke(target, args);
            System.out.println("after....");
            return result; // 让代理也返回目标方法执行的结果
        });
        System.out.println("代理类类型:"+proxy.getClass());
        System.out.println("---------------通过代理类调用目标方法-----------");
        proxy.foo();
    }
}

interface Foo {
    void foo();
}

class Target implements Foo {
    public void foo() {
        System.out.println("target foo");
    }
}

输出结果:

代理类类型:class com.xc.a11.$Proxy0
---------------通过代理类调用目标方法-----------
before...
target foo
after....

Process finished with exit code 0

2、cglib代理

  • 代理是子类型, 目标是父类型,所以目标类和方法都不能为final
  • methodProxy 可以避免使用反射调用,spring默认使用
  • 与jdk代理一样也是没有class文件,运行期间直接生成字节码文件
public class CglibProxyDemo {
    // 代理是子类型, 目标是父类型
    public static void main(String[] param) {
//        Target target = new Target();
        Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) 
        (p, method, args, methodProxy) -> {
  			System.out.println("before...");
//            Object result = method.invoke(target, args); // 用方法反射调用目标
            // methodProxy 它可以避免反射调用
//            Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring)
            Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理
            System.out.println("after...");
            return result;
        });
        proxy.foo();
    }
}

class Target {
    public void foo() {
        System.out.println("target foo");
    }
}

输出结果:

before...
target foo
after...

Process finished with exit code 0

3、手写jdk代理

  • 接口和目标对象
interface Foo {
    void foo();
    int bar();
}

class Target implements Foo {
    public void foo() {
        System.out.println("target foo");
    }
    @Override
    public int bar() {
        System.out.println("target bar");
        return 100;
    }
}
  • 代理类实现功能增强的方法的接口
  • 参数分别为:代理类本身,代理类执行的方法,方法参数
interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
  • 核心代理类
  • 构造方法,传入功能增强接口的实现类
public class $Proxy0 implements Foo {

	//构造函数,创建代理类需要服务执行接口(增强方法的实现类)
    private final InvocationHandler invocationHandler;
    public $Proxy0(InvocationHandler h) {
        this.invocationHandler = h;
    }

	//下面的方法会多次调用,用到method,这里静态代码块初始化一次
    private static final Method foo;
    private static final Method bar;
    static {
        try {
            foo = Foo.class.getMethod("foo");
            bar = Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    @Override
    public void foo() {
        try {
        	//无返回:调用代理类实现功能增强的方法
            invocationHandler.invoke(this, foo, new Object[0]);
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
        	//有返回:调用代理类实现功能增强的方法
            Object result = invocationHandler.invoke(this, bar, new Object[0]);
            return (int) result;
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
  • 创建代理类传入InvocationHandler的匿名内部类
  • 通过目标对象Target反射调用对应方法,前后可以添加功能增强
public static void main(String[] param) {
    //创建对应接口的代理
    Foo proxy = new $Proxy0(new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
            // 1. 功能增强
            System.out.println("before...");
            // 2. 调用目标
            Object invoke = method.invoke(new Target(), args);
            // 3. 功能增强
            System.out.println("after...");
            return invoke;
        }
    });
    //调用接口代理的方法
    proxy.foo();
    proxy.bar();
}

输出结果:

before...
target foo
after...
before...
target bar
after...

Process finished with exit code 0

JDK动态代理实现与上方不同的是:没有class文件,在程序运行期间的内存中生成以上的字节码文件,所以叫动态代理

4、手写cglib代理

4.1、反射之目标调用

  • 目标类及多个方法
public class Target {
    public void save() {
        System.out.println("save()");
    }

    public void save(int i) {
        System.out.println("save(int)");
    }

    public void save(long j) {
        System.out.println("save(long)");
    }
}
  • 代理类,继承目标类,目标类的子类
  • 创建代理类需要MethodInterceptor接口实现类(环绕增强方法及中间的调用目标方法)
  • 代理类调用方法即是调用MethodInterceptor实现类的内容
public class Proxy extends Target {

    private MethodInterceptor methodInterceptor;

    public Proxy(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    private final static Method save0;
    private final static Method save1;
    private final static Method save2;
    private final static MethodProxy save0Proxy;
    private final static MethodProxy save1Proxy;
    private final static MethodProxy save2Proxy;
    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);
            save2 = Target.class.getMethod("save", long.class);
            save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
            save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
            save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }
    
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
    public void saveSuper() {
        super.save();
    }
    public void saveSuper(int i) {
        super.save(i);
    }
    public void saveSuper(long j) {
        super.save(j);
    }
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(long j) {
        try {
            methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}

// 环绕增强方法及中间的调用目标方法的接口
public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
  • method.invoke(target, args): 通过目标类获取到method,再利用method,target,args反射调用方法
  • methodProxy.invoke(target, args): 通过目标类直接调用方法,无反射
  • methodProxy.invokeSuper(p, args): 通过代理类调用方法,无反射
  • MethodProxy.create:创建methodProxy,内部调用saveSuper(带原始功能的方法)
public class A13 {
    public static void main(String[] args) {
        //目标类
        Target target = new Target();
        //代理类
        Proxy proxy = new Proxy(new MethodInterceptor() {
            @Override
            public Object intercept(Object p, Method method, Object[] args,
                                    MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                return method.invoke(target, args); // 反射调用
                // FastClass
//                return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
//                return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
            }
        });
        proxy.save();
        proxy.save(1);
        proxy.save(2L);
    }
}

输出结果:

before...
save()
before...
save(int)
before...
save(long)

Process finished with exit code 0

4.2、无反射之目标调用

methodProxy.invoke(target, args):methodProxy对象的创建及invoke的执行原理

  • Signature:签名(描述方法名字-save及方法入参-()和方法返回值-V
  • 每个方法指定一个唯一的编号通过getIndex方法与签名一一对应
  • 在invoke中通过编号使用目标类调用不同的方法
public class TargetFastClass {
    private final static Signature s0 = new Signature("save", "()V");
    private final static Signature s1 = new Signature("save", "(I)V");
    private final static Signature s2 = new Signature("save", "(J)V");

    // 获取目标方法的编号
    /*
        Target
            save()              0
            save(int)           1
            save(long)          2
        signature 包括方法名字、参数返回值
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    // 根据方法编号, 正常调用目标对象方法
    public Object invoke(int index, Object target, Object[] args) {
        if (index == 0) {
            ((Target) target).save();
            return null;
        } else if (index == 1) {
            ((Target) target).save((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Target) target).save((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        TargetFastClass fastClass = new TargetFastClass();
        int index = fastClass.getIndex(new Signature("save", "(I)V"));
        System.out.println(index);
        fastClass.invoke(index, new Target(), new Object[]{100});
    }
}

输出结果:这里的方法是目标类直接调用的save(int i)方法

1
save(int)

Process finished with exit code 0

4.3、无反射之代理调用

methodProxy.invokeSuper(p, args):methodProxy对象的创建及invokeSuper的执行原理

  • 签名和方法标号与无反射目标调用一样
  • invoke则是通过代理类调用saveSuper方法的super.save(),实际还是目标类的方法实现
public class ProxyFastClass {
    static Signature s0 = new Signature("saveSuper", "()V");
    static Signature s1 = new Signature("saveSuper", "(I)V");
    static Signature s2 = new Signature("saveSuper", "(J)V");

    // 获取代理方法的编号
    /*
        Proxy
            saveSuper()              0
            saveSuper(int)           1
            saveSuper(long)          2
        signature 包括方法名字、参数返回值
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    // 根据方法编号, 正常调用目标对象方法
    public Object invoke(int index, Object proxy, Object[] args) {
        if (index == 0) {
            ((Proxy) proxy).saveSuper();
            return null;
        } else if (index == 1) {
            ((Proxy) proxy).saveSuper((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Proxy) proxy).saveSuper((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        ProxyFastClass fastClass = new ProxyFastClass();
        int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
        System.out.println(index);

        fastClass.invoke(index, new Proxy(null), new Object[0]);
    }
}

输出结果: 这里只需要代理类里面的saveSuper方法,MethodInterceptor这里没有用到

0
save()

Process finished with exit code 0
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:01:38  更:2022-10-22 21:06:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 -2025/3/10 18:19:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码