Java 从内存动态加载JAR
我想动态加载一个JAR,直接加载内存。Java 从内存动态加载JAR,java,jar,classloader,Java,Jar,Classloader,我想动态加载一个JAR,直接加载内存。 比方说,我有一个包含JAR的缓冲区,我想加载JAR中的所有类,或者至少列出JAR中存在的所有文件。(课程、图片等…) 例如,如果我加载的第一个类依赖于第二个类,我该怎么办? java知道如何处理这个问题吗?或者我自己处理这个问题?您应该使用自定义的类加载器并将JAR文件设置为其类路径。 类始终是惰性加载的,您不会显式加载它们。一旦JAR位于类加载器的类路径上,您就可以解析资源。您可能需要先将JAR写入磁盘,然后使用以下方法将其添加到类路径: 如果要枚举不在
比方说,我有一个包含JAR的缓冲区,我想加载JAR中的所有类,或者至少列出JAR中存在的所有文件。(课程、图片等…)
例如,如果我加载的第一个类依赖于第二个类,我该怎么办?
java知道如何处理这个问题吗?或者我自己处理这个问题?您应该使用自定义的
类加载器
并将JAR文件设置为其类路径。
类始终是惰性加载的,您不会显式加载它们。一旦JAR位于
类加载器的类路径上,您就可以解析资源。您可能需要先将JAR写入磁盘,然后使用以下方法将其添加到类路径:
如果要枚举不在类路径中的jar内容,可以始终将其视为zip文件:
在这里,您可以使用URLClassloader加载jar。
在这个例子中,我有一个jar,它有一个名为com.x.Test的类,它有一个print方法,下面的代码描述了如何加载类和调用方法,这只是一个例子,但是更好地使用接口、工厂方法和反射可以使代码更好
File jarFile = new File("myspace\\my.jar");
URLClassLoader loader = new URLClassLoader(new URL[]{jarFile.toURL()});
Class<?> clazz = loader.loadClass("com.x.Test");
clazz.getMethod("print").invoke(clazz.getConstructor().newInstance(), args);
File jarFile=new文件(“myspace\\my.jar”);
URLClassLoader=新URLClassLoader(新URL[]{jarFile.toURL()});
clazz类=loader.loadClass(“com.x.Test”);
调用(clazz.getConstructor().newInstance(),args);
既然您说过“至少列出JAR中存在的所有文件”,那么让我们从这个相当简单的任务开始
假设您的JAR文件位于字节数组中,byte[]buffer
:
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
for(;;) {
JarEntry nextEntry = is.getNextJarEntry();
if(nextEntry==null) break;
System.out.println(nextEntry);
}
}
从这样一个表示加载类并不是现成的,因为标准的ClassLoader
实现依赖于JarFile
实现,后者依赖于物理文件而不是抽象
因此,除非您只是将缓冲区写入一个临时文件,否则它最终将实现您自己的类加载器
。由于JRE只支持如上所示的流访问,因此您必须线性扫描以找到请求的资源/类,或者迭代一次并将条目存储到映射中
实现ClassLoader
的一种替代方法是实现一个自定义URL
处理程序,与URLClassLoader
一起使用,这样可以减少上述查找任务:
final Map<String,byte[]> map=new HashMap<>();
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
for(;;) {
JarEntry nextEntry = is.getNextJarEntry();
if(nextEntry==null) break;
final int est=(int)nextEntry.getSize();
byte[] data=new byte[est>0? est: 1024];
int real=0;
for(int r=is.read(data); r>0; r=is.read(data, real, data.length-real))
if(data.length==(real+=r)) data=Arrays.copyOf(data, data.length*2);
if(real!=data.length) data=Arrays.copyOf(data, real);
map.put("/"+nextEntry.getName(), data);
}
}
URL u=new URL("x-buffer", null, -1, "/", new URLStreamHandler() {
protected URLConnection openConnection(URL u) throws IOException {
final byte[] data = map.get(u.getFile());
if(data==null) throw new FileNotFoundException(u.getFile());
return new URLConnection(u) {
public void connect() throws IOException {}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
};
}
});
try(URLClassLoader cl=new URLClassLoader(new URL[]{u})) {
cl.loadClass( « a class from your JarFile buffer »);
}
final Map=new HashMap();
try(JarInputStream is=new-JarInputStream(new-ByteArrayInputStream(buffer))){
对于(;;){
JarEntry nextEntry=is.getNextJarEntry();
如果(nextEntry==null)中断;
final int est=(int)nextEntry.getSize();
字节[]数据=新字节[est>0?est:1024];
int实数=0;
for(int r=is.read(data);r>0;r=is.read(data,real,data.length real))
如果(data.length==(real+=r))data=Arrays.copyOf(data,data.length*2);
如果(real!=data.length)data=Arrays.copyOf(data,real);
map.put(“/”+nextEntry.getName(),数据);
}
}
URL u=新URL(“x-buffer”,null,-1,“/”,新的URLStreamHandler(){
受保护的URL连接openConnection(URL u)引发IOException{
最后一个字节[]data=map.get(u.getFile());
如果(data==null)抛出新的FileNotFoundException(u.getFile());
返回新的URL连接(u){
public void connect()引发IOException{}
@凌驾
公共InputStream getInputStream()引发IOException{
返回新的ByteArrayInputStream(数据);
}
};
}
});
try(URLClassLoader cl=newurlclassloader(newurl[]{u})){
cl.loadClass(«来自JarFile缓冲区的类»);
}
@Maksym这并没有回答我的问题,因为我问过如何直接从内存而不是从路径执行。不管它放在哪里……看看@Chris at using JCL的解决方案——其中有一个从任意输入流(在您的例子中是从内存加载的字节流)加载类的示例@基本问题是JCL本身就是一个JAR,所以我必须先加载它,然后才能开始使用它。因为我想直接从内存加载JAR中的类,所以我想我不能在类路径中设置JAR。还是我错了?你说的“因为我想直接从内存加载JAR中的类”是什么意思?这些是在JAR中,还是在内存中?内存中有一个缓冲区,其结构类似于JAR文件。它从磁盘加载JAR,而不是从内存加载。很好的解决方案,不幸的是它不处理资源,因此这不是一个功能齐全的类加载器。使用“类路径”添加其他URL协议允许通过ClasspathURLStreamHandler提供资源,但前提是该类加载器设置为contextClassLoader。@tporeba我刚刚验证了对该类加载器或使用该加载器加载的类调用getResource
或getResourceAsStream
是否有效。因为Java不关心上下文类加载器,所以您的问题似乎源于另一个软件、应用程序或框架,使用上下文类加载器进行资源查找。如果您想遵循该约定,则必须将该类装入器设置为这样。但这适用于所有类加载器,并不会使其成为不完整的类加载器。查找您自己的资源的正确方法是YourClass.class.getResource[AsStream](…)
…是的,您是对的,我不够精确。当您通过getResource
或getresourceastream
直接使用类加载器时,您可以正确地获取资源。但是,如果您将这个类加载器放入一个框架中,该框架处理getResource
返回的java.net.URL
实例,那么它的工作方式就不同于普通的类加载器。在我的例子中,URL被转换成字符串,然后不可能重新创建java.net.URL
ins
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
for(;;) {
JarEntry nextEntry = is.getNextJarEntry();
if(nextEntry==null) break;
System.out.println(nextEntry);
}
}
final Map<String,byte[]> map=new HashMap<>();
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
for(;;) {
JarEntry nextEntry = is.getNextJarEntry();
if(nextEntry==null) break;
final int est=(int)nextEntry.getSize();
byte[] data=new byte[est>0? est: 1024];
int real=0;
for(int r=is.read(data); r>0; r=is.read(data, real, data.length-real))
if(data.length==(real+=r)) data=Arrays.copyOf(data, data.length*2);
if(real!=data.length) data=Arrays.copyOf(data, real);
map.put("/"+nextEntry.getName(), data);
}
}
URL u=new URL("x-buffer", null, -1, "/", new URLStreamHandler() {
protected URLConnection openConnection(URL u) throws IOException {
final byte[] data = map.get(u.getFile());
if(data==null) throw new FileNotFoundException(u.getFile());
return new URLConnection(u) {
public void connect() throws IOException {}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
};
}
});
try(URLClassLoader cl=new URLClassLoader(new URL[]{u})) {
cl.loadClass( « a class from your JarFile buffer »);
}