zoukankan      html  css  js  c++  java
  • Spring AOP

    本文记述AOP相关的代理模式

    静态代理

    1.定义

    为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通过代理对象访问目标对象,防止直接访问目标对象造成系统复杂性提升.

    2.原理

    Subject : 接口,为RealSubject和ProxySubject提供一致性的接口

    RealSubject : 目标访问类,实际的主体,实现Subject接口的类

    ProxySubject : 代理类,处理Client的请求,只有当代理类无法回复请求时,才会调用目标访问类

    Client : 请求者

    3.适用场景

    Virtual Proxy : 对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象。

    Remote Proxy : 远程代理使得客户端可以访问远程主机上的对象,远程代理可以把网络的细节隐藏起来,使得客户端不必考虑网络的存在,客户端完全可以认为调用的远程代理对象在本地,而不是在远程

    Cache Proxy : 某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,从而可以避免某些方法的重复执行,优化系统性能。 

    Access Proxy : 可以在调用RealSubject时做访问限制,通过代理过滤不可以访问的对象 

    4.实现

    Printable.java

    public interface Printable {
        void setPrintName(String name);
    
        String getPrintName();
    
        void print(String str);
    }  
    

    Printer.java

    public class Printer implements Printable {
        private String name;
    
        public Printer() {
            heavyJob("正在生成Printer的实例");
        }
    
        public Printer(String name) {
            this.name = name;
            heavyJob("正在生成(Printer)" + name + "的实例");
        }
    
    
        @Override
        public void setPrintName(String name) {
            this.name = name;
        }
    
        @Override
        public String getPrintName() {
            return this.name;
        }
    
        @Override
        public void print(String str) {
            System.out.println("==" + name + "==");
            System.out.println(str);
        }
    
        public void heavyJob(String msg) {
            System.out.println(msg);
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print(".");
            }
            System.out.println("over");
        }
    }  

    PrintProxy.java

    public class PrintProxy implements Printable {
        private String name;
        private Printer real;
    
        public PrintProxy(String name) {
            this.name = name;
        }
    
        @Override
        public synchronized void setPrintName(String name) {
            if (real != null) {
                real.setPrintName(name);
            }
            this.name = name;
        }
    
        @Override
        public String getPrintName() {
            return this.name;
        }
    
        @Override
        public void print(String str) {
            realize();
            real.print(str);
        }
    
        public synchronized void realize() {
            if (real == null) {
                real = new Printer(name);
            }
        }
    }  

    Main.java

    public class Main {
        public static void main(String[] args) {
            Printable p = new PrintProxy("kristin");
            System.out.println("name: " + p.getPrintName());
            p.setPrintName("kkk");
            System.out.println("new name: " + p.getPrintName());
            p.print("hello world");
        }
    }
    

      

    Printable相当于Subject

    Printer相当于RealSubject

    PrinterProxy相当于ProxySubject

    Main相当于Client

    5.优缺点

    优点:

    协调调用者与被调用者,降低耦合度

    作为客户端对象与目标对象的中介,可以有效地保护目标对象

    缺点:

    在客户端与目标对象之间增加了代理对象,处理请求的速度可能会变慢

    如果代理的实现复杂,可能会增加系统实现的复杂性

    如果想要为多个类进行代理,需要创建多个代理类,维护难度加大 

      

    JDK动态代理

    1.定义

     代理类在程序运行时被创建,并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的.

    2.原理

    Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
    代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

    3.应用

    如果想对代理类的所有方法都加上日志,可以通过动态代理可以对代理类的所有方法进行统一的处理,而不用一一更改每一个方法.  

    UserDao.java

    /**
     * 真实类的接口
     */
    public interface UserDao {
        void add();
    }  

    UserDaoImpl.java

    /**
     * 真实类
     */
    public class UserDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("this is method-add running");
        }
    }
    

    MyInvocationHandler.java  

    /**
     * 运行时生成代理类的Handler
     */
    public class MyInvocationHandler implements InvocationHandler {
        /**
         * 真实类对象
         */
        private Object target;
    
        /**
         * 将真实类的实例对象设置到自定义InvocationHandler中
         *
         * @param target
         */
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        /**
         * 运行时生成真实类的代理类
         *
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("-------------------before------------------");
            Object result = method.invoke(target, args);
            System.out.println("--------------------after-------------------");
            return result;
        }
    }
    

    Main.java

    public class Main {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            UserDao user = new UserDaoImpl();     //实例化真实类
            MyInvocationHandler handler = new MyInvocationHandler(user);
            //保存class文件到本地,可以在项目根目录下的com.sun.proxy文件夹中看到$Proxy.class
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
            //运行时生成代理类
            /**
             * 方法一:
             */
            Class classProxy = Proxy.getProxyClass(user.getClass().getClassLoader(), UserDao.class);
            Constructor constructor = classProxy.getConstructor(InvocationHandler.class);
            UserDao userDao = (UserDao) constructor.newInstance(new MyInvocationHandler(user));
            userDao.add();
    
            /**
             * 方法二
             */
    //        UserDao userDao = (UserDao) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), handler);
    //        UserDao userDao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(), new Class[]{UserDao.class}, handler);
    //        userDao.add();  //调用代理类的方法
    
    
        }
    }
    

    4.实现原理

    4.1相关的类和接口

    Class : java.lang.reflect.Proxy

    Interface : java.lang.reflect.InvocationHandler

    4.2代理步骤

    1.Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)</p>

    方法参数:
    1. loader : 类加载器
    2. interfaces : 目标对象实现的接口
    3. h : InvocationHandler的实现类

    以下是Proxy.java的newProxyInstance方法

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h); // 如果h为null,就会抛出空指针异常,所以一定要有InvocationHandler的实现类
    
            final Class<?>[] intfs = interfaces.clone();  //拷贝目标对象所实现的接口
            final SecurityManager sm = System.getSecurityManager(); //进行一些安全检查
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * 根据类加载器和接口生成代理类
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
                //获取代理类的构造函数对象
                //参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class };
                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);
            }
        }
    

    以下是Proxy类的getProxyClass0方法, 生成代理类

    private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            //接口数不能超过65535
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            //如果缓存中有代理类就直接返回,否则由ProxyClassFactory生成
            return proxyClassCache.get(loader, interfaces);
        }
    

    以下是Proxy的内部类ProxyClassFactory,看看ProxyClassFactory是怎样生成的代理类

    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 所有代理类的名称前缀为"$Proxy"
        private static final String proxyClassNamePrefix = "$Proxy";
    
        // 使用唯一的编号作为代理类名称的一部分
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
            Map<Class<?>, Boolean> interfaceSet = new IdentityMyMyHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * 验证指定类加载器加载的Class对象是否与intf加载的Class对象相同
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * 验证Class对象是否是一个接口
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * 验证该接口是否重复
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
    
            String proxyPkg = null;     // 声明代理类所在包
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
            /*
             * 验证传入的接口是否为非public的,保证非public接口的代理类将被定义在同一个包中
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); //截取完整的包名
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
            
            if (proxyPkg == null) {
                // 如果没有非public接口,那么这些代理类都放在com.sun.proxy包中
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
    
            /*
             * 将nextUniqueNumber通过cas加1,对proxy类名编号进行generate,初始为1
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;      //代理类的全限定名,如com.sun.proxy.$Proxy0.calss,
    
            /*
             * 生成代理类的字节码文件
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    

    再看一看是怎样生成字节码文件的呢?
    以下是sun.misc.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;
    }
    

    以下是sun.misc.ProxyGenerator的内部静态方法,也是真正生成代理类字节码文件的方法

    private byte[] generateClassFile() {
        /* 将Object的三个方法,hashCode、equals、toString添加到代理类容器中
         * hashCodeMethod方法位于静态代码块中通过Object对象获得,
         * hashCodeMethod=Object.class.getMethod("hashCode",new Class[0]),
         * 相当于从Object中继承过来了这三个方法equalsMethod,toStringMethod
         */ 
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;
    
        int var3;
        Class var4;
        //将所有接口中的所有方法添加到代理方法中
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;
    
            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }
    
        Iterator var11 = this.proxyMethods.values().iterator();
    
        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);    //验证具有相同方法签名的的方法的返回值类型是否一致,因为不可能有两个方法名相同,参数相同,而返回值却不同的方法
        }
        //以下步骤是写代理类文件
        Iterator var15;
        try {
            this.methods.add(this.generateConstructor());    //生成代理类构造函数
            var11 = this.proxyMethods.values().iterator();
    
            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();
    
                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    /* 将代理字段声明为Method,10为ACC_PRIVATE和ACC_STATAIC的与运算,表示该字段的修饰符为private static
                     * 所以代理类的字段都是private static Method XXX
                     */
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    //生成代理类的代理方法
                    this.methods.add(var16.generateMethod());
                }
            }
            //为代理类生成静态代码块,对一些字段进行初始化
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }
    
        if (this.methods.size() > 65535) {    //代理方法数量超过65535将抛出异常
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {    //代理类的字段数量超过65535将抛出异常
            throw new IllegalArgumentException("field limit exceeded");
        } else {    //从这里开始就是写代理类字节码文件啦
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;
    
            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }
    
            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);
    
            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;
    
                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }
    
                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();
    
                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }
    
                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();
    
                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }
    
                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }
    

    是怎样将Object方法添加到代理类容器的呢?下面我们看一看sun.misc.ProxyGenerator中的addProxyMethod方法

    private void addProxyMethod(Method var1, Class<?> var2) {
        String var3 = var1.getName();    // 方法名
        Class[] var4 = var1.getParameterTypes();    // 方法参数类型
        Class var5 = var1.getReturnType();    // 方法返回值类型
        Class[] var6 = var1.getExceptionTypes();    // 方法异常类型
        String var7 = var3 + getParameterDescriptors(var4);    // 方法签名
        Object var8 = (List)this.proxyMethods.get(var7);    //根据方法签名获得方法对象
        if (var8 != null) {    // 处理多个代理接口中的重复方法
            Iterator var9 = ((List)var8).iterator();
    
            while(var9.hasNext()) {
                ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
                if (var5 == var10.returnType) {
                    ArrayList var11 = new ArrayList();
                    collectCompatibleTypes(var6, var10.exceptionTypes, var11);
                    collectCompatibleTypes(var10.exceptionTypes, var6, var11);
                    var10.exceptionTypes = new Class[var11.size()];
                    var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
                    return;
                }
            }
        } else {
            var8 = new ArrayList(3);
            this.proxyMethods.put(var7, var8);
        }
    
        ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null));
    }
    

    通过下面这个设置,可以将代理类字节码文件保存到项目根目录下的com.sun.proxy文件夹中看到$Proxy.class
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

    下面我们看一看$Proxy0.class

    package com.sun.proxy;
    
    import com.kristin.design_pattern.proxy.jdk_proxy.UserDao;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements UserDao {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        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 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 void add() throws  {
            try {
                /**
                 * this:  当前生成的$Proxy0实例
                 * m3: 就是我们定义的接口方法
                 * (Object)null: 被传入的被调用的接口方法的实际参数
                 * 
                 * 可以看到实际上这个方法中的3个参数Object proxy, Method method, Object[] args就是$Proxy0通过h.invoke(this, m3, new Object[]{var1});传递给我们的,
                 * 也就是说,我们实现的InvocationHandler的invoke()实际上是一个回调,也就是我们预先定义好的,然后JDK生成的类$Proxy0回过来调用的。
                 */
                super.h.invoke(this, m3, (Object[])null);   //注意这里super.h其实就是InvocationHandler的对象h
            } 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"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.kristin.design_pattern.proxy.jdk_proxy.UserDao").getMethod("add");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }  

    IN ALL:

    JDK动态代理的基本原理就是 我们定义好接口和默认实现,JDK根据通过生成class文件的方式”动态”的生成一个代理类,这个代理类实现了我们定义的接口,并在接口实现方法中回调了我们通过InvocationHandler定义的处理流程,这个处理流程中我们回去调用默认实现,并提供增强。

    不足:

    JDK动态代理只能代理实现了接口的类,而Cglib动态代理可以代理没有实现接口的类

    JDK vs CGLIB:

    JDK动态代理和CGLIB字节码生成的区别?

    (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类

    (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
    因为是继承,所以该类或方法最好不要声明成final

    结论

    1.同样情况下,cglib两种实现方式,invokeSuper + setSuperClass 永远比 invoke + setInterfaces慢

    2.cglib invoke + setInterfaces 在方法数量较少的时候,在函数平均调用的情况下 比jdkProxy快,随着函数增多,优势越来越不明显,到达某个数量级一定比jdk动态代理慢

    3.cglib invoke + setInterfaces 在调用特定函数(在switch中靠后的case) 会比jdk动态代理慢


    思考

    cglib的瓶颈在于:
    调用net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])时需要switch的case:
    如果有n个函数,那么就要一个个比较,复杂度O(n)
    这里如果有一个key -> behavior的映射就好了,目前并没有。
    如果可以用asm在这里写成一个二分搜索,cglib就会快多了,变成O(log2 n),时间上就是一个飞跃,只不过这个fastclass就会看起来很丑。(目前最新3.2.5的版本也没有改动这一块)

    CGLIB动态代理

    1.定义

    cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个类,并覆盖其中方法实现增强
    代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理.

    2.应用

    UserDao.java

    public interface UserDao {
        void add();
    }
    

    UserDaoImpl.java

    public class UserDaoImpl implements UserDao {
        public void add() {
            System.out.println("the method is running");
        }
    }
    

    Interceptor.java  

    public class Interceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("-------------------before------------------");
            proxy.invokeSuper(obj, args);
            System.out.println("--------------------after-------------------");
            return null;
        }
    }
    

     

    Main.java

    public class Main {
        @org.junit.jupiter.api.Test
        public void test() {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\code");
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserDaoImpl.class);
            enhancer.setCallback(new Interceptor());
            UserDao userDao = (UserDao) enhancer.create();
            userDao.add();
        }
    }

    3.实现原理

    代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理

    创建代理对象的几个步骤:
    1.生成代理类的二进制字节码文件;
    2.加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 );
    3.通过反射机制获得实例构造,并创建代理类对象

     代理类Class文件反编译后的Java代码:

    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Factory;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 extends UserDaoImpl implements Factory {
        private boolean CGLIB$BOUND;
        public static Object CGLIB$FACTORY_DATA;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
        private MethodInterceptor CGLIB$CALLBACK_0;
        private static Object CGLIB$CALLBACK_FILTER;
        private static final Method CGLIB$add$0$Method;
        private static final MethodProxy CGLIB$add$0$Proxy;
        private static final Object[] CGLIB$emptyArgs;
        private static final Method CGLIB$equals$1$Method;
        private static final MethodProxy CGLIB$equals$1$Proxy;
        private static final Method CGLIB$toString$2$Method;
        private static final MethodProxy CGLIB$toString$2$Proxy;
        private static final Method CGLIB$hashCode$3$Method;
        private static final MethodProxy CGLIB$hashCode$3$Proxy;
        private static final Method CGLIB$clone$4$Method;
        private static final MethodProxy CGLIB$clone$4$Proxy;
    
        static void CGLIB$STATICHOOK1() {
            CGLIB$THREAD_CALLBACKS = new ThreadLocal();
            CGLIB$emptyArgs = new Object[0];
            Class var0 = Class.forName("com.kristin.design_pattern.proxy.cglib_proxy.UserDaoImpl$$EnhancerByCGLIB$$3ef42b63");
            Class var1;
            Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
            CGLIB$equals$1$Method = var10000[0];
            CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
            CGLIB$toString$2$Method = var10000[1];
            CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
            CGLIB$hashCode$3$Method = var10000[2];
            CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
            CGLIB$clone$4$Method = var10000[3];
            CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
            CGLIB$add$0$Method = ReflectUtils.findMethods(new String[]{"add", "()V"}, (var1 = Class.forName("com.kristin.design_pattern.proxy.cglib_proxy.UserDaoImpl")).getDeclaredMethods())[0];
            CGLIB$add$0$Proxy = MethodProxy.create(var1, var0, "()V", "add", "CGLIB$add$0");
        }
    
        final void CGLIB$add$0() {
            super.add();
        }
    
        public final void add() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$add$0$Method, CGLIB$emptyArgs, CGLIB$add$0$Proxy);
            } else {
                super.add();
            }
        }
    
        final boolean CGLIB$equals$1(Object var1) {
            return super.equals(var1);
        }
    
        public final boolean equals(Object var1) {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
                return var2 == null ? false : (Boolean)var2;
            } else {
                return super.equals(var1);
            }
        }
    
        final String CGLIB$toString$2() {
            return super.toString();
        }
    
        public final String toString() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
        }
    
        final int CGLIB$hashCode$3() {
            return super.hashCode();
        }
    
        public final int hashCode() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
                return var1 == null ? 0 : ((Number)var1).intValue();
            } else {
                return super.hashCode();
            }
        }
    
        final Object CGLIB$clone$4() throws CloneNotSupportedException {
            return super.clone();
        }
    
        protected final Object clone() throws CloneNotSupportedException {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
        }
    
        public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
            String var10000 = var0.toString();
            switch(var10000.hashCode()) {
            case -1422568652:
                if (var10000.equals("add()V")) {
                    return CGLIB$add$0$Proxy;
                }
                break;
            case -508378822:
                if (var10000.equals("clone()Ljava/lang/Object;")) {
                    return CGLIB$clone$4$Proxy;
                }
                break;
            case 1826985398:
                if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                    return CGLIB$equals$1$Proxy;
                }
                break;
            case 1913648695:
                if (var10000.equals("toString()Ljava/lang/String;")) {
                    return CGLIB$toString$2$Proxy;
                }
                break;
            case 1984935277:
                if (var10000.equals("hashCode()I")) {
                    return CGLIB$hashCode$3$Proxy;
                }
            }
    
            return null;
        }
    
        public UserDaoImpl$$EnhancerByCGLIB$$3ef42b63() {
            CGLIB$BIND_CALLBACKS(this);
        }
    
        public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
            CGLIB$THREAD_CALLBACKS.set(var0);
        }
    
        public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
            CGLIB$STATIC_CALLBACKS = var0;
        }
    
        private static final void CGLIB$BIND_CALLBACKS(Object var0) {
            UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var1 = (UserDaoImpl$$EnhancerByCGLIB$$3ef42b63)var0;
            if (!var1.CGLIB$BOUND) {
                var1.CGLIB$BOUND = true;
                Object var10000 = CGLIB$THREAD_CALLBACKS.get();
                if (var10000 == null) {
                    var10000 = CGLIB$STATIC_CALLBACKS;
                    if (CGLIB$STATIC_CALLBACKS == null) {
                        return;
                    }
                }
    
                var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
            }
    
        }
    
        public Object newInstance(Callback[] var1) {
            CGLIB$SET_THREAD_CALLBACKS(var1);
            UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Callback var1) {
            CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
            UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        }
    
        public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
            CGLIB$SET_THREAD_CALLBACKS(var3);
            UserDaoImpl$$EnhancerByCGLIB$$3ef42b63 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$3ef42b63;
            switch(var1.length) {
            case 0:
                var10000.<init>();
                CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
                return var10000;
            default:
                throw new IllegalArgumentException("Constructor not found");
            }
        }
    
        public Callback getCallback(int var1) {
            CGLIB$BIND_CALLBACKS(this);
            MethodInterceptor var10000;
            switch(var1) {
            case 0:
                var10000 = this.CGLIB$CALLBACK_0;
                break;
            default:
                var10000 = null;
            }
    
            return var10000;
        }
    
        public void setCallback(int var1, Callback var2) {
            switch(var1) {
            case 0:
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
            default:
            }
        }
    
        public Callback[] getCallbacks() {
            CGLIB$BIND_CALLBACKS(this);
            return new Callback[]{this.CGLIB$CALLBACK_0};
        }
    
        public void setCallbacks(Callback[] var1) {
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
        }
    
        static {
            CGLIB$STATICHOOK1();
        }
    }
    

    CGLIB在进行代理的时候都进行了哪些工作呢

    1.生成的代理类UserDaoImpl$$EnhancerByCGLIB$$3ef42b63.class继承被代理类UserDaoImpl。在这里我们需要注意一点:如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;
    2.代理类会为委托方法生成两个方法,一个是重写的add方法,另一个是CGLIB$add$0方法,我们可以看到它是直接调用父类的add方法;
    3.当执行代理对象的add方法时,会首先判断一下是否存在实现了MethodInterceptor接口的CGLIB$CALLBACK_0;,如果存在,则将调用MethodInterceptor中的intercept方法。

    在intercept方法中,我们除了会调用委托方法,还会进行一些增强操作。在Spring AOP中,典型的应用场景就是在某些敏感方法执行前后进行操作日志记录。

    调用委托方法是通过代理方法的MethodProxy对象调用invokeSuper方法来执行的,下面我们看看invokeSuper方法:

    /**
         * Invoke the original (super) method on the specified object.
         * @param obj the enhanced object, must be the object passed as the first
         * argument to the MethodInterceptor
         * @param args the arguments passed to the intercepted method; you may substitute a different
         * argument array as long as the types are compatible
         * @see MethodInterceptor#intercept
         * @throws Throwable the bare exceptions thrown by the called method are passed through
         * without wrapping in an <code>InvocationTargetException</code>
         */
        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                init();
                FastClassInfo fci = fastClassInfo;
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    
    private static class FastClassInfo
        {
            FastClass f1;
            FastClass f2;
            int i1;
            int i2;
        }
    

      

    f1指向委托类对象

    f2指向代理类对象

    i1和i2分别代表着add方法以及CGLIB$add$0方法在对象信息数组中的下标。

    总结  

    代理方式 实现 特点 优点 缺点
    静态代理 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 好像没啥特点 实现简单,容易理解 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低
    JDK动态代理 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 底层使用反射机制进行方法的调用 不需要硬编码接口,代码复用率高 只能够代理实现了接口的委托类
    CGLIB动态代理 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 不能对final类以及final方法进行代理

    参考:
    https://www.cnblogs.com/MOBIN/p/5597215.html

    https://since1986.github.io/blog/bf178159.html

    https://www.jianshu.com/p/1aaacf92e2cd

    https://h2pl.github.io/2018/06/03/spring5/

    https://www.cnblogs.com/cruze/p/3865180.html

  • 相关阅读:
    杭电ACM 2052 Picture
    杭电ACM求平均成绩
    杭电ACM水仙花数
    cigarettes
    分数加减法
    推荐几个sql server牛人的博客
    npm 介绍
    centos Flash Player插件的安装
    node.js学习(1)
    查询功能:yum [list|info|search|provides|whatprovides] 参数
  • 原文地址:https://www.cnblogs.com/Hangtutu/p/9528707.html
Copyright © 2011-2022 走看看