我正在编写一个将在Tomcat8上运行的Web应用程序,该应用程序应该能够在其仍在运行时进行更新。
为此,它将创建一个新的ClassLoader,并在每次按下给定的“重新加载”按钮时再次加载整个API。
// get the urls from the current loader
URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
urls.addAll(Arrays.asList(loader.getURLs()));
// get the urls from the tomcat loader
loader = (URLClassLoader) loader.getParent();
urls.addAll(Arrays.asList(loader.getURLs()));
URL urlArray[] = new URL[urls.size()];
urlArray = urls.toArray(urlArray);
// my loader
loader = new URLClassLoader(urlArray, loader.getParent());
// this will throw ClassCastException
// because the newInstance will not return the System object
// that this loader knows
System newSystem = (System) loader.loadClass(System.class.getCanonicalName()).newInstance();
但是!当我需要调用即将死去的系统的shutdown
方法时,问题就开始了。
如果我尝试将“系统”存储在变量中,以便以后可以调用shutdown
,我将得到一个ClassCastException
,因为对于Java,我是从另一个ClassLoader与Tomcat知道的System类不同。
如何从Servlet上下文中调用所需的System.shutdown()
?
还是有一种非常不同的方法来处理这种情况?
答案 0 :(得分:1)
问题似乎是您在多个类加载器中都具有该类,然后-您不应该从主类加载器中加载该类,因为那样您将无法实际重新加载该代码。
通过这样的原始名称加载类:
System newSystem = (System) Class.forName("my.system.System", true, myClassLoader).newInstance();
newSystem.shutdown();
或者您也可以使用反射来调用方法:
Class<?> systemClass = Class.forName("my.system.System", true, myClassLoader);
Method shutdown = systemClass.getMethod("shutdown");
Object newSystem = systemClass.newInstance();
shutdown.invoke(newSystem);
或者您可以使用Java服务,并在主类加载器中具有接口,并且仅在您希望能够重新加载的动态代码中实现:https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html