Java动态代理与CGLib代理

java的动态代理举足轻重,它同反射原理一直是许多框架的底层实现。今天唠一下。

一、动态代理所涉及的一些接口、类及方法

java动态代理的主要类和接口有:
java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler

  1. java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。其包含以下重要方法:

1、public static InvocationHandler getInvocationHandler(Object var0) 该方法用于获取指定代理对象所关联的调用处理器
2、public static Class<?> getProxyClass(ClassLoader var0, Class… var1) 该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
3、public static Object newProxyInstance(ClassLoader var0, Class<?>[]var1, InvocationHandler var2) 方法用于为指定类装载器,一组接口及调用处理器生成动态代理类实例

  1. java.lang.reflect.InvocationHandler:调用处理器接口,自定义invoke方法,用于实现对于真正委托类的代理访问

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

  1. java.lang.ClassLoader:类装载器类

将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。

二、动态代理例子

1.方法接口

public interface SayGoodbye {
    void sayGoodbye(String name);
}
public interface SayHello {
    void sayHello(String name);
}

2.接口实现

public class goodByeImpl implements SayGoodbye {
    @Override
    public void sayGoodbye(String name) {
        System.out.println("GoodBye"+name);
    }
}
public class HelloImpl implements SayHello{
    @Override
    public void sayHello(String name) {
        System.out.println("Hello"+name);
    }
}

3.代理实例类

public class ServiceProxy implements InvocationHandler {
    private Object target;
    public void InvokeBefore()
    {
        System.out.println("调用前");
    }
    public void InvokeAfter()
    {
        System.out.println("调用后");
    }
    public Object getInstance(Object target) {
        this.target = target;
        //target.getClass().getClassLoader() 类加载器,动态代理的类加载器必然和被代理的对象在同一个加载器,所以直接使用
        //target.getClass().getInterfaces() 实现的接口
        //this 该方法只负责实例化代理,至于代理业务的流程规范,由InvocationHandler(this)来定义
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }
    @Override
    //Object proxy代理类
    //Method method要拦截(优化)的方法
    //Object[] args方法的参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result;
        //反射方法前调用
        InvokeBefore();
        //反射执行方法  相当于调用target.sayHelllo;
        result = method.invoke(target,args);
        //反射方法后调用.
        InvokeAfter();
        return result;
    }
}

4.测试类

public class ProxyTest {
    public static void main(String[] args) {
        //生成proxy类
        ServiceProxy proxy = new ServiceProxy();
        //获取SayGoodbye代理对象,此处需要传入goodByeImpl实例给getInstance方法,最终被newProxyInstance调用
        SayGoodbye sgServiceProxy = (SayGoodbye) proxy.getInstance(new goodByeImpl());
        //通过代理对象调用方法
        sgServiceProxy.sayGoodbye("  lisi");
        //获取SayHello代理对象
        SayHello shServiceProxy=(SayHello)proxy.getInstance(new HelloImpl());
        //调用对象方法
        shServiceProxy.sayHello("  zhangsan");
    }
}

5.运行结果

调用前
GoodBye  lisi
调用后
调用前
Hello  zhangsan
调用后

总结:动态代理如果需求改变,只需修改接口方法以及实现类,而无需修改代理的实例,实现了解耦。

扫描二维码关注公众号,回复: 11309387 查看本文章

三、动态代理源码解析

1.Proxy类的静态方法newProxyInstance方法

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //验证传入的InvocationHandler是否为空
        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);
            }
            //获取参数类型是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;
                    }
                });
            }
            //传入InvocationHandler实例去构造一个代理类的实例,所有代理类都继承自Proxy,而Proxy构造方法需要InvocationHandler实例作为参数
            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);
        }
    }

从newProxyInstance方法看出,产生代理类核心代码在getProxyClass0

2.Proxy类的静态方法getProxyClass0方法

private static Class<?> getProxyClass0(ClassLoader loader,
                                             Class<?>... interfaces) {
          if (interfaces.length > 65535) {
              throw new IllegalArgumentException("interface limit exceeded");
        }
         //如果由实现给定接口的代理类存在,这将简单地返回缓存的副本;否则,将通过ProxyClassFactory创建代理类
         return proxyClassCache.get(loader, interfaces);
   }

