java动态代理技术解析

jdk动态代理

所谓的动态代理,就是在运行时生成一个代理类,来执行被代理类的方法。

使用

1、创建一个接口对象

public interface Subject {
    void subject();
}

2、创建一个接口对象的实现类

public class RealSubject implements Subject {
    @Override public void subject() {
        System.out.println("real subject");
    }
}

3、创建一个InvocationHandler
这个handler就是代理subject对象。至于是哪个对象,就看有哪些实现。

public class Handler implements InvocationHandler {
    Subject subject;

    public Handler(Subject subject) {
        this.subject = subject;
    }

    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("handler invoke method.");
        method.invoke(subject,args);
        return subject;
    }
}

4、测试

public class Demo {
    public static void main(String[] args) {
        Subject sub = new RealSubject();

        Subject subject = (Subject) Proxy.newProxyInstance(sub.getClass().getClassLoader(),sub.getClass().getInterfaces(),new Handler(sub));

        System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class));
        subject.subject();
    }
}

原理

这里,我们可以看下生成的代理类,网络上搜索到的基本都是错误的方法,很多都是照抄的,我之前写过一篇文章,论技术人员的严谨性问题,说的就是这个现象。

1、这个$Proxy0的生成,是ProxyGenerator这个jdk包中的类操作的,我们看下他的方法:
很明显,有个saveGeneratedFiles判断,那么这个从哪里来?

    static byte[] generateProxyClass(final String name,
                                     Class[] interfaces,
                                     int accessFlags)
    {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        final byte[] classFile = gen.generateClassFile();

        if (saveGeneratedFiles) {
            java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction() {
                public Void run() {
                    try {
                        int i = name.lastIndexOf('.');
                        Path path;
                        if (i > 0) {
                            Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar));
                            Files.createDirectories(dir);
                            path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                        } else {
                            path = Path.of(name + ".class");
                        }
                        Files.write(path, classFile);
                        return null;
                    } catch (IOException e) {
                        throw new InternalError(
                            "I/O exception saving generated file: " + e);
                    }
                }
            });
        }

        return classFile;
    }

2、看下这段代码:

/** debugging flag for saving generated class files */
    private static final boolean saveGeneratedFiles =
        java.security.AccessController.doPrivileged(
            new GetBooleanAction(
                "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();

3、所以,正确的方式是在环境变量里加-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true。
4、执行main方法,就能看到$Proxy0.class文件了。
5、看下这个class文件:
基本明白几点:

  • 动态代理必须要实现接口,很多人问为什么需要接口,因为代理类不能多继承,只能实现接口。
  • 代理类会继承Proxy类。
  • 如果执行了具体的代理类的方法,代理类会调用invocationHandler的invoke方法。所以invocationHandler很重要。
  • 这个代理类中会有四个Method,代理类生成的时候,其中三个是hashcode,tostring,equals,剩下的就是被代理的方法。会直接初始化。
public final class $Proxy0 extends Proxy implements Subject {
    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 subject() 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("proxy.Subject").getMethod("subject");
            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());
        }
    }
}

以上就是动态代理的本质,有兴趣的同学可以继续到super中去看看。

tips:知识贵在积累。

你可能感兴趣的:(java动态代理技术解析)