【探索Spring底层】手撕jdk代理原理与cglib代理原理
1. 前言回顾
Spring的AOP实现有两个方式:
- jdk代理
- cglib代理
两者不同之处在于
- jdk要求实现接口,cglib不要求目标实现接口
- jdk代理与目标之间是平级的兄弟关系,cglib代理与目标之间是父子关系
2. 手撕jdk代理
接口
public interface Foo {
void foo();
int bar();
}
目标
public class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
@Override
public int bar() {
System.out.println("target bar");
return 100;
}
}
自定义InvocationHandler接口,里面有三个参数
- proxy是指代理对象
- Method是指方法
- Object是指方法执行需要的参数
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
手撕开始!!!
首先新建一个类$Proxy0实现Foo接口
在$Proxy0定义两个方法,通过反射获取到Foo类中的需要增强的两个方法foo和bar
同时还需要在类中定义一个成员变量InvocationHandler
之所以出现这个InvocationHandler(函数式接口),是因为aop增强的内容各不相同,如果写死在代理类中,那么就不能实现动态代理了(也就是说不这样做的话,一个代理类就只能实现一种增强方式,如果需要一百种不同的aop增强,那就需要一百种代理类),所以需要这个函数式接口,定义不同的增强内容
这个类可不简单,是实现动态代理的核心
然后调用InvocationHandler的invoke方法,将代理对象、需要增强的方法、方法的参数作为参数传递过去
public class $Proxy0 implements Foo {
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h1;
}
static Method foo;
static Method bar;
static {
try {
foo = A12.Foo.class.getMethod("foo");
bar = A12.Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
@Override
public void foo() {
try {
h.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public int bar() {
try {
Object result = h.invoke(this, bar, new Object[0]);
return (int) result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
编写测试代码测试
返回值之所以是Object是因为不是所有的方法返回值是void
为了避免返回值不是void 所以这里设计为Object
public static void main(String[] param) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
//方法反射调用
return method.invoke(new Target(), args);
}
});
proxy.foo();
proxy.bar();
}
注意:
method.invoke(" 要调用的方法的名字所隶属的对象实体",方法的参数值);
所以这里需要传递参数Target,因为需要增强的方法属于这个实体类
在完成增强之后的操作之后,就应该输入原来的实体类的对应方法的执行结果,并return
3. jdk反射优化
在前面调用目标的方法的时候是使用反射调用
反射调用效率性能要比正常调用要低,那么jdk代理对此有做什么优化吗?
// 运行时请添加 --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
/**
* 前16次是基于java本地api实现的,性能低
* 代理是在运行期间生成的代理类
*/
public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
for (int i = 1; i <= 17; i++) {
show(i, foo);
foo.invoke(null, i);
}
System.in.read();
}
// 方法反射调用时, 底层 MethodAccessor 的实现类
private static void show(int i, Method foo) throws Exception {
Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
getMethodAccessor.setAccessible(true);
Object invoke = getMethodAccessor.invoke(foo);
if (invoke == null) {
System.out.println(i + ":" + null);
return;
}
Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
delegate.setAccessible(true);
System.out.println(i + ":" + delegate.get(invoke));
}
public static void foo(int i) {
System.out.println(i + ":" + "foo");
}
}
由运行结果可以看出,前16次是通过NativeMethodAccessorImpl来反射调用方法的,效率比较低,这是基于java本地api实现的
但是从17次开始,就使用GeneratedMethodAccessor来调用方法了,效率相对较高
之所以效率相对较高,是因这里是直接调用方法,而不是反射调用
当然,完成这一系列的操作也是有代价的,这是借助代理来实现,代理是在运行期间生成的代理类
4. 手撕cglib代理
在了解了上面的jdk代理之后,想要理解cglib代理就会变得简单不少了
同样的通过一个案例来理解其底层实现
目标
public class Target {
public void save() {
System.out.println("save()");
}
public void save(int i) {
System.out.println("save(int)");
}
public void save(long j) {
System.out.println("save(long)");
}
}
代理类:
简单来说,这里的实现流程如下
首先如果增强的方法是使用反射调用的话
- 需要获取需要增强的目标对象的各种方法对象Method
- 代理对类继承目标类,并重写目标类需要增强的方法
- 使用MethodInterceptor类进行增强(其四个参数分别是指代理类、需要增强的方法对象、方法执行所需参数、方法的代理类)
- 最后在测试类中通过methodProxy.invoke(target, args)反射调用,在反射之前或者之后就可以进行增强,最后只需要将反射调用的结果retun即可
但是如果增强的方法是使用代理调用的话
- 不仅需要准备带增强功能的方法,并且还需要准备带原始功能的方法
- 对每个方法创建其代理对象(MethodProxy.create)
- 创建方法的代理对象的五个参数分别代表
- 需要增强的对象的class
- 代理对象的class
- 描述–》
- ()里面是指带增强方法的参数,如果括号里面什么也没有代表不需要参数;如果括号里面是I,代表参数为int类型;如果括号里面是J,代表是long类型
- 带增强功能的方法
- 带增强功能的方法对象的其带原始功能的方法
- 最后在测试类通过methodProxy.invokeSuper(p, args)代理调用方法
public class Proxy extends Target {
private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0;
static Method save1;
static Method save2;
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long j) {
super.save(j);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
测试类
public class A13 {
public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
// return method.invoke(target, args); // 反射调用
// FastClass
// return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
}
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}