学习笔记-Java动态代理的简单使用

这篇具有很好参考价值的文章主要介绍了学习笔记-Java动态代理的简单使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

代理模式

  • 一种设计模式

  • 简单地说,在代理模式中存在三个角色

    • 用户

    • 代理

    • 被代理的对象

  • 用户调用代理,代理去调用被代理的对象

  • 以此来实现功能的增强

  • 动态代理在java中有两种实现方法

    • JDK中的Proxy类

    • CGLIB

JDK中的Proxy类

步骤

  • 实现InvocationHandler接口,创建自己的调用处理器

  • 通过为Proxy类指定ClassLoader和一组Interface来创建动态代理类

    • 被代理对象的ClassLoader和Interface
  • 通过反射机制获取动态代理类的构造函数

    • 其需要的唯一参数类型是InvocationHandler
  • 通过构造函数创建动态代理实例

    • 构造时将之前实现的InvocationHandler对象作为参数传入

这四步之后,我们就可以用使用被代理对象的方式,来使用动态代理实例了

另外

  • 后三步可以自己手动调用Proxy类的方法来分别实现

  • 也可以直接调用Proxy封装好的方法来一步实现

    • Proxy.newProxyInstance(ClassLoader, Interface[], InvocationHandler)

Demo

package cn.andl;

import cn.andl.util.Computer;
import cn.andl.util.impl.ComputerImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 测试JDK动态代理方式
 * @author Andl
 * @since 2023/5/29 11:17
 */
public class TestProxy {

