Java Cglib动态代理原理源码分析

导读:本篇文章讲解 Java Cglib动态代理原理源码分析,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

环境:Java8


Cglib代理使用

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib");  
Enhancer enhancer = new Enhancer() ;
enhancer.setSuperclass(PersonDAOImpl.class) ;
enhancer.setCallback(new MethodInterceptor() {
  @Override
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    System.out.println("执行前...") ;
    Object result = proxy.invokeSuper(obj, args) ;
    System.out.println("执行后...") ;
    return result ;
  }
});
PersonDAOImpl dao = (PersonDAOImpl) enhancer.create() ;
dao.save(new Person()) ;

System.setProperty(
DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “E://cglib”);这行代码用来生成cglib生成的代理类,输出到指定目录

Java Cglib动态代理原理源码分析

 

反编译代理类

工具使用的是Luyten

由于代码量比较大,这里只贴出重要的代码

public class PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7 extends PersonDAOImpl 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$save$0$Method;
  private static final MethodProxy CGLIB$save$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];
    final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7");
    final Class<?> forName2;
    final Method[] methods = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$equals$1$Method = methods[0];
    CGLIB$equals$1$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
    CGLIB$toString$2$Method = methods[1];
    CGLIB$toString$2$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
    CGLIB$hashCode$3$Method = methods[2];
    CGLIB$hashCode$3$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()I", "hashCode", "CGLIB$hashCode$3");
    CGLIB$clone$4$Method = methods[3];
    CGLIB$clone$4$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    final Class<?> forName3;
    CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] { "save", "(Lcom/pack/anno/config/Person;)V" }, (forName3 = Class.forName("com.pack.dao.PersonDAOImpl")).getDeclaredMethods())[0];
    CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");
  }
    
  final void CGLIB$save$0(final Person person) {
    super.save(person);
  }
    
  public final void save(final Person person) {
    MethodInterceptor cglib$CALLBACK_2;
    MethodInterceptor cglib$CALLBACK_0;
    if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
      CGLIB$BIND_CALLBACKS(this);
      cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
    }
    if (cglib$CALLBACK_0 != null) {
      cglib$CALLBACK_2.intercept((Object)this, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Method, new Object[] { person }, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Proxy);
      return;
    }
    super.save(person);
  }
    
  public PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7() {
    CGLIB$BIND_CALLBACKS(this);
  }
    
  public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] array) {
    PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$THREAD_CALLBACKS.set(array);
  }
  private static final void CGLIB$BIND_CALLBACKS(final Object o) {
    final PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7 personDAOImpl$$EnhancerByCGLIB$$6f07c3f7 = (PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7)o;
    if (!personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$BOUND) {
      personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$BOUND = true;
      Object o2;
      if ((o2 = PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$STATIC_CALLBACKS) != null) {
        personDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
      }
    }
  }

  static {
    CGLIB$STATICHOOK1();
  }
}

生成的代理类继承了PersonDAOImpl;PersonDAOImpl中有几个方法就会生成对应的两个方法,一个方法是直接调用父类对象方法CGLIB$save$0,一个是save方法而这个save方法是通过Callback来调用

当在执行save方法的时候先判断CGLIB$CALLBACK_0是否为空,如果为空则会执行初始化方法

CGLIB$BIND_CALLBACKS(this);

在该方法中,会从CGLIB$THREAD_CALLBACKS对象(ThreadLocal)中获取回调方法(Callback),这个ThreadLocal对象是如何设置值的?

设置CGLIB$THREAD_CALLBACKS值

在执行enhancer.create()方法时会通过反射来调用代理类PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7的CGLIB$SET_THREAD_CALLBACKS方法

在创建代理类的时候时:

public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
  setThreadCallbacks(callbacks);
  try {
    // Explicit reference equality is added here just in case Arrays.equals does not have one
    if (primaryConstructorArgTypes == argumentTypes ||
        Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
      // If we have relevant Constructor instance at hand, just call it
      // This skips "get constructors" machinery
      return ReflectUtils.newInstance(primaryConstructor, arguments);
    }
    // Take a slow path if observing unexpected argument types
    return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
  } finally {
    // clear thread callbacks to allow them to be gc'd
    setThreadCallbacks(null);
  }

}

setThreadCallbacks(callbacks);这行代码就是用来执行代理类中的CGLIB$SET_THREAD_CALLBACKS方法。

private void setThreadCallbacks(Callback[] callbacks) {
  try {
    setThreadCallbacks.invoke(generatedClass, (Object) callbacks);
  } catch (IllegalAccessException e) {
    throw new CodeGenerationException(e);
  } catch (InvocationTargetException e) {
    throw new CodeGenerationException(e.getTargetException());
  }
}