getProxyClass0通过类加载器和接口集合去缓存里面获取,如果能找到代理类就直接返回,否则就会调用ProxyClassFactory这个工厂去生成一个代理类,下面我们看下Proxy的静态内部类ProxyClassFactory

3.ProxyClassFactory

  1 private static final class ProxyClassFactory
  2         implements BiFunction<ClassLoader, Class<?>[], Class<?>>
  3     {
  4         // prefix for all proxy class names 代理类名称前缀
  5         private static final String proxyClassNamePrefix = "$Proxy";
  6 
  7         // next number to use for generation of unique proxy class names, 用原子类来生成代理类的序号, 保证序号唯一
  8         private static final AtomicLong nextUniqueNumber = new AtomicLong();
  9 
 10         @Override
 11         public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 12 
 13             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
 14             for (Class<?> intf : interfaces) {
 15                 /*
 16                  * Verify that the class loader resolves the name of this
 17                  * interface to the same Class object.
 18                  */
 19                 Class<?> interfaceClass = null;
 20                 try {
 21                     interfaceClass = Class.forName(intf.getName(), false, loader);
 22                 } catch (ClassNotFoundException e) {
 23                 }
 24                 //intf是否可以由指定的类加载进行加载
 25                 if (interfaceClass != intf) {
 26                     throw new IllegalArgumentException(
 27                         intf + " is not visible from class loader");
 28                 }
 29                 /*
 30                  * Verify that the Class object actually represents an
 31                  * interface.
 32                  * intf是否是一个接口
 33                  */
 34                 if (!interfaceClass.isInterface()) {
 35                     throw new IllegalArgumentException(
 36                         interfaceClass.getName() + " is not an interface");
 37                 }
 38                 /*
 39                  * Verify that this interface is not a duplicate.
 40                  * intf在数组中是否有重复
 41                  */
 42                 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
 43                     throw new IllegalArgumentException(
 44                         "repeated interface: " + interfaceClass.getName());
 45                 }
 46             }
 47             // package to define proxy class in 生成代理类的包名
 48             String proxyPkg = null;
 49             // 代理类的访问标志, 默认是public final
 50             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
 51 
 52             /*
 53              * Record the package of a non-public proxy interface so that the
 54              * proxy class will be defined in the same package.  Verify that
 55              * all non-public proxy interfaces are in the same package.
 56              * 验证所有非公共代理接口都在同一个包中
 57              */
 58             for (Class<?> intf : interfaces) {
 59                 //获取接口的访问标志
 60                 int flags = intf.getModifiers();
 61                 //如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
 62                 if (!Modifier.isPublic(flags)) {
 63                     //生成的代理类的访问标志设置改为final
 64                     accessFlags = Modifier.FINAL;
 65                     String name = intf.getName();
 66                     int n = name.lastIndexOf('.');
 67                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
 68                     if (proxyPkg == null) {
 69                         proxyPkg = pkg;
 70                     } else if (!pkg.equals(proxyPkg)) {
 71                         //代理类如果实现不同包的接口, 并且接口都不是public的, 那么就会在这里报错
 72                         throw new IllegalArgumentException(
 73                             "non-public interfaces from different packages");
 74                     }
 75                 }
 76             }
 77 
 78             if (proxyPkg == null) {
 79                 // if no non-public proxy interfaces, use com.sun.proxy package 如果没有非公共代理接口,那生成的代理类都放到默认的包下:com.sun.proxy
 80                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
 81             }
 82 
 83             /*
 84              * Choose a name for the proxy class to generate.
 85              * 生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
 86              */
 87             long num = nextUniqueNumber.getAndIncrement();
 88             String proxyName = proxyPkg + proxyClassNamePrefix + num;
 89 
 90             /*
 91              * Generate the specified proxy class.
 92              * 这里是核心, 用ProxyGenerator来生成字节码, 该类放在sun.misc包下
 93              */
 94             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
 95                 proxyName, interfaces, accessFlags);
 96             //根据二进制文件生成相应的Class实例
 97             try {
 98                 return defineClass0(loader, proxyName,
 99                                     proxyClassFile, 0, proxyClassFile.length);
100             } catch (ClassFormatError e) {
101                 /*
102                  * A ClassFormatError here means that (barring bugs in the
103                  * proxy class generation code) there was some other
104                  * invalid aspect of the arguments supplied to the proxy
105                  * class creation (such as virtual machine limitations
106                  * exceeded).
107                  */
108                 throw new IllegalArgumentException(e.toString());
109             }
110         }
111     }

