每当我在tomcat中部署一个web应用程序时,WEB-INF/lib中的所有jar都将被加载到应用程序ClassLoader中。如何将动态从不同位置加载到当前类加载器?
我有几个其他位置的一些jar集,像WEB-INF/ChildApp1/*。jar,WEB-INF/ChildApp2/*。jar ..... 根据用户请求我想将一些jar集加载到当前的类加载器中。
注意:我不想创建任何子类加载器。
我的真实需求是,以编程方式如何将jar添加到当前类加载器中。
每当我在tomcat中部署一个web应用程序时,WEB-INF/lib中的所有jar都将被加载到应用程序ClassLoader中。如何将动态从不同位置加载到当前类加载器?
我有几个其他位置的一些jar集,像WEB-INF/ChildApp1/*。jar,WEB-INF/ChildApp2/*。jar ..... 根据用户请求我想将一些jar集加载到当前的类加载器中。
注意:我不想创建任何子类加载器。
我的真实需求是,以编程方式如何将jar添加到当前类加载器中。
我做了一次,但这是有点破解。请参阅下面的代码:
final URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
final Class<URLClassLoader> sysclass = URLClassLoader.class;
// TODO some kind of a hack. Need to invent better solution.
final Method method = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class });
method.setAccessible(true);
for (final File jar : jars) {
method.invoke(sysloader, new URL[] { jar.toURI().toURL() });
}
你需要ClassLoader.getSystemClassloader()更改为您要使用的类加载器。您也可以检查,如果这是一个URLClassLoader 我认为有更好的解决方案的实例,但是这个工作对我来说
看来你是在系统类loader中添加jar。如果有任何jar版本冲突,那么我的应用程序可能无法按预期工作。例如,jsonv1.jar已经加载到webapp CL(Application class loader)中,那么如果我加载jsonv2.jar在系统CL,如你所建议的。然后我的应用程序可能会导致NoSuchMethodError或NoClassDefFoundError? –
是的,我使用了系统类加载器(我有一个非常简单的例子,并确保一些jar只能加载一次),但对于我,我建议使用不同的加载器。我不是100%确定会发生什么,如果你添加另一个包含相同类的jar。我会说它会继续使用旧的。但是这只是我的猜测,但很容易检查 – Sergi
您需要实现自己的WebappClassLoaderBase
在loader
在context.xml
配置进行定义。
WebappClassLoaderBase
最简单的方法是延长WebappClassLoader
为未来
package my.package;
public class MyWebappClassLoader extends WebappClassLoader {
public MyWebappClassLoader() {
}
public MyWebappClassLoader(final ClassLoader parent) {
super(parent);
}
@Override
public void start() throws LifecycleException {
String[] paths = {"/WEB-INF/ChildApp1/lib", "/WEB-INF/ChildApp2/lib"};
// Iterate over all the non standard locations
for (String path : paths) {
// Get all the resources in the current location
WebResource[] jars = resources.listResources(path);
for (WebResource jar : jars) {
// Check if the resource is a jar file
if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
// Add the jar file to the list of URL defined in the parent class
addURL(jar.getURL());
}
}
}
// Call start on the parent class
super.start();
}
}
WebappClassLoaderBase
WebappClassLoaderBase
那可从here获得。Common ClassLoader
WebappClassLoaderBase
定义WebappClassLoaderBase
在context.xml
<Context>
...
<Loader loaderClass="my.package.MyWebappClassLoader" />
</Context>
完成后,现在您的web应用程序将能够加载来自/WEB-INF/ChildApp1/lib
和/WEB-INF/ChildApp2/lib
的jar文件。
正如你所喜欢做同样的事情,但只有一个war
,你将需要使用一个黑客动态添加您的jar
文件。
这里是你如何进行:
ServletContextListener
添加您的jar文件为了动态添加jar
文件时上下文被初始化,您需要创建一个ServletContextListener
那将通过反射来呼叫URLClassLoader#addURL(URL)
,这是一个丑陋的黑客,但它的工作。请注意,它的工作原理是因为Tomcat中的webapp的ClassLoader
是WebappClassLoader
,它实际上是URLClassLoader
的子类。
package my.package;
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(final ServletContextEvent sce) {
try {
// Get the method URLClassLoader#addURL(URL)
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
// Make it accessible as the method is protected
method.setAccessible(true);
String[] paths = {"/WEB-INF/ChildApp1/lib", "/WEB-INF/ChildApp2/lib"};
for (String path : paths) {
File parent = new File(sce.getServletContext().getRealPath(path));
File[] jars = parent.listFiles(
new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith(".jar");
}
}
);
if (jars == null)
continue;
for (File jar : jars) {
// Add the URL to the context CL which is a URLClassLoader
// in case of Tomcat
method.invoke(
sce.getServletContext().getClassLoader(),
jar.toURI().toURL()
);
}
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public void contextDestroyed(final ServletContextEvent sce) {
}
}
ServletContextListener
在你的web应用的web.xml
只需添加:
<listener>
<listener-class>my.package.MyServletContextListener</listener-class>
</listener>
谢谢,我会测试并让你知道。第一个问题,为什么要在tomcat中保存WebappClassLoaderBase共享库(tomcat/lib),为什么tomcat不能创建WebappClassLoaderBase CL应用程序类加载器)从我的WEB-INF/lib ....第二个,WebappClassLoaderBase CL将有WEB-INF/lib +/WEB-INF/ChildApp1/lib“+”/ WEB-INF/ChildApp2/lib“罐子? –
#1因为加载程序是全局的,并不是特定于给定的webapp,所以它可以在通用类加载器中定义,以便在tomcat启动时可用#2是 –
感谢Nicolas,它的工作。所有外部位置和WEB-INF/lib jar我的意思是在after()之后,我的意思是在after()之后部署战争,在应用程序启动时,是否可以加载所有罐子在单个CL? –
我的要求是,从不同的位置罐装载到当前类loader.Don't要创建子类加载器。 –
你想实现的是不是标准的,如果没有自定义类加载器,你将无法做到这一点 –
非标准位置的列表将在运行时改变,或者它是固定的? –