美文网首页Java知识123Java 杂谈
Java动态代理简析原理

Java动态代理简析原理

作者: 奔跑的笨鸟 | 来源:发表于2018-05-29 20:59 被阅读0次

说下Java动态代理,Spring的AOP就是基于Java的动态代理实现的。动态代理用到的几个类和接口,Proxy提供了一些静态的创建动态代理Class的方法。InvocationHandler接口,代理实现必须实现的接口。

一个简单的例子:

/**
 * HelloInterface 接口,定义了一个sayHello的方法
 */
interface  HelloInterface{
    public void sayHello();
}

/**
 * HelloInterface的一个实现类
 */
class HelloImpl implements HelloInterface{

    @Override
    public void sayHello() {
        System.out.println("Hello!");
    }
}

/**
 * 真正的代理类执行者
 */
class HelloHandler implements InvocationHandler{
    HelloInterface helloInterface;
    public HelloHandler(HelloInterface helloInterface){
        this.helloInterface = helloInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before exec method: "+ method.getName());
        method.invoke(helloInterface,args);
        System.out.println("after exec method: "+ method.getName());
        return null;
    }
}

一个简单的测试:

public static void main(String[] args) {
   HelloInterface helloInterface = (HelloInterface) Proxy.newProxyInstance(
           HelloImpl.class.getClassLoader(),
           HelloImpl.class.getInterfaces(),
           new  HelloHandler( new HelloImpl()));
    System.out.println(helloInterface.getClass().getName());
    helloInterface.sayHello();
}

输出:

$Proxy0
before exec method: sayHello
Hello!
after exec method: sayHello

Proxy.newProxyInstance 返回的对象名称是$Proxy0。这个怎么能转成HelloInterface呢?

分析下Proxy.newProxyInstance, 首先是3个参数,源码中的,挺简单不翻译了

 * @param   loader the class loader to define the proxy class
 * @param   interfaces the list of interfaces for the proxy class
 *          to implement
 * @param   h the invocation handler to dispatch method invocations to

下面就是简化后的代码:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
 
    /*
     * 在缓存中查找,如果找不到就新建个代理类
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * 调用代理类生成新的实例
     */
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    final InvocationHandler ih = h;
        
    return cons.newInstance(new Object[]{h});
    
}

调用方法getProxyClass0 获得Class 对象,这个方法先从proxyClassCache 获得,如果不存在通过ProxyClassFactory 来创建。实际上就是通过生成Class对象的字节数组,最后转成Class对象。
来看具体代码(删除了一些验证的代码)

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * 记录非公有的代理接口的包名,并且所有的非公有的代理接口必须在同一个包中。
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

        if (proxyPkg == null) {
            // 如果存在非公有的代理接口包,就用这个,如果都是公有的,就用默认的包com.sun.proxy
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * 此处就是生成$Proxy0的Class的名字
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * 生成Class的字节码,通过defineClass0转成Class对象
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }

如果细看ProxyGenerator.generateProxyClass会发现$Proxy0 其实实现了代理接口,可以解释了Proxy.newProxyInstance 返回的对象为什么能转成代理接口。如果想看生成的$Proxy0生成的class,可以添加代码System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");来保存$Proxy0.class.
下面来看看生成的代码:

final class $Proxy0 extends Proxy implements HelloInterface {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}

public final boolean equals(Object var1) throws  {
    try {
        return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}

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

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

public final int hashCode() throws  {
    try {
        return (Integer)super.h.invoke(this, m0, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

static {
    try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m3 = Class.forName("test.proxy.HelloInterface").getMethod("sayHello");
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
}

}

欢迎关注我的公众号:


奔跑蜗牛

相关文章

  • Java动态代理简析原理

    说下Java动态代理,Spring的AOP就是基于Java的动态代理实现的。动态代理用到的几个类和接口,Proxy...

  • Java动态代理从入门到原理再到实战

    目录 前言 什么是动态代理,和静态代理有什么区别 Java动态代理的简单使用 Java动态代理的原理解读 动态代理...

  • java基础巩固笔记(4)-代理

    标签: java Contents java基础巩固笔记(4)-代理概念动态代理创建动态类动态代理的工作原理面向切...

  • Java动态代理

    参考来源: Java动态代理视频 JDK动态代理实现原理 JDK Dynamic Proxies Building...

  • Java动态代理

    Spring的核心AOP的原理就是java的动态代理机制。所以,我们需要好好将java动态代理机制进行梳理。 ...

  • 你从来都不知道,Java动态代理竟然也是如此的简单!

    这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、...

  • 浅谈cglib动态代理

    cglib的使用与解析 上一篇讲解了java原生的动态代理的使用和部分原理,除了原生的动态代理很多java框架中使...

  • JDK动态代理实现原理:

    JDK动态代理实现原理思路: 1. 声明一段源码,这段源码动态生成我们的动态代理; 2. 把源码生成Java文件;...

  • java 动态代理封装使用

    1、java 动态代理原理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责...

  • Spring中AOP的特性解析

    要了解Spring的AOP就必须要了解动态代理的原理,因为AOP就是基于动态代理实现的。 java.lang....

网友评论

    本文标题:Java动态代理简析原理

    本文链接:https://www.haomeiwen.com/subject/ncsajftx.html