专栏首页Android机动车动态代理那些事

动态代理那些事

在介绍动态代理之前,有必要先聊聊静态代理。

静态代理优点在于,业务类只需关注业务本身,保证了业务类的重用性,这也是代理模式共有的优点;

其缺点是:

  • 代理对象的一个接口只服务于一种类型的对象,如果需要代理的方法很多,就要为每一种方法都进行代理;
  • 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现这些方法,增加了代码维护的复杂度。

如果使用静态代理,那么真实角色(委托类)必须是实现已经存在的,并将其作为代理对象的内部成员。但实际开发中,一个真实角色必须对应一个代理角色,如果大量使用就会导致类的急剧增加;另外,如果实现并不知道真实角色(委托类),该如何使用代理呢。

这时就需要动态代理上场了。

动态代理

动态代理类的源码是在程序运行期间有JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定的。

相关API

  • Proxy

是java动态代理机制生成的所有动态代理类的父类,它提供了一系列方法来为一组接口动态地生成代理类及其对象。

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器  
static InvocationHandler getInvocationHandler(Object proxy)   

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象  
static Class getProxyClass(ClassLoader loader, Class[] interfaces)   

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类  
static boolean isProxyClass(Class cl)   

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例  
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)  
  • InvocationHandler

这是调用处理器接口,它定义了一个invoke方法,用于处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个对应的调用处理器对象。

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象  
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行  
Object invoke(Object proxy, Method method, Object[] args) 
  • ClassLoader

类加载器,负责将类的字节码加载到JVM中并为其定义类对象。重点是其字节码是由JVM在运行时动态生成的而非与存在任何一个.class文件中。

动态代理实现步骤

  • 1、实现InvocationHandler接口创建调用处理器;
  • 2、给Proxy类提供ClassLoader和代理借口类型数组创建动态代理类;
  • 3、以调用处理器类型为参数,利用反射得到动态代理类的构造函数;
  • 4、以调用处理器对象为参数,利用动态代理类构造函数创建动态代理对象。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发  
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用  
InvocationHandler handler = new InvocationHandlerImpl(..);   

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象  
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });   

// 通过反射从生成的类对象获得构造函数对象  
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });   

// 通过构造函数对象创建动态代理类实例  
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });   

Proxy类的静态方法newProxyInstance对上面后三步进行了封装,简化了动态代理对象获取的过程:

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发  
InvocationHandler handler = new InvocationHandlerImpl(..);   

// 通过 Proxy 直接创建动态代理类实例  
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,   
     new Class[] { Interface.class },  handler );  

示例

委托类和代理类的接口

public interface Person {

    /**
     * 表演
     * @param payment 薪资待遇
     */
    void play(int payment);

    /**
     * 吃饭
     * @param food 食物名称
     */
    void eat(String food);

}

委托类

public class Actor implements Person {
    @Override
    public void play(int payment) {
        Log.e("jia", "eat: 开始表演  " + payment);
    }

    @Override
    public void eat(String food) {
        Log.e("jia", "eat: 开始吃饭  " + food);
    }
}

调用处理器

public class BusinessInvocationHandler implements InvocationHandler {

    private Actor actor;

    public BusinessInvocationHandler(Actor actor) {
        this.actor = actor;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object result = null;
        Log.e("jia", "invoke: 代理之前要做的事情");
        Log.e("jia", "invoke: 方法名: " + method.toString());
        if (args != null && args.length > 0){
            if(args[0] instanceof String || args[0] instanceof Integer){
                Log.e("jia", "invoke: 参数: " + args[0]);
            }
        }
        result = method.invoke(actor, args);
        Log.e("jia", "invoke: 代理之后要做的事情");

        return result;
    }
}

最后使用工厂创建

public class ProxyFactory {

    public static Person getProxy() {
        Actor actor = new Actor();
        BusinessInvocationHandler handler = new BusinessInvocationHandler(actor);
        Person personProxy = (Person) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), handler);
        return personProxy;
    }

}

使用(kotlin)

var proxy = ProxyFactory.getProxy()
proxy.eat("面包")
proxy.play(1200)

总结

动态代理与静态代理相比较,最大的好处就是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,在接口方法数量比较多的时候我们就可以灵活处理。

本文分享自微信公众号 - Android机动车(JsAndroidClub)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式——代理模式

    现在有个非常流行的程序叫做面向切面编程(AOP),其核心就是采用了动态代理的方式。怎么用?Java为我们提供了一个便捷的动态代理接口 InvocationHan...

    蜻蜓队长
  • 动态代理——从一知半解到恍然大悟

    动态代理是Java常见的一种设计模式,很多文章都介绍了什么是代理、静态代理和动态代理的实现方式,然而这些都偏理论,一篇好的文章要让大家知道知识点的具体用处,本文...

    蜻蜓队长
  • ClassLoader及双亲委派模型

    双亲委派模型的工作流程全部在ClassLoader的loadClass()方法中执行:

    蜻蜓队长
  • 基于 JDK 的动态代理机制

    Single
  • java 代理模式详解

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

    用户2965908
  • Spring知识点(五)代理模式

    使用代理模式的目的是为了将原来类生成一个代理类,由代理类来执行原来类的一些增强方法,但是也不影响原来类中方法的执行。

    虞大大
  • 所有和Java中代理有关的知识点都在这了。

    对于每一个Java开发来说,代理这个词或多或少都会听说过。你可能听到过的有代理模式、动态代理、反向代理等。那么,到底什么是代理,这么多代理又有什么区别呢。本文就...

    java思维导图
  • java动态代理实现与原理详细分析

    关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。

    java思维导图
  • 动态代理详解

    动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。     动态代理与代理模式原理是一样的,只是它没有具体的代理类,直接通过反射生成了一...

    黑泽君
  • 设计模式--Proxy模式

    Subject: 可以是接口,也可以是抽象类 Proxy: 内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理...

    河岸飞流

扫码关注云+社区

领取腾讯云代金券