Java 静态代理与动态代理解析

目录
  • 一、代码实践
    • 静态代理
    • 动态代理
  • 二、常见的动态代理场景
    • Retrofit中的动态代理
    • 使用动态代理实现onClick注入
  • 三、源码探索Jdk中的动态代理
    • 生成代理类
  • 四、总结

静态代理: 由我们开发者自己手动创建或者在程序运行前就已经存在的代理类,静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

动态代理: 在程序运行时,运用java反射机制动态创建而成,静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道,通常动态代理实现方式是通过实现 jdk 的 InvocationHandler 接口的 ​​invoke​​ 方法

一、代码实践

需要代理的接口:

interface IBook {
    fun toBookName(name: String)
}

静态代理

class BookImpl : IBook {
    override fun toBookName(name: String) {
        println("书名|$name")
    }
}
// TODO: 2020/11/25 静态代理最大的特点就是,对于具体的代理提前声明
class BookProxy(
    private val iBook: IBook,
    private var objStart: () -> Unit = { println("开始前的操作") },
    private var objStop: () -> Unit = { println("结束时的操作") }
) : IBook {
    override fun toBookName(name: String) {
        //我们可以在具体的实现前做一些处理操作
        objStart.invoke()
        iBook.toBookName(name)
        //具体的处理后做一些操作
        objStop.invoke()
    }
}

fun main() {
    val bookProxy = BookProxy(BookImpl())
    bookProxy.toBookName("Android&Petterp")
}

静态代理相对简单,也有局限性,对于代理类我们需要提前声明,而且每一个代理都需要提前创建好。

动态代理

class BookImpl : IBook {
    override fun toBookName(name: String) {
        println("测试输出文本$name")
    }
}

/** 用于帮助代理的类
 * BookImplHandler看起来很像一个比较奇怪的代理,实际上其只是用于帮助代理的类,我们最终生产的代理类会把调用发给它让其处理。
 * 代理类本身是通过 Proxy.newProxyInstance() 方法在运行时动态创建
 * */
class BookImplHandler(
    private val book: IBook,
    private var objStart: () -> Unit = { println("开始前的操作") },
    private var objStop: () -> Unit = { println("结束时的操作") }
) :
    InvocationHandler {
    // TODO: 2020/11/25
    //  (1)在invoke方法中接收可变长参数,在Kotlin语法中,数组是array,可变长参数类型是vararg,类型不匹配。
    //  (2)Kotlin中数组转为可变长参数,通过前面加*符号。
    //  (3)如果方法没有任何参数,args将为null,并将其传播到Kotlin将导致NullPointerException.
    //  作为一种解决方法,使用*(args?:arrayOfNulls< Any>(0)),并在所描述的极端情况下选择正确的部分并将其扩展为零参数.
    override fun invoke(proxy: Any, method: Method?, args: Array<out Any>?): Any? {
        objStart.invoke()
        val invoke = method?.invoke(book, *(args ?: emptyArray()))
        objStop.invoke()
        return invoke
    }
}

fun main() {
    val bookImplHandler = BookImplHandler(BookImplDynamic())
    val iBook = Proxy.newProxyInstance(
        BookImpl::class.java.classLoader,
        BookImpl::class.java.interfaces, bookImplHandler
    ) as IBook
    iBook.toBookName("测试的文本")
}

二、常见的动态代理场景

Retrofit中的动态代理

使用Retrofit时,当我们使用Retrofit.Builder().create() 时,传入了我们的接口类,其 ​​create​​ 方法内部就使用了动态代理,从而生成了相应的代理类。

public <T> T create(final Class<T> service) {
  //判断是否为接口
  validateServiceInterface(service);
  return (T)
      //代理实现
      Proxy.newProxyInstance(
          service.getClassLoader(),
          new Class<?>[] {service},
          new InvocationHandler() {
            ...

            @Override
            public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                throws Throwable {
              //如果是object方法,则直接触发
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              ...
            }
          });
}

使用动态代理实现 onClick注入

如下所示,我们声明一个注解,其应用于方法,并加入了一个Activity的扩展函数,用于对Activity重所有使用了该注解的方法进行onclick注入, 其方法内部使用反射+动态代理,从而实现。

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class InjectClick(@IdRes val ids: IntArray)

fun Activity.injectClicks() {
    javaClass.methods.asSequence().filter {
        it.isAnnotationPresent(InjectClick::class.java)
    }.forEach {
        it.isAccessible = true
        it.getAnnotation(InjectClick::class.java).ids.forEach { id ->
            findViewById<View>(id).apply {
                val clickProxy = Proxy.newProxyInstance(
                    javaClass.classLoader, arrayOf(View.OnClickListener::class.java)
                ) { _, _, _ ->
                    it.invoke(this@injectClicks)
                } as View.OnClickListener
                setOnClickListener(clickProxy)
            }
        }
    }
}

三、源码探索 Jdk 中的动态代理

