Java动态代理讲解——深入浅出

一、代理模式

        要讲解动态代理,就要先说代理模式,这是一种最常用的设计模式。该模式中有以下三个角色。

        抽象角色:通过接口或抽象类声明真实角色实现的业务方法,即被代理类与代理类都要实现的接口。

        代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

        真实角色:实现抽象角色,定义真实所要实现的业务逻辑,供代理角色调用。


二、静态代理与动态代理

       静态代理需要为每个被代理类(真实角色)都创建特定的代理类(代理角色),这些类需要程序员自己编写,这样会带来极大的不便。

       因此出现了动态代理,动态代理是基于JDK的反射机制。

三、动态代理的实现方式

        假如我们现在要实现一个日志增强类,在各种类调用各种方法前和方法执行结束后打印日志。LogHandler实现InvocationHandler,并且需要自己实现invoke方法,该方法中需要执行日志打印语句和原本的业务逻辑方法。

public class LogHandler implements InvocationHandler{

    private Object target;

    public LogHandler(Object target) {
        this.target = target;
    }

    //生成代理对象
    public Object bind(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("log before");//增强日志语句
        method.invoke(target, args);//调用被代理类的业务逻辑方法
        System.out.println("log after");//增强日志语句
        return null;
    }
}
        假如现在我们有一个User接口及其实现类UserImpl。
public interface User {
    void hello();
}
public class UserImpl implements User{

    public void hello(){
        System.out.println("hello");
    }
}

        要通过LogHandler类来获得UserImpl的日志代理类,怎么办呢?

public class Main {

    public static void main(String[] args) {
        UserImpl user = new UserImpl();
        LogHandler logHandler = new LogHandler(user);
        User proxy = (User) logHandler.bind();
        proxy.hello();
    }
}

三、实现原理

       关于到底是如何生成代理类的,我们要仔细看一下bind方法中的Proxy.newProxyInstance(......)方法,这个方法里面有一切我们想知道的玄机。我们来看一下源代码中最核心的一些东西(一些关于安全性合法性检查的语句就不说了)

Class<?> cl = getProxyClass0(loader, intfs);

       参数里的loader指的是被代理的类加载器,intfs指的是被代理类所实现的接口集合,getProxyClass0方法根据这两个参数构造了一个类对象,该类继承Proxy类(因此JDK动态代理只支持接口代理!),实现了被代理类所实现的接口。

       接着进入getProxyClass0方法

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
}

       这里可以看得很清楚,如果再之前已经生成过“拥有相同接口集合的类”的代理类,则可以直接从缓存中取出该代理类对象,否则通过代理类工厂创建。在获取到代理类之后,就要创建代理对象了,通过Java的反射机制,直接通过Contructor类的newInstance方法创建并且返回。

return cons.newInstance(new Object[]{h});

        这里new Object[]{h}是构造器参数,因为我们的代理类是继承Proxy的,而Proxy提供了一个受保护的构造器和一个私有构造器,我们来看一下受保护的构造器。

protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
}

        其中的参数正是InvocationHandler,因此其子类代理类一定会有一个有参构造器,且有传入InvocationHandler对象。为什么要传入这个参数呢?因为Proxy有个属性就是InvocationHandler,这个属性有什么用呢?我们来看一下生成的代理类的代码(来自网络)。

public final class $Proxy0 extends Proxy implements UserManager {  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  
  
    static {  
        try {  
            m1 = Class.forName("java.lang.Object").getMethod("equals",  
                    new Class[] { Class.forName("java.lang.Object") });  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                    new Class[0]);  
            m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",  
                    new Class[0]);  
            m2 = Class.forName("java.lang.Object").getMethod("toString",  
                    new Class[0]);  
        } catch (NoSuchMethodException nosuchmethodexception) {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        } catch (ClassNotFoundException classnotfoundexception) {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    }  
  
    public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }  
  
    @Override  
    public final boolean equals(Object obj) {  
        try {  
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
                    .booleanValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final int hashCode() {  
        try {  
            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final String toString() {  
        try {  
            return (String) super.h.invoke(this, m2, null);  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public void addUser() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
  
    }  
}  
        可以看到,所有的方法都会调用super.h.invoke方法!因此,当我们调用代理类的很多方法时,其实都是指向了invoke方法。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值