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

posted @ 2018-08-24 16:55  KristinLee  阅读(198)  评论(0编辑  收藏  举报
jQuery火箭图标返回顶部代码