动态代理的源码实现相对简单,我们先进入Proxy.newProxyInstance 方法,一探究竟。

Proxy.newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException {
        Objects.requireNonNull(h);
        //先克隆一个传递过来的代理对象
        final Class<?>[] intfs = interfaces.clone();
        //拿到生成的代理类,内部维护了一个map
        Class<?> cl = getProxyClass0(loader, intfs);
        //获取代理类构造函数
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        //授予权限
        if (!Modifier.isPublic(cl.getModifiers())) {
            cons.setAccessible(true);
        }
        //反射创建代理类,并传入用户自己实现的中间层接口
        return cons.newInstance(new Object[]{h});
}

具体如上述描述,然后我们自己生成一个代理类,来看看内部的调用:

生成代理类

fun writeFileProxy() {
    //IApple是我要代理的接口
    val name = IApple::class.java.name + "Proxy()"
    val bytes = ProxyGenerator.generateProxyClass(name, arrayOf(IApple::class.java))
    val fos = FileOutputStream("$name.class")
    fos.write(bytes)
    fos.close()
}

示例代码:

public final class IAppleProxy() extends Proxy implements IApple {

    private static Method m3;

    public IAppleProxy__/* $FF was: IAppleProxy()*/(InvocationHandler var1) throws  {
        super(var1);
    }

    public final void count() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
            m3 = Class.forName("com.android.readbook.proxy.IApple").getMethod("count")
    }
}

生成的代理类如上所示,其实现了我们的接口,并且代理了相应的所有方法,这里做了一部分删减。

观察代理类具体方法的话,其具体实现里,通过InvocationHandler对象,也就是我们自己实现的辅助了,并调用其invoke方法, 以接口回调的方式回调到我们具体的实现处。整个过程比较容易理解,也并没有什么太高深的难度。

四、总结

关于jdk中的动态代理,当我们调用 Proxy.newProxyInstance 时,传入了一个当前类的classLoader以及要代理的接口数组及实现了InvocationHandler 接口 的辅助类对象,其会在运行时在内存中生成一个代理类,这个代理类实现了我们的接口并接收 一个我们外部传入的 InvocationHandler 辅助类对象,并在具体的方法实现位置通过调用辅助类的 ​​invoke​​ 方法,从而实现我们的接口方法代理。

