我能够挂接到并监视这些类的所有新实例。完整的代码可以在源存储库中看到。以下是该方法的概述:java.net.Socket
java.net.ServerSocket
当实例化 Socket 或 ServerSocket 时,其构造函数中的第一件事是实例化真正实现套接字功能的对象的调用。默认实现是 的一个实例,但可以通过 java.net.Socket
#setSocketImplFactory 和 java.net.ServerSocket#setSocketFactory 设置自定义 java.net.SocketImplFactory
来覆盖它。
setImpl()
java.net.SocksSocketImpl
java.net.SocketImpl
的所有实现都是包私有的,这有点复杂,但是稍微反射一下就不太难了:
private static SocketImpl newSocketImpl() {
try {
Class<?> defaultSocketImpl = Class.forName("java.net.SocksSocketImpl");
Constructor<?> constructor = defaultSocketImpl.getDeclaredConstructor();
constructor.setAccessible(true);
return (SocketImpl) constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
SocketImplFactory 实现用于监视创建的所有套接字,如下所示:
final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<SocketImpl>());
ServerSocket.setSocketFactory(new SocketImplFactory() {
public SocketImpl createSocketImpl() {
SocketImpl socket = newSocketImpl();
allSockets.add(socket);
return socket;
}
});
请注意,setSocketFactory/setSocketImplFactory 只能调用一次,因此您要么只需要一个测试来执行此操作(就像我一样),要么必须创建一个静态单例(yuck!)来持有该间谍。
那么问题来了,如何找出套接字是否关闭?Socket 和 ServerSocket 都有一个方法,但是它使用这些类的内部布尔值来跟踪它是否已关闭 - SocketImpl 实例没有一个简单的方法来检查它是否已关闭。(顺便说一句,Socket和ServerSocket都由SocketImpl支持 - 没有“ServerSocketImpl”。isClosed()
值得庆幸的是,SocketImpl对它所支持的Socket或ServerSocket有一个引用。上述方法调用 或 ,并且可以通过调用 或 来取回该引用。setImpl()
impl.setSocket(this)
impl.setServerSocket(this)
java.net.SocketImpl#getSocket
java.net.SocketImpl#getServerSocket
再一次,这些方法是包私有的,所以需要一点点的反思:
private static Socket getSocket(SocketImpl impl) {
try {
Method getSocket = SocketImpl.class.getDeclaredMethod("getSocket");
getSocket.setAccessible(true);
return (Socket) getSocket.invoke(impl);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static ServerSocket getServerSocket(SocketImpl impl) {
try {
Method getServerSocket = SocketImpl.class.getDeclaredMethod("getServerSocket");
getServerSocket.setAccessible(true);
return (ServerSocket) getServerSocket.invoke(impl);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
请注意,getSocket/getServerSocket 可能不会在 SocketImplFactory 内部调用,因为 Socket/ServerSocket 仅在 SocketImpl 从那里返回后才设置它们。
现在,有所有必要的基础设施来检查我们的测试,无论我们想要的关于Socket/ServerSocket:
for (SocketImpl impl : allSockets) {
assertIsClosed(getSocket(impl));
}
完整的源代码在这里。