我正在尝试使用如下代码为java.net.SocketImpl类创建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
我们没有“对getSocket的调用”,因此尚未拦截SocketImpl#getSocket()。此方法仅在访问级别上与SocketImpl#getLocalPort()不同-SocketImpl#getLocalPort()受保护,而SocketImpl#getSocket()是程序包私有的。我与其他程序包专用方法SocketImpl#getServerSocket()的行为相同。
我试图用用户类(根据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通过创建覆盖其超类的所有方法的子类来拦截方法。要覆盖包专用方法,必须在同一包中定义子类。
对于SocketImpl,这是不可能的,原因有两个。
1.仅允许引导类加载器在内部名称空间中定义包。
2.为了覆盖方法,运行时程序包也需要相等。但是,无法将类注入引导类加载器。
使用cglib,这是不可能的。但是,您可以查看Byte Buddy,它允许通过检测来创建引导类代理。