    public static void main(String[] args) {

        // 创建被代理对象实例
        ComputerImpl computer = new ComputerImpl();

        // 实现一个调用处理器
        InvocationHandler invocationHandler = new InvocationHandler() {

            /**
             * 在之后的代理类调用方法时,会实际调用这个方法
             *
             * @param proxy 代理
             *
             * @param method 要被代理的方法
             *
             * @param args 方法里的参数列表
             *
             * @return 方法的返回值
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.format("[%s] 执行 %s 方法, 参数1:%d, 参数2:%d\n",
                        this.getClass().getName(), method.getName(), (Integer)args[0], (Integer)args[1]);

                // 调用被代理对象的方法,并获取返回值
                Object result = method.invoke(computer, args);

                System.out.format("[%s] 执行 %s 方法完毕, 结果:%d\n",
                        this.getClass().getName(), method.getName(), (Integer)result);

                return result;
            }
        };

        //获取代理对象
        Computer computerProxy = (Computer) Proxy.newProxyInstance(
                // 被代理对象的类加载器
                computer.getClass().getClassLoader(),
                // 被代理对象实现的接口
                computer.getClass().getInterfaces(),
                // 调用处理器
                invocationHandler);

        // 执行方法
        computerProxy.add(1, 2);
    }

}

Computer接口

package cn.andl.util;

/**
 * 计算接口
 * @author Andl
 * @create 2023/5/29 11:18
 */
public interface Computer {

    /**
     * 计算a和b的和
     * @param a 加数1
     * @param b 加数2
     * @return 和
     */
    int add(int a, int b);

}

ComputerImpl类

package cn.andl.util.impl;

import cn.andl.util.Computer;

/**
 * 计算接口实现类
 * @author Andl
 * @since 2023/5/29 11:23
 */
public class ComputerImpl implements Computer {
    @Override
    public int add(int a, int b) {
        System.out.format("[%s] 方法执行中\n", this.getClass().getName());
        return a + b;
    }
}

输出结果

[cn.andl.TestProxy$1] 执行 add 方法, 参数1:1, 参数2:2
[cn.andl.util.impl.ComputerImpl] 方法执行中
[cn.andl.TestProxy$1] 执行 add 方法完毕, 结果:3:3

Demo2

简单封装一下

package cn.andl;

import cn.andl.util.Computer;
import cn.andl.util.impl.ComputerImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 测试JDK动态代理2
 * @author Andl
 * @since 2023/5/29 13:19
 */
public class TestProxy2 {

    static class InvocationHandlerImpl implements InvocationHandler {

        Object originalObject;

        public Object bind(Object originalObject) {
            this.originalObject = originalObject;

            return Proxy.newProxyInstance(
                    originalObject.getClass().getClassLoader(),
                    originalObject.getClass().getInterfaces(),
                    this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.format("[%s] 执行 %s 方法, 参数1:%d, 参数2:%d\n",
                    this.getClass().getName(), method.getName(), (Integer)args[0], (Integer)args[1]);

            Object result = method.invoke(originalObject, args);

            System.out.format("[%s] 执行 %s 方法完毕, 结果:%d\n",
                    this.getClass().getName(), method.getName(), (Integer)result);

            return result;
        }
    }

    public static void main(String[] args) {
        // 获取代理
        Computer computer = (Computer) new InvocationHandlerImpl().bind(new ComputerImpl());

        // 执行方法
        computer.add(1, 2);
    }

}

输出结果

[cn.andl.TestProxy2$InvocationHandlerImpl] 执行 add 方法, 参数1:1, 参数2:2
[cn.andl.util.impl.ComputerImpl] 方法执行中
[cn.andl.TestProxy2$InvocationHandlerImpl] 执行 add 方法完毕, 结果:3

原理简述

通过在main方法中最开始时加入一句代码,我们可以保留动态代理对象的字节码文件

  • System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

  • 可以在和src同级的com文件夹下的sun/proxy/中找到

类名

public final class $Proxy0 extends Proxy implements Computer {

观察类名可以发现,动态代理类继承了Proxy方法,实现了Computer接口

静态代码块

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.andl.util.Computer").getMethod("add", Integer.TYPE, Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

观察静态代码块可以发现,被代理对象的方法被赋值到了变量中

add方法

    public final int add(int var1, int var2) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

观察动态代理类中的add方法可以发现

  • 是通过调用父类中变量h的invoke方法来实现功能的

  • 而这个h,就是我们在之前创建动态代理类时,向构造器传入的InvocationHandler

CGLIB

  • cglib是一个功能强大、高性能、高质量的字节码操作库

  • 主要用于在运行时拓展Java类或者根据接口生成对象

  • 本身的实现基于asm库

  • 要使用cglib主要会用到Enhancer和回调类

Enhancer

  • Enhancer是cglib中使用最多的类

  • Enhancer可以生成被代理类的子类,并且会拦截所有方法的调用

    • 称之为增强
  • Enhancer可以基于接口来生成动态代理类,也可以直接基于类生成动态代理类

  • Enhancer不能增强构造函数,也不能增强被final修饰的类,或者被static和final修饰的方法

    • 因为Enhancer是通过继承被代理的目标类来是实现增强的
  • Enhancer的使用分成两步

    • 传入目标类型

    • 设置回调

MethodInterceptor

cglib中回调类型有很多,这里主要介绍方法拦截器MethodInterceptor

  • 方法拦截器会对被代理的目标类中所有可以增强的方法进行增强

    • 不包括构造方法、final方法和static方法
  • 方法拦截器的核心方法public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

    • o

      • 被代理的目标对象
    • method

      • 被代理的目标方法
    • objects

      • 参数列表
    • methodProxy文章来源地址https://www.toymoban.com/news/detail-499197.html

      • 代理类的方法引用

Demo

public class TestCGLIB {

    public static void main(String[] args) {
        // 初始化enhancer对象
        Enhancer enhancer = new Enhancer();
        // 传入目标类型
        enhancer.setSuperclass(ComputerImpl.class);
        // 也可以传入接口
//        enhancer.setInterfaces(ComputerImpl.class.getInterfaces());
        // 设置回调类型
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 拦截方法
             * @param o 目标对象
             * @param method 目标方法
             * @param objects 参数列表
             * @param methodProxy 代理方法
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

                System.out.format("[%s] 执行方法 [%s] 参数:[%d][%d]\n",
                        this.getClass().getName(), method.getName(), (Integer)objects[0], (Integer)objects[1]);
                Object result = methodProxy.invokeSuper(o, objects);
                System.out.format("[%s] 执行方法 [%s] 结果:[%d]\n",
                        this.getClass().getName(), method.getName(), (Integer)result);

                return result;
            }
        });
        // 创建代理对象
        ComputerImpl computer = (ComputerImpl)enhancer.create();
        // 执行方法
        computer.add(1, 2);
    }

}

到了这里,关于学习笔记-Java动态代理的简单使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java学习笔记:爬虫-操作动态网页的Selenium

    Why Selenium? 有些网页内容是在浏览器端动态生成的,直接Http获取网页源码是得不到那些元素的。 Selenium可以自动启动一个浏览器、打开网页,可以用程序操作页面元素,也可以获得浏览器当前页面动态加载的页面元素。 比如:百度图片的图片是动态加载的。 用法: 1、下载安

    2024年02月13日
    浏览(43)
  • 【设计模式——学习笔记】23种设计模式——代理模式Proxy(原理讲解+应用场景介绍+案例介绍+Java代码实现)

    代理模式 为一个对象提供一个代理对象,以控制对这个对象的访问 。即通过代理对象访问目标对象,这样做的好处是:可以在不修改目标对象代码的基础上,增强额外的功能操作,即扩展目标对象的功能 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对

    2024年02月14日
    浏览(43)
  • 反射机制-体会反射的动态性案例(尚硅谷Java学习笔记)

    // 举例01 public class Reflect{ } 案例:榨汁机榨水果汁,水果分别有果(com.reflect.Apple)、香蕉(Banana)、桔子(Orange)等。 效果如图。 提示: 1、声明(Fruit)水果接口,包含榨汁抽象方法: void squeeze(); /skwi:z/ 2、声明榨汁机(Juicer),包含运行方法: public void run(Fruit f),方法体中,调用f的榨汁方

    2024年02月11日
    浏览(47)
  • java中的静态代理、jdk动态代理以及CGLIB 动态代理

    代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能 那以下文章主要谈三种代理模式, 分别是静态代理,jdk的动态代理,cglib的动

    2024年02月11日
    浏览(43)
  • Java代理模式——静态代理与动态代理

    代理模式允许你为其他对象提供一个代理,以控制对这个对象的访问。代理模式在不改变实际对象的情况下,可以在访问对象时添加额外的功能。 可以理解为代理模式为被代理对象创造了一个替身,调用者可以通过这个替身去实现这个被代理对象的功能,这个替身也可以为被

    2024年02月13日
    浏览(47)
  • Java企业级开发学习笔记(2.1)MyBatis实现简单查询

    零、创建数据库与表 在Navicat里创建MySQL数据库testdb 创建用户表 - t_user CREATE TABLE t_user ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) DEFAULT NULL, age int(11) DEFAULT NULL, address varchar(255) DEFAULT NULL, PRIMARY KEY ( id ) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 执行SQL语句来创建用户表 INSERT IN

