Java 动态代理

JDK Proxy

例子

https://github.com/lunaczp/java/blob/master/hello/src/com/lunac/learning/java/proxy/JDKProxy.java

package com.lunac.learning.java.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy {

    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("drive")) {
                    System.out.println("let's drive " + args[0]);
                }
                return null;
            }
        };

        Car car = (Car) Proxy.newProxyInstance(
                Car.class.getClassLoader(),
                new Class[] {Car.class},
                handler
        );
        car.drive("cow");
    }
}

interface Car {
    void drive(String something);
}

代理类的生成

//获取代理类
//通过ProxyGenerator动态生成了指定的interface的实现类
"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at sun.misc.ProxyGenerator.generateClassFile(ProxyGenerator.java:570)
	  at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:339)
	  at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:639)
	  at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	  at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
	  - locked <0x20a> (a java.lang.reflect.WeakCache$Factory)
	  at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	  at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	  at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	  at com.lunac.learning.java.proxy.JDKProxy.main(JDKProxy.java:19)

//实例化代理类
"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	  at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:739)
	  at com.lunac.learning.java.proxy.JDKProxy.main(JDKProxy.java:19)

JDK源码(以JDK8为例,使用openJDK代码)

https://github.com/lunaczp/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/misc/ProxyGenerator.java

/**
     * Generate a class file for the proxy class.  This method drives the
     * class file generation process.
     */
    private byte[] generateClassFile() {

        /* ============================================================
         * Step 1: Assemble ProxyMethod objects for all methods to
         * generate proxy dispatching code for.
         */

        /*
         * Record that proxy methods are needed for the hashCode, equals,
         * and toString methods of java.lang.Object.  This is done before
         * the methods from the proxy interfaces so that the methods from
         * java.lang.Object take precedence over duplicate methods in the
         * proxy interfaces.
         */
        addProxyMethod(hashCodeMethod, Object.class);
        addProxyMethod(equalsMethod, Object.class);
        addProxyMethod(toStringMethod, Object.class);

        /*
         * Now record all of the methods from the proxy interfaces, giving
         * earlier interfaces precedence over later ones with duplicate
         * methods.
         */
        for (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                addProxyMethod(m, intf);
            }
        }

        /*
         * For each set of proxy methods with the same signature,
         * verify that the methods' return types are compatible.
         */
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            checkReturnTypes(sigmethods);
        }

        /* ============================================================
         * Step 2: Assemble FieldInfo and MethodInfo structs for all of
         * fields and methods in the class we are generating.
         */
        try {
            methods.add(generateConstructor());

            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                for (ProxyMethod pm : sigmethods) {

                    // add static field for method's Method object
                    fields.add(new FieldInfo(pm.methodFieldName,
                        "Ljava/lang/reflect/Method;",
                         ACC_PRIVATE | ACC_STATIC));

                    // generate code for proxy method and add it
                    methods.add(pm.generateMethod());
                }
            }

            methods.add(generateStaticInitializer());

        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }

        if (methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }

        /* ============================================================
         * Step 3: Write the final class file.
         */

        /*
         * Make sure that constant pool indexes are reserved for the
         * following items before starting to write the final class file.
         */
        cp.getClass(dotToSlash(className));
        cp.getClass(superclassName);
        for (Class<?> intf: interfaces) {
            cp.getClass(dotToSlash(intf.getName()));
        }

        /*
         * Disallow new constant pool additions beyond this point, since
         * we are about to write the final constant pool table.
         */
        cp.setReadOnly();

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);

        try {
            /*
             * Write all the items of the "ClassFile" structure.
             * See JVMS section 4.1.
             */
                                        // u4 magic;
            dout.writeInt(0xCAFEBABE);
                                        // u2 minor_version;
            dout.writeShort(CLASSFILE_MINOR_VERSION);
                                        // u2 major_version;
            dout.writeShort(CLASSFILE_MAJOR_VERSION);

            cp.write(dout);             // (write constant pool)

                                        // u2 access_flags;
            dout.writeShort(accessFlags);
                                        // u2 this_class;
            dout.writeShort(cp.getClass(dotToSlash(className)));
                                        // u2 super_class;
            dout.writeShort(cp.getClass(superclassName));

                                        // u2 interfaces_count;
            dout.writeShort(interfaces.length);
                                        // u2 interfaces[interfaces_count];
            for (Class<?> intf : interfaces) {
                dout.writeShort(cp.getClass(
                    dotToSlash(intf.getName())));
            }

                                        // u2 fields_count;
            dout.writeShort(fields.size());
                                        // field_info fields[fields_count];
            for (FieldInfo f : fields) {
                f.write(dout);
            }

                                        // u2 methods_count;
            dout.writeShort(methods.size());
                                        // method_info methods[methods_count];
            for (MethodInfo m : methods) {
                m.write(dout);
            }

                                         // u2 attributes_count;
            dout.writeShort(0); // (no ClassFile attributes for proxy classes)

        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }

        return bout.toByteArray();
    }

CgLib Proxy

例子

https://github.com/lunaczp/java/blob/master/hello/src/com/lunac/learning/java/proxy/CglibProxy.java

package com.lunac.learning.java.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before Intercept");
        methodProxy.invokeSuper(o, objects);
        System.out.println("after Intercept");
        return null;
    }

    public static void main(String[] args) {
        CCar cCar = Factory.getInstance(new CglibProxy());
        cCar.drive();
    }
}

class Factory {

    public static CCar getInstance(CglibProxy proxy) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CCar.class);
        enhancer.setCallback(proxy);
        return (CCar) enhancer.create();
    }
}

class CCar {

    public CCar() {
        System.out.println("i'm in ccar constructor now");

    }

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

}

被代理类的实例化过程

"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at com.lunac.learning.java.proxy.CCar.<init>(CglibProxy.java:37)
	  at com.lunac.learning.java.proxy.CCar$$EnhancerByCGLIB$$f9118a4f.<init>(<generated>:-1)
	  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(NativeConstructorAccessorImpl.java:-1)
	  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	  at net.sf.cglib.core.ReflectUtils.newInstance(ReflectUtils.java:291)
	  at net.sf.cglib.proxy.Enhancer$EnhancerFactoryData.newInstance(Enhancer.java:448)
	  at net.sf.cglib.proxy.Enhancer.nextInstance(Enhancer.java:700)
	  at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:298)
	  at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
	  at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
	  at com.lunac.learning.java.proxy.Factory.getInstance(CglibProxy.java:30)
	  at com.lunac.learning.java.proxy.CglibProxy.main(CglibProxy.java:19)

被代理类的方法的执行过程

"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at com.lunac.learning.java.proxy.CCar.drive(CglibProxy.java:42)
	  at com.lunac.learning.java.proxy.CCar$$EnhancerByCGLIB$$f9118a4f.CGLIB$drive$0(<generated>:-1)
	  at com.lunac.learning.java.proxy.CCar$$EnhancerByCGLIB$$f9118a4f$$FastClassByCGLIB$$2d4d573.invoke(<generated>:-1)
	  at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
	  at com.lunac.learning.java.proxy.CglibProxy.intercept(CglibProxy.java:13)
	  at com.lunac.learning.java.proxy.CCar$$EnhancerByCGLIB$$f9118a4f.drive(<generated>:-1)
	  at com.lunac.learning.java.proxy.CglibProxy.main(CglibProxy.java:20)