每當我在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要創建子類加載器。 –
你想實現的是不是標準的,如果沒有自定義類加載器,你將無法做到這一點 –
非標準位置的列表將在運行時改變,或者它是固定的? –