1. 代理
方法增强,原理都是生成增强的class类,加载字节码之后生成对应的代理类
1.1 jdk代理
在Java.lang.reflect包中(rt.jar)
1.1.1 实现方式
- 一个实现Invocation的处理类
- Proxy.newProxyInstance(classLoader,interfacesClasses,invocationHandler) 来获取代理类
1.1.2 代码
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 测试结果
1.1.4 原理
生成了对应的class对象,加载之后 被注入的就是代理对象,使用代理对象调用相关方法,就会执行到对应的handler类中的invoke方法中
此处重要步骤时生成了代理对象,那么生成的代理对象在哪呢?为啥代理对象执行相关方法就直接进入了对应的handler类中的invoke方法中呢,那么就需要看下生成的代理对象时什么样了
Proxy.newProxyInstance(classLoader,interfacesClasses,invocationHandler) 源码流程
此处ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);中还会调用ProxyGenerator中的generateClassFile方法来生成对应的class文件(详情可以自己查看,其实就是使用ProxyGenerator的各种生成方法,生成了一个Data输出流然后转换成二进制,这块是jdk自带的,转换成String就可以看到部分细节源码,自己试一试)
1.1.5 查看代理class
添加打印参数System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
之后在工作空间目录下看到相应class
为何是com.sun.proxy.$Proxy0呢?通过源码解析,默认就是com.sun.proxy.$Proxy0
其中比较难看到的是几个参数
supplier.get() 中的 supplier = factory = new Factory(key, parameter, subKey, valuesMap);
此处的 valuesMap = new ConcurrentHashMap<>(); valuesMap.putIfAbsent(subKey, factory)
valueFactory.apply(key, parameter); 此处的 valueFacotry是Proxy 类中就定义了
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());所以最后会调用到ProxyClassFactory的apply方法中
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 测试结果
1.2.4 原理
生成了对应的class对象,加载之后 被注入的就是代理对象,使用代理对象调用相关方法,就会执行到对应的interceptor类中的intercept方法中
此处重要步骤时生成了代理对象,那么生成的代理对象在哪呢?为啥代理对象执行相关方法就直接进入了对应的interceptor类中的intercept方法中呢,那么就需要看下生成的代理对象时什么样了。
1.2.4.1 源码debug流程
看源码还是要有点耐心的,多几次debug无所谓的啦
首先我们在 Enhancer enhancer = new Enhancer();初debug,debug点击进行下一步,对idea项目右键,同步磁盘,发现生成了一个文件
这个文件怎么生成的呢?我们只是知道我们在开头设置了一个参数System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\share\\simple");,点进去查看(下载好源码),发现其中有一个静态代码块,其中第一行就是 debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY); 那肯定是要创建目录的,所以我看再次查看哪个地方调用了debugLocation这个静态属性,发现真正使用的只有当前类的toByteArray方法,所以我们在此处进行到Enhancer enhancer = new Enhancer();时,在DebuggingClassWriter的toByteArray打个断点,发现果然进来了,然后我们查看下当前的堆栈信息,发现入口时在new Enhancer()时,有一个静态属性 private static final EnhancerKey KEY_FACTORY = (EnhancerKey)KeyFactory.create(EnhancerKey.class); 一直点进去直到AbstractClassGenerator的create方法,其中strategy.generate(this);继续更进transform(cw.toByteArray()) (当然了这个方法之上的 transform(cg).generateClass(cw); 是生成字节码的逻辑) 此处的toByteArray方法进入到DebuggingClassWriter中,然后内部判断是否有指定目录,有的话就生成文件。这几个文件都是这样的生成流程。
我们主要看的是携带代理方法的类是怎么生成的 BusinessImpl proxy = (BusinessImpl) enhancer.create(); 就这个生成了我们想看到代理类。
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 代理类分析
目前仅仅需要关注的是 BusinessImpl5527c5ca
在创建代理类之后呢,调用代理类中的testHandle方法就会调用自己自定义的CglibProxyTestInterceptor的intercept方法,这个还是比较方便的。(生成的方法在刚才我们看到的create流程中)