Java:从不同的ClassLoader中的类调用方法

时间:2018-07-16 18:53:29

标签: java reflection classloader

我正在编写一个将在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()? 还是有一种非常不同的方法来处理这种情况?

1 个答案:

答案 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