Java 扫描Embeded Tomcat中的清单类路径JAR
我有一个嵌入式Tomcat应用程序,它打包为一个可执行(瘦)Java 扫描Embeded Tomcat中的清单类路径JAR,java,spring-boot,servlet-3.0,embedded-tomcat-8,Java,Spring Boot,Servlet 3.0,Embedded Tomcat 8,我有一个嵌入式Tomcat应用程序,它打包为一个可执行(瘦)jar,具有多个外部jar依赖项 构建过程生成一个META-INF/MANIFEST.MF,其中包含头字段Main Class和Class Path(每个运行时依赖项有一个条目) 我想使用一个简单的java-jar my_app.jar来执行应用程序,但是无法让Tomcat扫描这些依赖的jar(以便发现TLD或@handleType类,比如SpringWebApplicationInitializer) 我以这种方式配置jar扫描: S
jar
,具有多个外部jar
依赖项
构建过程生成一个META-INF/MANIFEST.MF
,其中包含头字段Main Class
和Class Path
(每个运行时依赖项有一个条目)
我想使用一个简单的java-jar my_app.jar
来执行应用程序,但是无法让Tomcat扫描这些依赖的jar(以便发现TLD或@handleType
类,比如SpringWebApplicationInitializer
)
我以这种方式配置jar扫描:
StandardJarScanner jarScanner = (StandardJarScanner) ctx.getJarScanner();
jarScanner.setScanBootstrapClassPath(true);
jarScanner.setScanClassPath(true);
所有的JAR都有一个META-INF
文件夹,但是扫描器完全忽略了它们
有什么想法吗
注意:我可以使用不同的方法(fatjar,从maven运行,…)来实现这一点,但我对以这种方式实现它感兴趣,就像任何其他java应用程序一样
Tomcat通过在类加载器层次结构(自底向上)中反复调用
URLClassLoader.getURLS()
,获取要扫描的jar URL
当java应用程序作为java-jar执行时,URLClassLoader.getURLS()
不会返回类路径jar,因此系统类加载器会出现问题
见:
在上一篇文章中,建议使用反射来访问system classloader实例中的私有字段,但这会带来几个问题:
- 安全管理员可以禁止此登录
- 解决方案取决于实现
所以我想出了另一个办法:
对于给定的类加载器,枚举所有可用的清单cl.getResources(“META-INF/manifest.MF”)
。这些清单可以是由当前类加载器或其提升类加载器管理的JAR清单
对其父类加载器执行相同的操作
返回(1)中但不在(2)中的那些清单的JAR集
该方法工作的唯一要求是类路径中的JAR必须有一个清单才能返回(要求不多)
/**
*返回URL的搜索路径,以加载
*指定的类装入器,包括在
*{@code Class path}可执行jar清单的头,在
*类加载器是系统类加载器的情况。
*
*注意:这些最后的罐子不是由供应商退回的
*{@link java.net.URLClassLoader#getURLs()}。
*
*@param cl
*@返回
*/
公共静态URL[]获取URL(URLClassLoader cl){
如果(cl.getParent()==null | |!(cl.getParent())
URLClassLoader的实例){
返回cl.getURL();
}
Set urlSet=new LinkedHashSet();
URL[]URL=cl.getURL();
URL[]urlsFromManifest=GetJarURLSFROMMANIFEST(cl);
URLClassLoader parentCl=(URLClassLoader)cl.getParent();
URL[]ancestorUrls=getJarUrlsFromManifests(parentCl);
for(int i=0;i
Tomcat注册发行:
/**
* 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);
}
}