我尝试研究这个问题,因为这似乎是一个有趣的案例。我把我的发现发布在这里,尽管我觉得我可能还是误解了一些东西,或者过于简化了一些内容。实际上,也有可能我完全误解了你的情况,这个答案是毫无用处的(如果是这样,我很抱歉)。
我所组织的内容基于两个概念:
- 应用程序服务器全局状态(我使用 System.props,但这可能不是最好的选择 - 也许一些临时文件会更好)
- 容器特定的全局状态(这意味着由容器特定 ClassLoader 加载的所有类)
我建议一个 EmbeddedEngineHandler.loadEmbeddedEngineIfNeeded 方法,在以下情况下调用它:
- 在您的驱动程序注册期间
- 在您的 javax.sql.DataSource 实现静态初始化程序中(如果整个 DataSource 相关的事情都是这样进行的 - 我对此知之甚少)
如果我理解正确,您将不需要调用 Runtime.removeShutdownHook。
我在这里最不确定的主要事情是 - 如果驱动程序是全局部署的,那么它是否会在任何 Servlet 初始化之前进行注册?如果不是,那么我就错了,这不起作用。但也许检查 EmbeddedEngineHandler 的 ClassLoader 可以帮助解决这个问题?
这是 EmbeddedEngineHandler:
final class EmbeddedEngineHandler {
private static final String PREFIX = "";
private static final String IS_SERVLET_CONTEXT = PREFIX + "-is-servlet-context";
private static final String GLOBAL_ENGINE_LOADED = PREFIX + "-global-engine-loaded";
private static final String TRUE = "true";
private static volatile boolean localEngineLoaded = false;
static void loadEmbeddedEngineIfNeeded() {
if (isServletContext()) {
loadEmbeddedEngineInLocalContextIfNeeded();
} else {
loadEmbeddedEngineInGlobalContextIfNeeded();
}
}
private static void loadEmbeddedEngineInLocalContextIfNeeded() {
if (!isGlobalEngineLoaded() && !isLocalEngineLoaded()) {
loadEmbeddedEngine();
markLocalEngineAsLoaded();
}
}
private static void loadEmbeddedEngineInGlobalContextIfNeeded() {
if (!isGlobalEngineLoaded()) {
loadEmbeddedEngine();
markGlobalEngineAsLoaded();
Runtime.getRuntime().addShutdownHook(new Thread(EmbeddedEngineHandler::unloadEmbeddedEngine));
}
}
private static void loadEmbeddedEngine() {
}
static void unloadEmbeddedEngine() {
}
private static boolean isServletContext() {
return TRUE.equals(System.getProperty(IS_SERVLET_CONTEXT));
}
static void markAsServletContext() {
System.setProperty(IS_SERVLET_CONTEXT, TRUE);
}
private static boolean isGlobalEngineLoaded() {
return TRUE.equals(System.getProperty(GLOBAL_ENGINE_LOADED));
}
private static void markGlobalEngineAsLoaded() {
System.setProperty(GLOBAL_ENGINE_LOADED, TRUE);
}
static boolean isLocalEngineLoaded() {
return localEngineLoaded;
}
private static void markLocalEngineAsLoaded() {
localEngineLoaded = true;
}
}
这是ServletContextListener
:
@WebListener
final class YourServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
EmbeddedEngineHandler.markAsServletContext();
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
if (EmbeddedEngineHandler.isLocalEngineLoaded()) {
EmbeddedEngineHandler.unloadEmbeddedEngine();
}
}
}
@WebListener
的解决方案,在一些容器中注销了一些java.sql.Driver
(使用DriverManager.deregisterDriver
),我总是通过存储Class<? extends java.sql.Driver>
来注销给定容器注册的确切驱动程序,并且我想知道是否有什么遗漏。 - Tomasz Linkowski<catalina-home>/lib
中并在 server.xml 中定义数据源,则可以实现此目的。其他应用服务器中也存在类似的功能。手动注册/注销驱动程序对于这些情况不起作用(对于非Web应用程序也是如此),即使每个WAR都注册了驱动程序,如果驱动程序全局可用,我认为它也无法解决我的问题(java.sql.Driver
实现应该是轻量级的)。 - Mark Rotteveel