大boss终于出现了,下面我们分ProxyGenerator.generateClassFile
5.ProxyGenerator.generateClassFile

  1     private byte[] generateClassFile() {
  2         //1、将所有的方法组装成ProxyMethod对象
  3         //首先为代理类生成toString, hashCode, equals等代理方法
  4         this.addProxyMethod(hashCodeMethod, Object.class);
  5         this.addProxyMethod(equalsMethod, Object.class);
  6         this.addProxyMethod(toStringMethod, Object.class);
  7         Class[] var1 = this.interfaces;
  8         int var2 = var1.length;
  9 
 10         int var3;
 11         Class var4;
 12         //遍历每一个接口的每一个方法, 并生成ProxyMethod对象
 13         for(var3 = 0; var3 < var2; ++var3) {
 14             var4 = var1[var3];
 15             Method[] var5 = var4.getMethods();
 16             int var6 = var5.length;
 17 
 18             for(int var7 = 0; var7 < var6; ++var7) {
 19                 Method var8 = var5[var7];
 20                 this.addProxyMethod(var8, var4);
 21             }
 22         }
 23 
 24         Iterator var11 = this.proxyMethods.values().iterator();
 25 
 26         List var12;
 27         while(var11.hasNext()) {
 28             var12 = (List)var11.next();
 29             checkReturnTypes(var12);
 30         }
 31 
 32         //2、组装要生成的class文件的所有的字段信息和方法信息
 33         Iterator var15;
 34         try {
 35             //添加构造器方法
 36             this.methods.add(this.generateConstructor());
 37             var11 = this.proxyMethods.values().iterator();
 38 
 39             //遍历缓存中的代理方法
 40             while(var11.hasNext()) {
 41                 var12 = (List)var11.next();
 42                 var15 = var12.iterator();
 43 
 44                 while(var15.hasNext()) {
 45                     ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
 46                     //添加代理类的静态字段, 例如:private static Method m1;
 47                     this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
 48                     //添加代理类的代理方法
 49                     this.methods.add(var16.generateMethod());
 50                 }
 51             }
 52 
 53             //添加代理类的静态字段初始化方法
 54             this.methods.add(this.generateStaticInitializer());
 55         } catch (IOException var10) {
 56             throw new InternalError("unexpected I/O Exception", var10);
 57         }
 58 
 59         if(this.methods.size() > '\uffff') {
 60             throw new IllegalArgumentException("method limit exceeded");
 61         } else if(this.fields.size() > '\uffff') {
 62             throw new IllegalArgumentException("field limit exceeded");
 63         } else {
 64             //3、写入最终的class文件
 65             //验证常量池中存在代理类的全限定名
 66             this.cp.getClass(dotToSlash(this.className));
 67             //验证常量池中存在代理类父类的全限定名
 68             this.cp.getClass("java/lang/reflect/Proxy");
 69             var1 = this.interfaces;
 70             var2 = var1.length;
 71 
 72             //验证常量池存在代理类接口的全限定名
 73             for(var3 = 0; var3 < var2; ++var3) {
 74                 var4 = var1[var3];
 75                 this.cp.getClass(dotToSlash(var4.getName()));
 76             }
 77 
 78             //接下来要开始写入文件了,设置常量池只读
 79             this.cp.setReadOnly();
 80             ByteArrayOutputStream var13 = new ByteArrayOutputStream();
 81             DataOutputStream var14 = new DataOutputStream(var13);
 82 
 83             try {
 84                 //1.写入魔数
 85                 var14.writeInt(-889275714);
 86                 //2.写入次版本号
 87                 var14.writeShort(0);
 88                 //3.写入主版本号
 89                 var14.writeShort(49);
 90                 //4.写入常量池
 91                 this.cp.write(var14);
 92                 //5.写入访问修饰符
 93                 var14.writeShort(this.accessFlags);
 94                 //6.写入类索引
 95                 var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
 96                 //7.写入父类索引, 生成的代理类都继承自Proxy
 97                 var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
 98                 //8.写入接口计数值
 99                 var14.writeShort(this.interfaces.length);
100                 
101                 Class[] var17 = this.interfaces;
102                 int var18 = var17.length;
103 
104                 //9.写入接口集合
105                 for(int var19 = 0; var19 < var18; ++var19) {
106                     Class var22 = var17[var19];
107                     var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
108                 }
109                 //10.写入字段计数值
110                 var14.writeShort(this.fields.size());
111                 var15 = this.fields.iterator();
112                 //11.写入字段集合 
113                 while(var15.hasNext()) {
114                     ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
115                     var20.write(var14);
116                 }
117                 //12.写入方法计数值
118                 var14.writeShort(this.methods.size());
119                 var15 = this.methods.iterator();
120                 //13.写入方法集合
121                 while(var15.hasNext()) {
122                     ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
123                     var21.write(var14);
124                 }
125                  //14.写入属性计数值, 代理类class文件没有属性所以为0
126                 var14.writeShort(0);
127                 //转换成二进制数组输出
128                 return var13.toByteArray();
129             } catch (IOException var9) {
130                 throw new InternalError("unexpected I/O Exception", var9);
131             }
132         }
133     }

