[Java] 模拟Jdk 以及 CGLib 代理原理
文章目录
- JDK
- arthas 反编译jdk代理对象
- arthas 反编译的结果是:
- CGlib
- methodProxy 不经过反射调用方法的原理
- MethodProxy原理
- 模拟 结合目标对象使用
- 模拟结合 代理对象使用
JDK
Jdk代理的最简单模拟, 由前文可知 JDK动态代理需要实现接口,所以基于此,进行最简单的模拟。
package com.example.proxy;
public class Jdk {
interface Foo {
void foo();
}
static class Target implements Foo {
@Override
public void foo() {
System.out.println("foo");
}
}
// 代理类
static class $Proxy0 implements Foo {
@Override
public void foo() {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
new Target().foo();
}
}
public static void main(String[] args) {
Foo f = new $Proxy0();
f.foo();
}
}
虽然简单实现了代理,但是目前增强是固定的,但是在实际应用中,使用到代理类,方法是不可能固定的,所以接下来进行优化一下。使用抽象类+模版方法设置代理的执行逻辑。
package com.example.proxy;
public class Jdk {
interface Foo {
void foo();
}
static abstract class InvokeHandler {
abstract Object invoke();
}
// 代理类
static class $Proxy0 implements Foo {
private final InvokeHandler invokeHandler;
$Proxy0(InvokeHandler invokeHandler) {
this.invokeHandler = invokeHandler;
}
@Override
public void foo() {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
invokeHandler.invoke();
}
}
public static void main(String[] args) {
Foo f = new $Proxy0(new InvokeHandler() {
@Override
Object invoke() {
System.out.println(">>>>>>>> foo");
return null;
}
});
f.foo();
}
}
至此,方法就是可以不再固定。但是很显然,代理的对象不可能永远只有一个方法,所以想办法动态设置。
package com.example.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Jdk {
interface Foo {
void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}
static abstract class InvokeHandler {
abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException;
}
// 代理类
static class $Proxy0 implements Foo {
private final InvokeHandler invokeHandler;
$Proxy0(InvokeHandler invokeHandler) {
this.invokeHandler = invokeHandler;
}
@Override
public void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);
}
@Override
public void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
invokeHandler.invoke(Foo.class.getMethod("bar"), new Object[0]);
}
}
static class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo");
}
@Override
public void bar() {
System.out.println("target bar");
}
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Foo f = new $Proxy0(new InvokeHandler() {
@Override
Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException {
// 传入代理对象
method.invoke(new Target(), params);
return null;
}
});
f.foo();
f.bar();
}
}
/**
运行结果
before
target foo
before
target bar
**/
到这里,可以发现,多方法的代理对象也可以正常执行。但是如果执行方法有值返回呢,这个也简单,小修改一波。
package com.example.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Jdk {
interface Foo {
Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}
static abstract class InvokeHandler {
abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException;
}
// 代理类
static class $Proxy0 implements Foo {
private final InvokeHandler invokeHandler;
$Proxy0(InvokeHandler invokeHandler) {
this.invokeHandler = invokeHandler;
}
@Override
public Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
return invokeHandler.invoke(Foo.class.getMethod("foo"), new Object[0]);
}
@Override
public Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
return invokeHandler.invoke(Foo.class.getMethod("bar"), new Object[0]);
}
}
static class Target implements Foo {
@Override
public Integer foo() {
System.out.println("target foo");
return 1;
}
@Override
public String bar() {
System.out.println("target bar");
return "hello";
}
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Foo f = new $Proxy0(new InvokeHandler() {
@Override
Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException {
// 传入代理对象
return method.invoke(new Target(), params);
}
});
System.out.println(f.foo());
System.out.println(f.bar());
}
}
/**
运行结果
before
target foo
1
before
target bar
hello
**/
在源码实现中,方法还可以被缓存复用,不需要每次都重新创建。
package com.example.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class Jdk {
interface Foo {
Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}
static abstract class InvokeHandler {
abstract Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException;
}
// 代理类
static class $Proxy0 implements Foo {
private final InvokeHandler invokeHandler;
private final Map<String, Method> cache = new HashMap<>();
$Proxy0(InvokeHandler invokeHandler) {
this.invokeHandler = invokeHandler;
}
@Override
public Object foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
Method foo = cache.getOrDefault("foo", null);
if(foo == null) {
foo = Foo.class.getMethod("foo");
System.out.println(">>>>>> 新创建方法");
cache.put("foo", foo);
}
return invokeHandler.invoke(foo, new Object[0]);
}
@Override
public Object bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before");
// 2. 调用目标
Method bar = cache.getOrDefault("bar", null);
if(bar == null) {
bar = Foo.class.getMethod("foo");
System.out.println(">>>>>> 新创建方法");
cache.put("bar", bar);
}
return invokeHandler.invoke(bar, new Object[0]);
}
}
static class Target implements Foo {
@Override
public Integer foo() {
System.out.println("target foo");
return 1;
}
@Override
public String bar() {
System.out.println("target bar");
return "hello";
}
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Foo f = new $Proxy0(new InvokeHandler() {
@Override
Object invoke(Method method, Object[] params) throws InvocationTargetException, IllegalAccessException {
// 传入代理对象
return method.invoke(new Target(), params);
}
});
System.out.println(f.foo());
System.out.println(f.bar());
System.out.println(f.foo());
System.out.println(f.bar());
}
}
/**
before
>>>>>> 新创建方法
target foo
1
before
>>>>>> 新创建方法
target foo
1
before
target foo
1
before
target foo
1
**/
到此,代理方法只会被寻找一次。
JDK 动态代理生成的代理类是以字节码的形式存在的,并不存在所谓的 .java 文件,但也不是说就没办法看到生成的代理类信息了。不过可
以使用 arthas反编译,看到字节码。
arthas 反编译jdk代理对象
比如:
package com.example.proxy;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class Jdk1 {
interface Foo {
void foo();
}
static final class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] args) throws IOException {
// 原始对象
Target target = new Target();
// 用来加载在运行期间动态生成的字节码
ClassLoader classLoader = Jdk1.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, (p, method, params) -> {
System.out.println("before...");
// 目标.方法(参数) --> 方法.invoke(目标, 参数)
Object result = method.invoke(target, params);
System.out.println("after...");
// 也返回目标方法执行的结果
return result;
});
// 打印代理类的全限定类名
System.out.println(proxy.getClass());
proxy.foo();
// 只要不在控制台上输入并回车,程序就不会终端
System.in.read();
}
}
打印的结果是:
class com.example.proxy.$Proxy0
before...
target foo
after...
arthas 反编译的结果是:
[arthas@60054]$ jad com.example.proxy.$Proxy0
ClassLoader:
±jdk.internal.loader.ClassLoaders
A
p
p
C
l
a
s
s
L
o
a
d
e
r
@
251
a
69
d
7
+
−
j
d
k
.
i
n
t
e
r
n
a
l
.
l
o
a
d
e
r
.
C
l
a
s
s
L
o
a
d
e
r
s
AppClassLoader@251a69d7 +-jdk.internal.loader.ClassLoaders
AppClassLoader@251a69d7+−jdk.internal.loader.ClassLoadersPlatformClassLoader@17747fbe
Location:
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.example.proxy.Jdk1$Foo
*/
package com.example.proxy;
import com.example.proxy.Jdk1;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0
extends Proxy
implements Jdk1.Foo {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException {
if (lookup.lookupClass() == Proxy.class && lookup.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
}
throw new IllegalAccessException(lookup.toString());
}
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.example.proxy.Jdk1$Foo").getMethod("foo", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final void foo() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
CGlib
cglib 代理类继承目标对象。
public class Target {
public void save() {
System.out.println("0");
}
public void save(int i) {
System.out.println(i);
}
public void save(long l) {
System.out.println(l);
}
}
//- cglib 代理模拟
public class Proxy extends Target{
private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
private static final Method save0;
private static final Method save1;
private static final Method save2;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], null);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, null);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public void save(long l) {
try {
methodInterceptor.intercept(this, save2, new Object[]{l}, null);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
//- 测试类
public class ProxyTest {
public static void main(String[] args) {
Target target = new Target();
Proxy proxy = new Proxy();
proxy.setMethodInterceptor((obj, method, args1, proxy1) -> {
System.out.println("before----");
return method.invoke(target, args1);
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}
methodProxy 不经过反射调用方法的原理
在在上述 Proxy 类中,重写了父类中的方法,并在重写的方法中调用了 intercept() 方法,重写的这些方法相当于是带增强功能的方法。
在 JDK 的动态代理中,使用反射对方法进行调用,而在 CGLib 动态代理中,可以使用 intercept() 方法中 MethodProxy 类型的参数实现不经过反射来调用方法。
接收的 MethodProxy 类型的参数可以像 Method 类型的参数一样,在静态代码块中被实例化。
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 i) {
super.save(i);
}
// >>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@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 i) {
try {
methodInterceptor.intercept(this, save2, new Object[]{i}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
//- 测试类
public class ProxyTest {
public static void main(String[] args) {
Target target = new Target();
Proxy proxy = new Proxy();
proxy.setMethodInterceptor((obj, method, args1, methodProxy) -> {
System.out.println("before----");
// return method.invoke(target, args1);
// return methodProxy.invoke(target, args1); // 内部无反射调用 结合目标对象使用
return methodProxy.invokeSuper(obj, args1); // 内部无反射调用, 结合代理对象使用
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}
MethodProxy原理
其内部是通过一个 FastClass+ 方法签名实现
模拟 结合目标对象使用
Target target = new Target();
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, args, methodProxy) -> {
System.out.println("before...");
// 内部没使用反射,需要目标(spring 的选择)
Object result = methodProxy.invoke(target, args);
System.out.println("after...");
return result;
});
package com.example.proxy;
import org.springframework.cglib.core.Signature;
public class TargetFastClass {
static Signature s0 = new Signature("save", "()V");
static Signature s1 = new Signature("save", "(I)V");
static Signature s2 = new Signature("save", "(J)V");
/**
* <p>获取目标方法的编号</p>
* <p>
* Target 目标类中的方法:
* save() 0
* save(int) 1
* save(long) 2
* </p>
*
* @param signature 包含方法名称、参数返回值
* @return 方法编号
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
}
if (s1.equals(signature)) {
return 1;
}
if (s2.equals(signature)) {
return 2;
}
return -1;
}
/**
* 根据 getIndex() 方法返回的方法编号正常调用目标对象方法
*
* @param index 方法编号
* @param target 目标对象
* @param args 调用目标对象方法需要的参数
* @return 方法返回结果
*/
public Object invoke(int index, Object target, Object[] args) {
if (index == 0) {
((Target) target).save();
return null;
}
if (index == 1) {
((Target) target).save((int) args[0]);
return null;
}
if (index == 2) {
((Target) target).save((long) args[0]);
return null;
}
throw new RuntimeException("无此方法");
}
public static void main(String[] args) {
TargetFastClass fastClass = new TargetFastClass();
int index = fastClass.getIndex(new Signature("save", "()V"));
fastClass.invoke(index, new Target(), new Object[0]);
index = fastClass.getIndex(new Signature("save", "(J)V"));
fastClass.invoke(index, new Target(), new Object[]{2L});
}
}
模拟结合 代理对象使用
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (obj, method, args, methodProxy) -> {
System.out.println("before...");
// 内部没使用反射,需要代理
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("after...");
return result;
});
package com.example.proxy;
import org.springframework.cglib.core.Signature;
public class ProxyFastClass {
static Signature s0 = new Signature("saveSuper", "()V");
static Signature s1 = new Signature("saveSuper", "(I)V");
static Signature s2 = new Signature("saveSuper", "(J)V");
/**
* <p>获取代理方法的编号</p>
* <p>
* Proxy 代理类中的方法:
* saveSuper() 0
* saveSuper(int) 1
* saveSuper(long) 2
* </p>
*
* @param signature 包含方法名称、参数返回值
* @return 方法编号
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
}
if (s1.equals(signature)) {
return 1;
}
if (s2.equals(signature)) {
return 2;
}
return -1;
}
/**
* 根据 getIndex() 方法返回的方法编号正常调用代理对象中带原始功能的方法
*
* @param index 方法编号
* @param proxy 代理对象
* @param args 调用方法需要的参数
* @return 方法返回结果
*/
public Object invoke(int index, Object proxy, Object[] args) {
if (index == 0) {
((Proxy) proxy).saveSuper();
return null;
}
if (index == 1) {
((Proxy) proxy).saveSuper((int) args[0]);
return null;
}
if (index == 2) {
((Proxy) proxy).saveSuper((long) args[0]);
return null;
}
throw new RuntimeException("无此方法");
}
public static void main(String[] args) {
ProxyFastClass fastClass = new ProxyFastClass();
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
fastClass.invoke(index, new Proxy(), new Object[0]);
int index1 = fastClass.getIndex(new Signature("saveSuper", "(J)V"));
fastClass.invoke(index1, new Proxy(), new Object[]{2L});
}
}