Java Cglib动态代理原理源码分析

 

在这代理类中的MethodInterceptor 就设置完成,接下来会执行代理类中save方法的如下代码:

cglib$CALLBACK_2.intercept((Object)this, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Method, new Object[] { person }, PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7.CGLIB$save$0$Proxy);

MethodInterceptor中intercept方法的执行

intercept方法

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  System.out.println("执行前...") ;
  Object result = proxy.invokeSuper(obj, args) ;
  System.out.println("执行后...") ;
  return result ;
}

这里的MethodProxy proxy对象又是从哪里来的?这需要回到代理类中的静态代码段中的如下一行代码:

CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");

进入MethodProxy.create方法:

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
  MethodProxy proxy = new MethodProxy();
  proxy.sig1 = new Signature(name1, desc);
  proxy.sig2 = new Signature(name2, desc);
  proxy.createInfo = new CreateInfo(c1, c2);
  return proxy;
}

进入invokeSuper方法

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();
  }
}

init方法用来初始化一个fastClassInfo对象

private void init()
{
  if (fastClassInfo == null)
  {
    synchronized (initLock)
    {
      if (fastClassInfo == null)
      {
        CreateInfo ci = createInfo;

        FastClassInfo fci = new FastClassInfo();
        fci.f1 = helper(ci, ci.c1);
        fci.f2 = helper(ci, ci.c2);
        fci.i1 = fci.f1.getIndex(sig1);
        fci.i2 = fci.f2.getIndex(sig2);
        fastClassInfo = fci;
        createInfo = null;
      }
    }
  }
}
// FastClassInfo
private static class FastClassInfo {
  FastClass f1;
  FastClass f2;
  int i1;
  int i2;
}

f1为对应的是CreateInfo中的c1,f2对应的是CreateInfo中的c2;这里结合代理类中的静态代码段来看看分别执行的是谁

final Class<?> forName = Class.forName("com.pack.dao.PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7");
final Class<?> forName3;
CGLIB$save$0$Method = ReflectUtils.findMethods(new String[] { "save", "(Lcom/pack/anno/config/Person;)V" }, (forName3 = Class.forName("com.pack.dao.PersonDAOImpl")).getDeclaredMethods())[0];
CGLIB$save$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Lcom/pack/anno/config/Person;)V", "save", "CGLIB$save$0");

结合上面的代码已经很清楚了。

f1表示的目标类的快速访问类,f2表示的代理类的快速访问类;i1表示的是目标类方法对应的索引,i2表示的是代理类方法对应的索引。

快速访问类是继承了FastClass类,其目的就是能快速地调用执行,因为通过反射性能不高。

原理就是通过每个方法的签名信息(方法名,参数)来为每个方法生成一个索引值,然后调用方法的时候就根据这个索引值直接调用目标方法。

目标类的快速访问类

Java Cglib动态代理原理源码分析

 

继续回到invokeSuper方法执行

FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);

此时fci对象完整信息如下:

Java Cglib动态代理原理源码分析

 

fci.f2.invoke(fci.i2, obj, args);这行代码才是真正执行目标类的代码,注意这里的i2=19

进入到PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7$$FastClassByCGLIB$$8343b7a5.class中查看invoke方法

Java Cglib动态代理原理源码分析

 

PersonDAOImpl$$EnhancerByCGLIB$$6f07c3f7类中的CGLIB$save$0方法

Java Cglib动态代理原理源码分析

 

直接调用父类PersonDAOImpl中的save方法。

到这里也就明白cglib生成的3个class。

Java Cglib动态代理原理源码分析

 

完毕!!!

给个关注+转发呗谢谢

公众:Springboot实战案例锦集

Spring Cloud链路追踪zipkin及整合Elasticsearch存储

Springboot整合MyBatis参数传值方式

Springboot整合MyBatis复杂查询应用

SpringCloud Nacos 服务消费者

Springboot接口幂等性基于token实现方案

Springboot项目使用docker部署

Springboot中接口参数校验N种方法你会几个?

SpringBoot一个提升N倍性能的操作

SpringBoot整合Quartz实现任务调度

Springboot之Actuator详解

SpringCloud zuul 动态网关配置

SpringMVC内嵌Tomcat零配置

SpringMVC请求原理源码分析

SpringMVC自定义注解实现接口调用

Springboot整合ELK日志收集详解步骤

SpringBoot开发中几个实用小技巧

Java Cglib动态代理原理源码分析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/80003.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!