动态代理的本质:通过类加载器获取类字节码,通过类实现的接口反射获得该类的属性,方法等,并生成新的字节码文件

6.代理类字节码文件分析 $Proxy0.class

  1 package com.sun.proxy;
  2 
  3 import com.doubibi.framework.util.proxy.HelloService;
  4 import java.lang.reflect.InvocationHandler;
  5 import java.lang.reflect.Method;
  6 import java.lang.reflect.Proxy;
  7 import java.lang.reflect.UndeclaredThrowableException;
  8 
  9 public final class $Proxy0 extends Proxy
 10   implements HelloService
 11 {
 12   //equals方法
 13   private static Method m1;
 14   //HelloService 的sayHello方法
 15   private static Method m3;
 16   //toString方法
 17   private static Method m2;
 18   //hashCode方法
 19   private static Method m0;
 20 
 21   //构造方法
 22   public $Proxy0(InvocationHandler paramInvocationHandler)
 23     throws 
 24   {
 25     super(paramInvocationHandler);
 26   }
 27 
 28   public final boolean equals(Object paramObject)
 29     throws 
 30   {
 31     try
 32     {
 33       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
 34     }
 35     catch (Error|RuntimeException localError)
 36     {
 37       throw localError;
 38     }
 39     catch (Throwable localThrowable)
 40     {
 41       throw new UndeclaredThrowableException(localThrowable);
 42     }
 43   }
 44 
 45   //调用了invocationHandler的invoke方法,invoke执行了HelloService 的sayHello方法
 46   public final void sayHello()
 47     throws 
 48   {
 49     try
 50     {
 51       this.h.invoke(this, m3, null);
 52       return;
 53     }
 54     catch (Error|RuntimeException localError)
 55     {
 56       throw localError;
 57     }
 58     catch (Throwable localThrowable)
 59     {
 60       throw new UndeclaredThrowableException(localThrowable);
 61     }
 62   }
 63 
 64   public final String toString()
 65     throws 
 66   {
 67     try
 68     {
 69       return (String)this.h.invoke(this, m2, null);
 70     }
 71     catch (Error|RuntimeException localError)
 72     {
 73       throw localError;
 74     }
 75     catch (Throwable localThrowable)
 76     {
 77       throw new UndeclaredThrowableException(localThrowable);
 78     }
 79   }
 80 
 81   public final int hashCode()
 82     throws 
 83   {
 84     try
 85     {
 86       return ((Integer)this.h.invoke(this, m0, null)).intValue();
 87     }
 88     catch (Error|RuntimeException localError)
 89     {
 90       throw localError;
 91     }
 92     catch (Throwable localThrowable)
 93     {
 94       throw new UndeclaredThrowableException(localThrowable);
 95     }
 96   }
 97 
 98   static
 99   {
100     try
101     {
102       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
103       m3 = Class.forName("com.doubibi.framework.util.proxy.HelloService").getMethod("sayHello", new Class[0]);
104       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
105       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
106       return;
107     }
108     catch (NoSuchMethodException localNoSuchMethodException)
109     {
110       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
111     }
112     catch (ClassNotFoundException localClassNotFoundException)
113     {
114       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
115     }
116   }
117 }

