我将嵌入式Tomcat应用程序打包为具有多个外部jar
依赖项的可执行文件(瘦)jar
。
构建过程生成带有标头字段META-INF/MANIFEST.MF
和Main-Class
的{{1}}(每个运行时依赖项都有一个条目)。
我想使用简单的Class-Path
执行应用程序,但我无法让Tomcat扫描这些依赖的jar (为了发现TLD或java -jar my_app.jar
类,春天@HandlesTypes
)。
我正在以这种方式配置jar扫描:
WebApplicationInitializer
所有的jar都有一个StandardJarScanner jarScanner = (StandardJarScanner) ctx.getJarScanner();
jarScanner.setScanBootstrapClassPath(true);
jarScanner.setScanClassPath(true);
文件夹,但扫描程序完全忽略它们。
有什么想法吗?
注意:我可以使用不同的方法(胖jar,从maven运行,......)来完成这项工作,但我有兴趣让它以这种方式工作,就像任何其他java应用程序一样。
答案 0 :(得分:3)
Tomcat通过在类加载器层次结构中重复调用URLClassLoader.getURLS()
来获取要扫描的jar URL(自下而上)
系统类加载器出现问题,因为当URLClassLoader.getURLS()
java -jar <executable-jar>
不返回类路径jar
请参阅:How does a classloader load classes reference in the manifest classpath?
在上一篇文章中,建议使用反射来访问系统类加载器实例中的私有字段,但这会带来几个问题:
所以我想出了另一种方式:
cl.getResources("META-INF/MANIFEST.MF")
。这些清单可以是由当前类加载器或其上级类加载器管理的jar。此方法的唯一要求是,类路径中的jar必须有一个清单才能返回(不多问)。
/**
* Returns the search path of URLs for loading classes and resources for the
* specified class loader, including those referenced in the
* {@code Class-path} header of the manifest of a executable jar, in the
* case of class loader being the system class loader.
* <p>
* Note: These last jars are not returned by
* {@link java.net.URLClassLoader#getURLs()}.
* </p>
* @param cl
* @return
*/
public static URL[] getURLs(URLClassLoader cl) {
if (cl.getParent() == null || !(cl.getParent()
instanceof URLClassLoader)) {
return cl.getURLs();
}
Set<URL> urlSet = new LinkedHashSet();
URL[] urLs = cl.getURLs();
URL[] urlsFromManifest = getJarUrlsFromManifests(cl);
URLClassLoader parentCl = (URLClassLoader) cl.getParent();
URL[] ancestorUrls = getJarUrlsFromManifests(parentCl);
for (int i = 0; i < urlsFromManifest.length; i++) {
urlSet.add(urlsFromManifest[i]);
}
for (int i = 0; i < ancestorUrls.length; i++) {
urlSet.remove(ancestorUrls[i]);
}
for (int i = 0; i < urLs.length; i++) {
urlSet.add(urLs[i]);
}
return urlSet.toArray(new URL[urlSet.size()]);
}
/**
* Returns the URLs of those jar managed by this classloader (or its
* ascendant classloaders) that have a manifest
* @param cl
* @return
*/
private static URL[] getJarUrlsFromManifests(ClassLoader cl) {
try {
Set<URL> urlSet = new LinkedHashSet();
Enumeration<URL> manifestUrls =
cl.getResources("META-INF/MANIFEST.MF");
while (manifestUrls.hasMoreElements()) {
try {
URL manifestUrl = manifestUrls.nextElement();
if(manifestUrl.getProtocol().equals("jar")) {
urlSet.add(new URL(manifestUrl.getFile().substring(0,
manifestUrl.getFile().lastIndexOf("!"))));
}
} catch (MalformedURLException ex) {
throw new AssertionError();
}
}
return urlSet.toArray(new URL[urlSet.size()]);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
Tomcat注册问题:https://bz.apache.org/bugzilla/show_bug.cgi?id=59226