代理解析

418 阅读6分钟

1. 代理

方法增强,原理都是生成增强的class类,加载字节码之后生成对应的代理类

1.1 jdk代理

在Java.lang.reflect包中(rt.jar)

1.1.1 实现方式
  • 一个实现Invocation的处理类
  • Proxy.newProxyInstance(classLoader,interfacesClasses,invocationHandler) 来获取代理类
1.1.2 代码

image-20210713103051608

WrapperResponse

package com.ph.learn.java.proxy.jdk;

import lombok.Builder;
import lombok.Data;
import lombok.ToString;

import java.io.Serializable;

@Data
@Builder
@ToString
public class WrapperResponse implements Serializable {

    private static final long serialVersionUID = 1L;

    private String str1;

    private Long l;

}

IBusinessService

package com.ph.learn.java.proxy.jdk;

public interface IBusinessService {

    WrapperResponse testHandle(String str1, Long l);

}

BusinessImpl

package com.ph.learn.java.proxy.jdk;

public class BusinessImpl implements IBusinessService {
    @Override
    public WrapperResponse testHandle(String str1, Long l) {
        return WrapperResponse.builder()
                .str1(str1)
                .l(l)
                .build();
    }
}

JdkProxyTestHandler

package com.ph.learn.java.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyTestHandler implements InvocationHandler {

    private Object target;

    public JdkProxyTestHandler() {
        // 实例化bean
        target = new BusinessImpl();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("======开始增强=====");
        Object invoke = method.invoke(target, args);
        System.out.println("======增强完毕=====");
        return invoke;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

}

JdkProxyTestMain

package com.ph.learn.java.proxy.jdk;

public class JdkProxyTestMain {

    public static void main(String[] args) {
        // 1. 生成代理处理器对象
        JdkProxyTestHandler jdkProxyTestHandler = new JdkProxyTestHandler();
        // 2. 获取代理对象
        IBusinessService proxy = (IBusinessService) jdkProxyTestHandler.getProxy();
        // 3. 执行代理方法
        WrapperResponse wrapperResponse = proxy.testHandle("2", 1L);
        System.out.println(wrapperResponse);
    }

}
1.1.3 测试结果

image-20210713103347195

1.1.4 原理

生成了对应的class对象,加载之后 被注入的就是代理对象,使用代理对象调用相关方法,就会执行到对应的handler类中的invoke方法中

此处重要步骤时生成了代理对象,那么生成的代理对象在哪呢?为啥代理对象执行相关方法就直接进入了对应的handler类中的invoke方法中呢,那么就需要看下生成的代理对象时什么样了

Proxy.newProxyInstance(classLoader,interfacesClasses,invocationHandler) 源码流程

image-20210713175747179

此处ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);中还会调用ProxyGenerator中的generateClassFile方法来生成对应的class文件(详情可以自己查看,其实就是使用ProxyGenerator的各种生成方法,生成了一个Data输出流然后转换成二进制,这块是jdk自带的,转换成String就可以看到部分细节源码,自己试一试)

1.1.5 查看代理class

添加打印参数System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

image-20210713145041507

之后在工作空间目录下看到相应class

image-20210713145133754

为何是com.sun.proxy.$Proxy0呢?通过源码解析,默认就是com.sun.proxy.$Proxy0

其中比较难看到的是几个参数

  1. supplier.get() 中的 supplier = factory = new Factory(key, parameter, subKey, valuesMap);

  2. 此处的 valuesMap = new ConcurrentHashMap<>(); valuesMap.putIfAbsent(subKey, factory)

  3. valueFactory.apply(key, parameter); 此处的 valueFacotry是Proxy 类中就定义了

    1. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
          proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
      
  4. 所以最后会调用到ProxyClassFactory的apply方法中

image-20210713154718885

1.1.6 代理类分析
package com.sun.proxy;

