Java读取jar文件、Java加载的资源路径、Java的ClassPatch、System.getProperty("java.class.path")、springboot自动装配时扫描所有依赖jar包中的spring.factories的配置类底层原理
Java 读取JAR文件信息-PathMatchingResourcePatternResolver 解析classpath*:
Java 读取JAR文件信息
JAR 文件格式以流行的 ZIP 文件格式为基础。与 ZIP 文件不同的是,JAR 文件不仅用于压缩和发布,而且还用于部署和封装库、组件和插件程序,并可被像编译器和 JVM 这样的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用来指示工具如何处理特定的 JAR-百度百科
参考资料1
参考资料2
Java遍历包中所有类方法注解-很详细
为什么想到读取JAR文件的信息
查看spring 资源处理,查找多个资源classpath*,会去寻找jar包中的内容,因此会出现读取jar包中的文件或者读取文件夹中的文件等等形式。主要的类PathMatchingResourcePatternResolver
通过阅读源码和查询JavaAPI资源有两种读取的方式
-
通过加载文件的形式读取 JarFile jarFile = new JarFile(jarAddress);
你可能注意到当文件不在class path中时,JarFile类对于从JAR中读取文件文件是很有用的。当你想指定目标JAR文件时,JarFile类对于从JAR中读取文件同样也很有用。@Test public void readJarFile() throws IOException{ String jarAddress = "F:/project/...../WEB-INF/lib/activation-1.1.jar"; JarFile jarFile = new JarFile(jarAddress); Enumeration<JarEntry> entries = jarFile.entries();//遍历整个jar文件 while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); if(!jarEntry.isDirectory()){ //是否为一个文件夹 // javax/activation/DataSource.class System.out.println(jarEntry.getName());//获取文件的路径信息 jarFile.getInputStream(jarEntry);//获取文件流信息 System.out.println(IOUtils.toString(jarFile.getInputStream(jarEntry))); } } }
-
通过URL的方式.getConnection获取
@Test public void JarURLConnection() throws IOException{ //这里可以是JAR包中的子文件夹或者子文件的信息 URL url = new URL("jar:file:F:/project/...../WEB-INF/lib/activation-1.1.jar!/"); JarURLConnection jarConnection = (JarURLConnection)url.openConnection(); System.out.println("文件路径:"+jarConnection.getURL().getFile()); System.out.println("入口文件:"+jarConnection.getEntryName()); System.out.println("Manifest :"+jarConnection.getManifest ().getEntries().toString()); JarFile jarFile = jarConnection.getJarFile(); //获取Jar文件夹 Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); if(!jarEntry.isDirectory()){ // javax/activation/DataSource.class System.out.println(jarEntry.getName()); } } }
如果JAR在ClassPath下面获取指定路径下的文件信息
URL url = ClassLoader.getSystemResource(name); //单个
Enumeration<URL> resources ClassLoader.getSystemResources(String name); //多个
//或者
InputStream stream = ClassLoader.getSystemResourceAsStream("javax/activation/DataSource.class");
// JDK下的,会加载classpath下的资源
public static URL getSystemResource(String name) {
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResource(name);
}
return system.getResource(name);//调用下面的方法
}
public URL getResource(String name) {
URL url;
if (parent != null) {
url = parent.getResource(name);
} else {
url = getBootstrapResource(name);
}
if (url == null) {
url = findResource(name);
}
return url;
}
spring 中寻找多个classpath *:/META-INF/ ** .txt
去下是测试例子,你可以尝试断点跟踪
1.先获取目录(顶级的目录没有模式匹配的/META-INF/),加载目录里面的所有资源/META-INF/
2.在所有资源里面进行查找匹配URL资源中去选择匹配的(/META-INF/下面的文件夹),找出我们需要的资源
@Test
public void PathMatchingResourcePatternResolver1() throws Exception{
ResourcePatternResolver loader = new PathMatchingResourcePatternResolver();
Resource[] resources = loader.getResources("classpath:/META-INF/ *.txt");
for(int i=0;i< resources.length;i++) {
System.out.println(resources[i].getURL().getFile());
}
}
这个方法找到所有的资源,然后一个个的遍历,如果里面有JAR资源或者文件夹资源需要进行遍历处理哦
参考资源3-spring 资源
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
ClassLoader cl = getClassLoader();
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); //找到所有的资源哦
Set<Resource> result = new LinkedHashSet<Resource>(16);
while (resourceUrls.hasMoreElements()) {
URL url = resourceUrls.nextElement();
result.add(new UrlResource(url));// 就是将URL资源进行封装成spring resource
}
return result.toArray(new Resource[result.size()]);
}
得到了这样的URL资源后,判断这个资源是JAR类型的jar:file:…xx.jar!/META-INF/,然后进行遍历,会得到当前文件中的所有的资源,不光光是这个/META-INF/下的,然后进行处理,看看代码
/**
* Find all resources in jar files that match the given location pattern
* via the Ant-style PathMatcher.
* @param rootDirResource the root directory as Resource(根路径的URl资源,被spring 抽象化Resource)
* @param subPattern the sub pattern to match (below the root directory) 需要匹配的类型
*/
protected Set<Resource> doFindPathMatchingJarResources(Resource rootDirResource, String subPattern)
throws IOException {
URLConnection con = rootDirResource.getURL().openConnection();
JarFile jarFile;
String jarFileUrl;
String rootEntryPath;
boolean newJarFile = false;
if (con instanceof JarURLConnection) { //通过Connection的方式获取JARFile
// Should usually be the case for traditional JAR files.
JarURLConnection jarCon = (JarURLConnection) con;
ResourceUtils.useCachesIfNecessary(jarCon);
jarFile = jarCon.getJarFile();
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
JarEntry jarEntry = jarCon.getJarEntry();//当前的入口地址是?就是!/后面的地址的信息
rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
}
else {
// No JarURLConnection -> need to resort to URL file parsing.
// We'll assume URLs of the format "jar:path!/entry", with the protocol
// being arbitrary as long as following the entry format.
// We'll also handle paths with and without leading "file:" prefix.
String urlFile = rootDirResource.getURL().getFile();
int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
jarFileUrl = urlFile.substring(0, separatorIndex);
rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
jarFile = getJarFile(jarFileUrl);
}
else { // 通过JAR文件的形式
jarFile = new JarFile(urlFile);
jarFileUrl = urlFile;
rootEntryPath = "";
}
newJarFile = true;
}
try {
if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
// Root entry path must end with slash to allow for proper matching.
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set<Resource> result = new LinkedHashSet<Resource>(8);
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement(); //获取文件的信息
String entryPath = entry.getName();//文件的路径信息
if (entryPath.startsWith(rootEntryPath)) {
String relativePath = entryPath.substring(rootEntryPath.length());
if (getPathMatcher().match(subPattern, relativePath)) {
result.add(rootDirResource.createRelative(relativePath));//根据root的URL路径创建一个相对的路径new URL(root, relativePath)
}
}
}
return result;
}
finally {
// Close jar file, but only if freshly obtained -
// not from JarURLConnection, which might cache the file reference.
if (newJarFile) {
jarFile.close();
}
}
}