到此这篇关于Java 静态代理与动态代理解析的文章就介绍到这了,更多相关Java 静态代理与动态代理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 带你重新认识Java动态代理

    目录 什么是动态代理? 动态代理的常用两种方式: 1.基于接口的动态代理 2.基于类的动态代理 基于子类的动态代理 总结: 什么是动态代理? 动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术.在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法.可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作(也可以对原方法的参数进行操作). 代理类在程序运行期间,创建的代理对象称之为动态代理对象.这种情况下,创建的

  • Java 动态代理你真的懂了吗(动态和代理)

    好几天不写文章,今天来写一篇,从之前的计划表上看到还有关于java的动态代理没写,这个技术平常用的少,也不是特别好理解,今天补上这篇,希望能讲明白,不至于像我一样迷茫好久,开始吧 动态代理分两部分,动态和代理,我们先说下代理模式 1.代理模式 代理模式是常用的设计模式之一,也是开发中常见的设计模式. 简单的描述一下,代理模式就是将实现类隔离开,比如你想给你女朋友过个生日,找个明星唱生日歌,你女朋友的偶像是周杰伦,想找周杰伦给她过生日,唱歌,但是你不太能联系上周杰伦,即使在社交网站上联系,可能也不

  • java动态代理实现代码

    目录 1.代理模式 2.动态代理 3.原理研究 4.应用 5.总结 1.代理模式 代理模式是常用的设计模式之一,也是开发中常见的设计模式. 简单的描述一下,代理模式就是将实现类隔离开,比如你想给你女朋友过个生日,找个明星唱生日歌,你女朋友的偶像是周杰伦,想找周杰伦给她过生日,唱歌,但是你不太能联系上周杰伦,即使在社交网站上联系,可能也不太理你,所以你可以联系周杰伦的经纪人进行沟通,经纪人就是周杰伦的代理. 实现过程: 1.定义一个唱歌的接口,代表业务 public interface ISing

  • java代理模式(静态代理、动态代理、cglib代理)

    目录 代理模式 静态代理 代码 接口 被代理对象 代理对象 测试 动态代理 代码: 接口 目标对象 代理对象 测试 cglib代理 代码: 目标对象 代理对象 测试 应用 总结 代理模式 代理模式(Proxy Pattern)是一种结构性模式.代理模式为一个对象提供了一个替身,以控制对这个对象的访问.即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 被代理的对象可以是远程对象.创建开销答得对象或需要安全控制得对象.代理模式主要有三种形式,分别

  • Java动态代理的示例详解

    目录 定义 分类 案例 需求 方案一:jdk动态代理 方案二:cglib动态代理 分析 总结 定义 动态代理指的是,代理类和目标类的关系在程序运行的时候确定的,客户通过代理类来调用目标对象的方法,是在程序运行时根据需要动态的创建目标类的代理对象. 分类 jdk动态代理 cglib动态代理 案例 需求 苹果公司通过苹果代理商来卖手机 方案一:jdk动态代理 定义抽象接口 /** * 售卖手机的接口(代理模式--抽象角色) * @author:liyajie * @createTime:2022/2

  • JAVA代理,静态,动态详解

    目录 代理 静态代理 动态代理 JDK动态代理 CGLib动态代理 总结 代理 为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象 (委托类) 中介作用. 按照代理的创建时期,代理类可以分为两种: 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译.在程序运行前代理类的.class文件就已经存在了. 动态:在程序运行时运用反射机制动态创建而成. 静态代理 Subject: 代理类和被代理类实现同样的接口 Proxy:代理

  • Java 静态代理与动态代理解析

    目录 一.代码实践 静态代理 动态代理 二.常见的动态代理场景 Retrofit中的动态代理 使用动态代理实现onClick注入 三.源码探索Jdk中的动态代理 生成代理类 四.总结 静态代理: 由我们开发者自己手动创建或者在程序运行前就已经存在的代理类,静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类. 动态代理: 在程序运行时,运用java反射机制动态创建而成,静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道,通常动态代理实现方式是通过实现 j

  • 深入解析java中的静态代理与动态代理

    java编码中经常用到代理,代理分为静态代理和动态代理.其中动态代理可以实现spring中的aop. 一.静态代理:程序运行之前,程序员就要编写proxy,然后进行编译,即在程序运行之前,代理类的字节码文件就已经生成了 被代理类的公共父类 复制代码 代码如下: package staticproxy;public abstract class BaseClass {    public abstract void add();} 被代理类 复制代码 代码如下: package staticpro

  • 解析Spring中的静态代理和动态代理

    一.静态代理 1.1.静态代理的使用 静态代理,代理类和被代理的类实现了同样的接口,代理类同时持有被代理类的引用,这样,当我们需要调用被代理类的方法时,可以通过调用代理类的方法来做到.举例如下: 假设领导的工作是开会和给员工考评. 先定义一个接口: package com.sharpcj; public interface IWork { void meeting(); int evaluate(String name); } 然后定义领导类: package com.sharpcj; impo

  • JAVA中的静态代理、动态代理以及CGLIB动态代理总结

    代理模式是java中最常用的设计模式之一,尤其是在spring框架中广泛应用.对于java的代理模式,一般可分为:静态代理.动态代理.以及CGLIB实现动态代理. 对于上述三种代理模式,分别进行说明. 1.静态代理 静态代理其实就是在程序运行之前,提前写好被代理方法的代理类,编译后运行.在程序运行之前,class已经存在. 下面我们实现一个静态代理demo: 静态代理 定义一个接口Target package com.test.proxy; public interface Target { p

  • Java静态代理和动态代理总结

    静态代理 第一种实现(基于接口): 1>接口 public interface Hello { void say(String msg); } 2>目标类,至少实现一个接口 public class HelloImpl implements Hello { public void say(String msg) { System.out.println("Hi,"+msg); } } 3>代理类(与目标类实现相同接口,从而保证功能一致) public class He

  • Java代理模式实例详解【静态代理与动态代理】

    本文实例讲述了Java代理模式.分享给大家供大家参考,具体如下: 即Proxy Pattern,23种java常用设计模式之一.代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问. Java的代理模式是Java中比较常用的设计模式,分为2中代理:静态代理与动态代理(JDK动态代理和cglib动态代理) 优点: 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可. 高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动. 缺点: 很明显的一点

  • Java静态代理和动态代理的深入讲解

    代理模式 代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问. 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层. 代理模式的元素是:共同接口.代理对象.目标对象. 代理模式的行为:由代理对象执行目标对象的方法.由代理对象扩展目标对象的方法. 代理模式的

  • 代理模式:JAVA静态代理和动态代理的实例和实现详解

    目录 前言 静态代理 实现简述 创建human接口 创建接口实现类 创建针对接口实现增强操作的代理 代理实现效果 动态代理 实现简述 要点:向上转型 创建YoungMan接口 创建两个接口实现类 创建动态代理实例对象 代理实现效果 要点:InvocationHandler补充 代理模式和修饰模式的区别 总结 前言 代理模式,我们这里结合JAVA的静态代理和动态代理来说明,类比Spring AOP面向切面编程:增强消息,也是代理模式. 而我们的静态代理和动态代理,与(service)接口和(ser

  • Java静态代理与动态代理案例详解

    代理模式 代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问. 主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层. 代理模式的元素是:共同接口.代理对象.目标对象. 代理模式的行为:由代理对象执行目标对象的方法.由代理对象扩展目标对象的方法. 代理模式的

随机推荐