zoukankan      html  css  js  c++  java
  • 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()再解析字节码,生成代理对象。

  • 相关阅读:
    Get distinct count of rows in the DataSet
    单引号双引号的html转义符
    PETS Public English Test System
    Code 39 basics (39条形码原理)
    Index was outside the bounds of the array ,LocalReport.Render
    Thread was being aborted Errors
    Reportviewer Error: ASP.NET session has expired
    ReportDataSource 值不在预期的范围内
    .NET/FCL 2.0在Serialization方面的增强
    Perl像C一样强大,像awk、sed等脚本描述语言一样方便。
  • 原文地址:https://www.cnblogs.com/SunSAS/p/12318677.html
Copyright © 2011-2022 走看看