前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >动态代理那些事

动态代理那些事

作者头像
蜻蜓队长
发布2019-03-08 11:04:03
3100
发布2019-03-08 11:04:03
举报
文章被收录于专栏:Android机动车Android机动车

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

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

其缺点是:

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

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

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

动态代理

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

相关API
  • Proxy

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

代码语言:javascript
复制
// 方法 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方法,用于处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个对应的调用处理器对象。

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

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

动态代理实现步骤

  • 1、实现InvocationHandler接口创建调用处理器;
  • 2、给Proxy类提供ClassLoader和代理借口类型数组创建动态代理类;
  • 3、以调用处理器类型为参数,利用反射得到动态代理类的构造函数;
  • 4、以调用处理器对象为参数,利用动态代理类构造函数创建动态代理对象。
代码语言:javascript
复制
// 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对上面后三步进行了封装,简化了动态代理对象获取的过程:

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

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

示例

委托类和代理类的接口
代码语言:javascript
复制
public interface Person {

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

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

}
委托类
代码语言:javascript
复制
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);
    }
}
调用处理器
代码语言:javascript
复制
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;
    }
}
最后使用工厂创建
代码语言:javascript
复制
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)
代码语言:javascript
复制
var proxy = ProxyFactory.getProxy()
proxy.eat("面包")
proxy.play(1200)

总结

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android机动车 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动态代理
    • 相关API
    • 动态代理实现步骤
    • 示例
      • 委托类和代理类的接口
        • 委托类
          • 调用处理器
            • 最后使用工厂创建
              • 使用(kotlin)
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档