写在前面
未经允许不得转载!
- 什么是代理?
- (我的理解)在编程语言中,代理就是由于某些原因控制某个对象/方法的访问
- 为什么要有静态代理?
- 开闭原则,需求总是变化无常的,在不修改原有逻辑的情况下,对其进行拓展
- 为什么要有动态代理?
- 用静态代理的原因,动态代理也有
- 方便维护,从静态代理的维护实现类到现在只需要维护拓展逻辑
这篇文章有好多隐藏的知识点,用心看用心挖掘,最好自己写一遍代码,走一遍完整的逻辑。
静态代理
静态代理感觉没啥好说的,实现方式可以是组合,也可以是继承,当然是推荐用组合的方式实现静态代理,理由是解耦,可以任意组装需要的组合,比如说你要对原逻辑代理3次(我的案例可以拆成两个代理,一个校验参数,一个打印信息),进行不同的业务逻辑,且执行的顺序可能会变化。
代码看附录,需要用到的类:
Calculable
,Calculator
,CalculatorStaticProxy
,CalculatorStaticProxyTest
。
动态代理
动态代理相对于静态代理来说,目标是一致的,就是控制目标方法的访问,但是代理类是动态生成的。
重点来了:动态生成代理类
,生成方式也有好多种,比如说:生成新的一个代理类,将原类改造成代理类。
JDK 动态代理
(这里只说果,了解因看下面的原理解析),还有排除
java.lang.Object
(本来就是所有实现类的 super class)
这段很重要,结论:
代理类一定是java.lang.reflect.Proxy
的子类(所以 JDK 动态代理只能代理有interface
的类及其方法),并且implements
了指定类的所有interface
,并且一定有且只有一个带java.lang.reflect.InvocationHandler
参数的Constructor
方法,实例化对象的时候一定会将 InvocationHandler 带入。
还有个很重要的点: 为什么创建代理对象的时候,要指定 ClassLoder?
我觉得有两个原因:
* 可以实现安全机制,注释上有说明
* 跟随目标类进行同步热加载类
然后,当你调用某个
interface
的方法时,最终会转发给上面构造器带入的那个对象的invoke
方法,搞定,收工。
代码看附录,需要用到的类:
Calculable
,Calculator
,CalculatorJdkDynamicProxyHandler
,CalculatorJdkDynamicProxyTest
,$CalculatorJdkDynamicProxy
(这个类就是生成的代理类)。
基于JDK 8u201
原理解析
想要动态生成对象的前提是:要有这个对象的 Class 对象。
想要有这个对象的 Class 对象的前提是:要有这个对象的 class (也就是字节码),然后通过 ClassLoader 加载到方法区的。
所以问题就在怎么动态生成字节码,并且怎么加载到方法区的。
那么来看看JDK 动态代理怎么实现的
解析基于
JDK 8u201
先来看看代理类的结构:
- 第1部分:定义 class 的那段
- 第2部分:定义 fields 的那段
- 第3部分:定义 constructor methods 的那段
- 第4部分:定义 methods 的那段
然后来看看代理类的字节码是怎么生成的(只说关键部分):
- 调用顺序:
java.lang.reflect.Proxy#newProxyInstance
java.lang.reflect.Proxy#getProxyClass0
proxyClassCache.get
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
java.lang.reflect.WeakCache#get
(不重要)subKeyFactory.apply(key, parameter)
(不重要)java.lang.reflect.Proxy.KeyFactory#apply
factory = new Factory(key, parameter, subKey, valuesMap);
supplier = factory;
supplier.get()
java.lang.reflect.WeakCache.Factory#get
valueFactory.apply
java.lang.reflect.Proxy.ProxyClassFactory#apply
走到这里开始最重要的那块了 !!!- class name 怎么生成的,怎么拿到全部的
interface
自己看- 先来看下怎么加载动态生成的字节码的
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
- 不是通过双亲委派的形式加载的,但是这个动态生成的类加载器是
AppClassLoader
- 再来看怎么生成字节码的
sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
- 如果要保存生成的代理类,查看
saveGeneratedFiles
这个字段- 再来看生成字节码的逻辑
sun.misc.ProxyGenerator#generateClassFile
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
- 进入
sun.misc.ProxyGenerator#generateClassFile
方法后,第一次看建议反着看,从结果往前推进- 从结果那可以看出,这字节码是 jdk 自己生成的
- 然后跟着上面的代理类的结构一步步看上去
- 解密上面的一些结论:
- 有且只有一个带
java.lang.reflect.InvocationHandler
参数的Constructor
方法
this.methods.add(this.generateConstructor());
- 为什么父类是Proxy
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
- 下面附上了 ClassFile Structure,助你一臂之力,是不是豁然开朗了 ^^
- 从
var14.writeInt(-889275714);
对应的是 magic: cafebabe- 一步步对过来
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
知识补充
- 除了 ClassLoder ,还可以用
sun.misc.Unsafe#defineClass
来把加载类信息
附录
public interface Calculable {
Integer divide(int a, int b);
}
public class Calculator implements Calculable {
@Override
public Integer divide(int a, int b) {
return a / b;
}
}
public class CalculatorStaticProxy implements Calculable {
private Calculable target;
public CalculatorStaticProxy(Calculable target) {
this.target = target;
}
@Override
public Integer divide(int a, int b) {
Integer result = null;
boolean isPass = beforeDivideProxy(a, b);
if (isPass) {
result = target.divide(a, b);
afterDivideProxy(a, b);
}
return result;
}
private boolean beforeDivideProxy(int a, int b) {
System.out.printf("before exec %d / %d\n", a, b);
if (b == 0) {
return false;
}
return true;
}
private void afterDivideProxy(int a, int b) {
System.out.printf("after divide method\n");
}
}
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class CalculatorStaticProxyTest {
@Test
public void testDivide_normal() {
Calculable calc = new CalculatorStaticProxy(new Calculator());
assertEquals(Integer.valueOf(2), calc.divide(6, 3));
}
@Test
public void testDivide_exception() {
Calculable calc = new CalculatorStaticProxy(new Calculator());
assertNull(calc.divide(6, 0));
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class CalculatorJdkDynamicProxyHandler implements InvocationHandler {
private Calculable target;
public CalculatorJdkDynamicProxyHandler(Calculable target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ("divide".equals(methodName)) {
return handleDivideMethod(method, args);
}
return method.invoke(target, args);
}
private Integer handleDivideMethod(Method method, Object[] args)
throws InvocationTargetException, IllegalAccessException
{
Integer result = null;
boolean isPass = beforeDivideProxy(method, args);
if (isPass) {
result = (Integer) method.invoke(target, args);
afterDivideProxy(method, args);
}
return result;
}
private boolean beforeDivideProxy(Method method, Object[] args) {
System.out.printf("before exec divide method\n", args[0], args[1]);
if ((Integer) args[1] == 0) {
return false;
}
return true;
}
private void afterDivideProxy(Method method, Object[] args) {
System.out.printf("after divide method\n");
}
}
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class CalculatorJdkDynamicProxyTest {
private Calculable proxyCalc = null;
// public static void main(String[] args) {
// // 保存生成的代理类的字节码文件: jdk8 下的配置,默认生成的路径是项目根目录
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//
// sun.misc.ProxyGenerator.generateProxyClass(
// "$CalculatorJdkDynamicProxy",
// Calculator.class.getInterfaces(),
// java.lang.reflect.Modifier.PUBLIC);
// }
@Before
public void beforeTest() {
Calculable calc = new Calculator();
ClassLoader loader = calc.getClass().getClassLoader();
Class<?>[] interfaces = calc.getClass().getInterfaces();
InvocationHandler h = new CalculatorJdkDynamicProxyHandler(calc);
proxyCalc = (Calculable) Proxy.newProxyInstance(loader, interfaces, h);
System.out.println(proxyCalc.getClass().getName());
System.out.println(proxyCalc.getClass().getClassLoader());
}
@Test
public void testDivide_normal() {
assertEquals(Integer.valueOf(2), proxyCalc.divide(6, 3));
}
@Test
public void testDivide_exception() {
Calculable calc = new CalculatorStaticProxy(new Calculator());
assertNull(calc.divide(6, 0));
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public class $CalculatorJdkDynamicProxy extends Proxy implements Calculable {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $CalculatorJdkDynamicProxy(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 Integer divide(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
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("Calculable").getMethod("divide", Integer.TYPE, Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}