源码分析参考自

四、CGLib代理

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。

五、CGLib原理粗略解析

先看cglib包里有一个主要的包proxy包如下:
在这里插入图片描述
在该包中的Enhancer类和MethodInterceptor接口是整个包的核心所在!Enhancer就是“增强”的意思嘛!主要用于生成动态子类以启用方法拦截,什么意思?这样子讲!cglib类代理的基本思想就是对被代理类生成一个新的类(proxy),该类是继承自被代理类的,并对被代理类方法执行前后执行一些操作,这些操作的通常就是一些回调操作,可以是MethodInterceptor,LazyLoader,CallbackFilter,其中MethodInterceptor是最常用的。
所有被Enhancer关联的对象默认都是实现Factory接口的,该接口提供了一组可以设置对象回调类型的方法,你可以通过调用setUseFactory(false)取消此特性!
需要注意的是,cglib是无法代理final修饰的方法的,因为这是java语言规范决定的!
MethodInterceptor是一个提供环绕通知的通用回调接口!Aop中有这样的术语,那就是前置通知,后置通知,环绕通知,非常好理解,就是一个在方法执行前的通知,一个方法执行后的通知,另外一个就是方法执行前后都通知。
该接口只有一个intercept()方法:

 public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;

所有对被代理类方法的执行都会跳转到这个方法上面来,而原来的方法则通过反射得到的Method对象或者MethodProxy对象进行调用。

六、CGLib代理例子

  • 有一个没有实现任何接口的实体类
  • 实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
  • 然后在需要使用实体类的时候,通过CGLIB动态代理获取代理对象。

实体类:

public class MyEntity {
    public void method() {
        System.out.println("method");
    }
}

实现一个MethodInterceptor:

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyMethodInterceptor implements MethodInterceptor {
	@Override
	public Object intercept(Object obj, Method method, Object[] arg,
			MethodProxy proxy) throws Throwable {
		System.out.println("before:"+method.getName());
		Object object = proxy.invokeSuper(obj, arg);
		System.out.println("after:"+method.getName());
		return object;
	}
}

测试类:

public class Testcglib {
	public static void main(String[] args) {
//该设置用于输出cglib动态代理产生的类 //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\proxy");  
		Enhancer enhancer = new Enhancer();
		//继承被代理类
		enhancer.setSuperclass(MyEntity .class);
		//设置回调
		enhancer.setCallback(new MyMethodInterceptor());
		//生成代理类对象
		MyEntity myEntity= (MyEntity)enhancer.create();
		//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
		myEntity.method();
	}
}

详细的CGLib源码分析

六、JDK动态代理和CGLIB代理的区别

JDK动态代理模式只能代理接口,如果要代理类那么就不行了。而CGLIB则是代理类。因此,Spring AOP 会这样子来进行切换,因为Spring AOP 同时支持 CGLIB、ASPECTJ、JDK动态代理,当你的真实对象有实现接口时,Spring AOP会默认采用JDK动态代理,否则采用cglib代理。同时CGLIB也有其缺陷,那就是必须目标类必须是可以继承的,如果目标类不可继承,那么我们就无法使用CGLIB来增强该类,我们可以称JDK动态代理实现的AOP为面向接口的动态增强,将CGLIB实现的AOP称为面向子类的动态增强。

猜你喜欢

转载自blog.csdn.net/cristianoxm/article/details/106199974