美文网首页Java 杂谈
java动态代理技术解析

java动态代理技术解析

作者: braveheart075 | 来源:发表于2019-05-29 16:34 被阅读0次

    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<Void>() {
                    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动态代理技术解析

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