JDK动态代理

 

静态代理是通过在代码中显式编码定义一个业务实现类的代理类,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

静态代理在编译时产生class字节码文件,可以直接使用,效率高。动态代理必须实现InvocationHandler接口,通过invoke调用被委托类接口方法是通过反射方式,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。 cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

此篇只介绍jdk的动态代理,而不会说cglib代理。

JDK动态代理的代理类字节码在创建时,需要实现业务实现类所实现的接口作为参数。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。(JDK动态代理重要特点是代理接口) 并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

动态代理只能对接口产生代理,不能对类产生代理。

新建一个接口,一个类实现此接口:

public interface IPeople {
    void eat();
}
public class Student implements IPeople{
    @Override
    public void eat() {
        System.out.println("Student eat");
    }
}

新建类,实现InvokeHandle接口:

public class ProxyHandle implements InvocationHandler {
    // 此object是被代理对象
    private Object object;
    public ProxyHandle(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        method.invoke(object,args);
        System.out.println("after");
        return null;
    }
}

新建测试类:

public class Test {
    public static void main(String[] args) {
        IPeople iPeople = new Student();
        ProxyHandle p = new ProxyHandle(iPeople);
        IPeople proxy = (IPeople) Proxy.newProxyInstance(iPeople.getClass().getClassLoader(),
                iPeople.getClass().getInterfaces(), p);
        proxy.eat();
    }
}

打印如下:

before
Student eat
after

源码

我们通过Proxy.newProxyInstance()得到代理对象,进入此方法:

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

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

我们断点进入此方法,看看是哪一步产生代理对象:

 上面这一步产生了代理对象:

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); }

 进入get()方法:

 apply()的方法逻辑在Proxy类的内部类ProxyClassFactory中:

 ProxyGenerator.generateProxyClass()生成字节码:

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    final byte[] var4 = var3.generateClassFile();
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        Files.createDirectories(var3);
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        var2 = Paths.get(var0 + ".class");
                    }

                    Files.write(var2, var4, new OpenOption[0]);
                    return null;
                } catch (IOException var4x) {
                    throw new InternalError("I/O exception saving generated file: " + var4x);
                }
            }
        });
    }

    return var4;

 generateClassFile()方法生成代理对象字节码,这里var0,var1,var2是

proxyName, interfaces, accessFlags

 通过defineClass0()再解析字节码,生成代理对象。

原文地址:https://www.cnblogs.com/SunSAS/p/12318677.html