使用 CGLib 拦截 JDK class 的包私有方法
Interception of package-private method for JDK class with CGLib
我正在尝试使用如下代码为 java.net.SocketImpl class 创建 CGLib 代理:
Enhancer e = new Enhancer();
e.setSuperclass(SocketImpl.class);
e.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object socketImplInstance, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
System.out.println("Got call to " + method.getName());
return methodProxy.invokeSuper(socketImplInstance, arguments);
}
});
SocketImpl socketImpl = (SocketImpl)e.create();
Method m = SocketImpl.class.getDeclaredMethod("getSocket");
m.setAccessible(true);
System.out.println("getSocket: " + m.invoke(socketImpl));
m = SocketImpl.class.getDeclaredMethod("getLocalPort");
m.setAccessible(true);
System.out.println("getLocalPort: " + m.invoke(socketImpl));
作为这段代码的结果,我得到了一个输出:
getSocket: null
Got call to getLocalPort
getLocalPort: 0
我们没有"Got call to getSocket",所以SocketImpl#getSocket()没有被拦截。此方法与 SocketImpl#getLocalPort() 的区别仅在于访问级别 - SocketImpl#getLocalPort() 是受保护的,而 SocketImpl#getSocket() 是包私有的。我与其他包私有方法 SocketImpl#getServerSocket() 有相同的行为。
我试图用用户 class 重现这个错误(根据 SocketImpl 是抽象的),但一切都按预期工作,如果我们有:
package userpackage;
public abstract class Abstract {
void testMethod() {}
}
package somethingother;
Enhancer e = new Enhancer();
e.setSuperclass(Abstract.class);
e.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object abstractInstance, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
System.out.println("Got call to " + method.getName());
return methodProxy.invokeSuper(abstractInstance, arguments);
}
});
Abstract abstrct = (Abstract)e.create();
Method m = Abstract.class.getDeclaredMethod("testMethod");
m.setAccessible(true);
System.out.println("testMethod: " + m.invoke(abstrct));
我们得到输出:
Got call to testMethod
testMethod: null
完全没问题,这个包私有方法已经被拦截了。
拜托,你能帮我理解这个例子中发生了什么,以及为什么我们有不同的行为。我只有一个猜测,它可能与 SecurityManager 有关,但在那种情况下,你能指出它不起作用的具体情况吗?
我使用 CGLib 3.1 和 3.2.0 进行测试。
Cglib 通过创建一个 subclass 来拦截方法,该 subclass 覆盖其 superclass 的所有方法。要覆盖包私有方法,subclass 必须在同一个包中定义。
这对 SocketImpl 来说是不可能的,原因有二。
1. 只允许 bootstrap class 加载程序在内部命名空间中定义包。
2.为了覆盖一个方法,运行时包也需要相等。但是,无法将 class 注入 bootstrap class 加载程序。
使用 cglib,这是不可能的。但是,您可以查看 Byte Buddy,它允许通过检测创建 bootstrap class 代理。
我正在尝试使用如下代码为 java.net.SocketImpl class 创建 CGLib 代理:
Enhancer e = new Enhancer();
e.setSuperclass(SocketImpl.class);
e.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object socketImplInstance, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
System.out.println("Got call to " + method.getName());
return methodProxy.invokeSuper(socketImplInstance, arguments);
}
});
SocketImpl socketImpl = (SocketImpl)e.create();
Method m = SocketImpl.class.getDeclaredMethod("getSocket");
m.setAccessible(true);
System.out.println("getSocket: " + m.invoke(socketImpl));
m = SocketImpl.class.getDeclaredMethod("getLocalPort");
m.setAccessible(true);
System.out.println("getLocalPort: " + m.invoke(socketImpl));
作为这段代码的结果,我得到了一个输出:
getSocket: null
Got call to getLocalPort
getLocalPort: 0
我们没有"Got call to getSocket",所以SocketImpl#getSocket()没有被拦截。此方法与 SocketImpl#getLocalPort() 的区别仅在于访问级别 - SocketImpl#getLocalPort() 是受保护的,而 SocketImpl#getSocket() 是包私有的。我与其他包私有方法 SocketImpl#getServerSocket() 有相同的行为。
我试图用用户 class 重现这个错误(根据 SocketImpl 是抽象的),但一切都按预期工作,如果我们有:
package userpackage;
public abstract class Abstract {
void testMethod() {}
}
package somethingother;
Enhancer e = new Enhancer();
e.setSuperclass(Abstract.class);
e.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object abstractInstance, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
System.out.println("Got call to " + method.getName());
return methodProxy.invokeSuper(abstractInstance, arguments);
}
});
Abstract abstrct = (Abstract)e.create();
Method m = Abstract.class.getDeclaredMethod("testMethod");
m.setAccessible(true);
System.out.println("testMethod: " + m.invoke(abstrct));
我们得到输出:
Got call to testMethod
testMethod: null
完全没问题,这个包私有方法已经被拦截了。
拜托,你能帮我理解这个例子中发生了什么,以及为什么我们有不同的行为。我只有一个猜测,它可能与 SecurityManager 有关,但在那种情况下,你能指出它不起作用的具体情况吗?
我使用 CGLib 3.1 和 3.2.0 进行测试。
Cglib 通过创建一个 subclass 来拦截方法,该 subclass 覆盖其 superclass 的所有方法。要覆盖包私有方法,subclass 必须在同一个包中定义。
这对 SocketImpl 来说是不可能的,原因有二。 1. 只允许 bootstrap class 加载程序在内部命名空间中定义包。 2.为了覆盖一个方法,运行时包也需要相等。但是,无法将 class 注入 bootstrap class 加载程序。
使用 cglib,这是不可能的。但是,您可以查看 Byte Buddy,它允许通过检测创建 bootstrap class 代理。