我尝试使用以下代码为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()是包私有的。我与其他package-private方法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进行测试。
答案 0 :(得分:2)
Cglib通过创建一个覆盖其超类的所有方法的子类来拦截方法。对于重写的package-private方法,必须在同一个包中定义子类。
SocketImpl不可能出于两个原因。 1.只允许引导类加载器在内部名称空间中定义包。 2.为了覆盖方法,运行时包也需要相等。但是,不可能将类注入引导类加载器。
使用cglib,这是不可能的。但是,您可以查看Byte Buddy,它允许通过检测创建引导类代理。