    2024年02月09日
    浏览(89)
  • Java中的代理模式(二)JDK动态代理

    大家好👋,我是极客涛😎,上一篇中我们对代理模式有两大类,静态代理和动态代理,对于静态代理相信大家都信手拈来。对于动态代理还有两种实现,一种是java原生的Jdk代理,一种是Cglib方式。因为涉及到源码解读,所以我也将分两期完成,本期主要讲讲JDK动态代理的实

    2024年01月22日
    浏览(41)
  • Java代理之jdk动态代理+应用场景实战

    本文将先介绍jdk动态代理的基本用法,并对其原理和注意事项予以说明。之后将以两个最常见的应用场景为例,进行代码实操。这两个应用场景分别是 拦截器 和 声明性接口 ,它们在许多开发框架中广泛使用。比如在spring和mybatis中均使用了拦截器模式,在mybatis中还利用动态

    2023年04月10日
    浏览(43)
  • Java——JDK动态代理

    动态代理(理解) 基于反射机制 举个例子,生活中一般在打官司的时候都会请代理律师,为什么要请律师呢?是因为开庭的时候大部人对于打官司没有经验,只会说出自己案件的陈述,并不会根据法律等争取自己权益的最大化,此时就可以请律师帮助自己不仅完成对案件的陈述

    2024年02月09日
    浏览(42)
  • Java动态代理

    我们可以把姬哥看成对象,那么他就会有唱,跳的方法。Java的动态代理就像中介,负责给他收钱的,以及之后维护的功能(也就是增强的功能)。当有人来找的时候,代理通过实现包含唱跳的方法的接口,

    2024年02月20日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包