import com.ph.learn.java.proxy.jdk.IBusinessService;
import com.ph.learn.java.proxy.jdk.WrapperResponse;
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 IBusinessService {
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;

    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 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);
        }
    }
 
    // 代理类调用的是此方法
    public final WrapperResponse testHandle(String var1, Long var2) throws  {
        try {
            // 最后调用的是对应的InvocationHandler调用相关的invoke方法
            // 从Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);  然后跟踪下代码可以知道此处的h 就是JdkProxyTestHandler
            return (WrapperResponse)super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    static {
        try {
            // 默认3个方法 equals、toString、hashCode
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("com.ph.learn.java.proxy.jdk.IBusinessService").getMethod("testHandle", Class.forName("java.lang.String"), Class.forName("java.lang.Long"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

1.2 cglib代理

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类

1.2.1 实现方式
  • 引入pom文件

    • <!-- https://mvnrepository.com/artifact/cglib/cglib -->
      <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>3.1</version>
      </dependency>
      
  • 实现MethodInterceptor接口,重写intercept方法

1.2.2 代码

CglibProxyTestInterceptor

package com.ph.learn.java.proxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyTestInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======开始增强=====");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("======增强完毕=====");
        return o1;
    }
}

CglibProxyTestMain

package com.ph.learn.java.proxy.cglib;

import com.ph.learn.java.proxy.bean.BusinessImpl;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTestMain {

    public static void main(String[] args) {
        // https://github.com/cglib/cglib/wiki/How-To
        // 设置打印class文件
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\share\\simple");
        // cglib代理对象
        Enhancer enhancer = new Enhancer();
        // 设置代理类
        enhancer.setSuperclass(BusinessImpl.class);
        // 设置拦截器 在cglib中称为回调
        enhancer.setCallback(new CglibProxyTestInterceptor());
        // 创建代理对象
        BusinessImpl proxy = (BusinessImpl) enhancer.create();
        // 调用方法
        System.out.println(proxy.testHandle("1", 1L));
    }

}
1.2.3 测试结果

image-20210713185442072

1.2.4 原理

生成了对应的class对象,加载之后 被注入的就是代理对象,使用代理对象调用相关方法,就会执行到对应的interceptor类中的intercept方法中

此处重要步骤时生成了代理对象,那么生成的代理对象在哪呢?为啥代理对象执行相关方法就直接进入了对应的interceptor类中的intercept方法中呢,那么就需要看下生成的代理对象时什么样了。

1.2.4.1 源码debug流程

看源码还是要有点耐心的,多几次debug无所谓的啦

首先我们在 Enhancer enhancer = new Enhancer();初debug,debug点击进行下一步,对idea项目右键,同步磁盘,发现生成了一个文件

image-20210714165834221

这个文件怎么生成的呢?我们只是知道我们在开头设置了一个参数System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\share\\simple");,点进去查看(下载好源码),发现其中有一个静态代码块,其中第一行就是 debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY); 那肯定是要创建目录的,所以我看再次查看哪个地方调用了debugLocation这个静态属性,发现真正使用的只有当前类的toByteArray方法,所以我们在此处进行到Enhancer enhancer = new Enhancer();时,在DebuggingClassWritertoByteArray打个断点,发现果然进来了,然后我们查看下当前的堆栈信息,发现入口时在new Enhancer()时,有一个静态属性 private static final EnhancerKey KEY_FACTORY = (EnhancerKey)KeyFactory.create(EnhancerKey.class); 一直点进去直到AbstractClassGeneratorcreate方法,其中strategy.generate(this);继续更进transform(cw.toByteArray()) (当然了这个方法之上的 transform(cg).generateClass(cw); 是生成字节码的逻辑) 此处的toByteArray方法进入到DebuggingClassWriter中,然后内部判断是否有指定目录,有的话就生成文件。这几个文件都是这样的生成流程。

我们主要看的是携带代理方法的类是怎么生成的 BusinessImpl proxy = (BusinessImpl) enhancer.create(); 就这个生成了我们想看到代理类。

image-20210715093116113

1.2.4.2 小插曲

cglib代理跟jdk代理的在使用上的差别就是不需要使用者去调用Proxy.newProxyInstance方法,对于使用者更加方便,而且是对类做代理的,不像jdk只能代理接口(可以自己试下,看下效果)最后也能生成对应的class代理类,但是这个生成的代理类没有实现IBusinessService接口,以致于程序上强转失败,导致不能继续进行下去了。知道大家都懒,我直接将这种方式生成的$Proxy0贴出来

package com.sun.proxy;

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 {
    private static Method m1;
    private static Method m2;
    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 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");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
1.2.5 查看代理class

主要代理类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.ph.learn.java.proxy.bean;

import com.ph.learn.java.proxy.jdk.WrapperResponse;
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 BusinessImpl$$EnhancerByCGLIB$$5527c5ca extends BusinessImpl implements Factory {
    // 省略
	// 太多了 想看完整的自己动手试下 很简单的
    public final WrapperResponse testHandle(String var1, Long var2) {
        // 此处的 CGLIB$CALLBACK_0就是 CglibProxyTestInterceptor 此处debug到main方法的 
        // System.out.println(proxy.testHandle("1", 1L));  点击proxy可以看到CALLBACK_0是CglibProxyTestInterceptor
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (WrapperResponse)var10000.intercept(this, CGLIB$testHandle$0$Method, new Object[]{var1, var2}, CGLIB$testHandle$0$Proxy) : super.testHandle(var1, var2);
    }
   // 省略
}
1.2.6 代理类分析

目前仅仅需要关注的是 BusinessImplEnhancerByCGLIBEnhancerByCGLIB5527c5ca

在创建代理类之后呢,调用代理类中的testHandle方法就会调用自己自定义的CglibProxyTestInterceptorintercept方法,这个还是比较方便的。(生成的方法在刚才我们看到的create流程中)