我有两个jar文件,Foo.jar
和Bar.jar
。在Foo.jar
内,我有一个Foo
课,其main
方法如下:
public class Foo {
public static void main(String[] args) {
Bar bar = new Bar();
bar.sayHello();
new File("./Bar.jar").deleteOnExit();
}
}
班级Bar
位于Bar.jar
,如下所示:
public class Bar {
public void sayHello() {
System.out.println("Hello! I'm Bar!");
}
}
这里的意图是Foo.jar
将使用Bar.jar
中的一个类,之后它将删除Bar.jar
。通过上面的实现,这是执行时的输出:
$ java -jar Foo.jar
Hello! I'm Bar!
但Bar.jar
不会被删除。在这里,我在课程new File("./Bar.jar").deleteOnExit();
中尝试了new File("./Bar.jar").delete();
和Foo
。
如果我更改类Bar
以关闭其ClassLoader:
public class Bar {
public void sayHello() {
System.out.println("Hello! I'm Bar!");
URLClassLoader classLoader = (URLClassLoader) this.getClass().getClassLoader();
try {
classLoader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
再次运行Foo.jar
,这是输出:
$ java -jar Foo.jar
Hello! I'm Bar!
输出与前一个示例相同,但在这种情况下Bar.jar
被删除,这很好,但现在的问题是,如果我在类Foo
中执行类似的操作:
9 public class Foo {
10 public static void main(String[] args) {
11 Bar bar = new Bar();
12 bar.sayHello();
13
14 new File("./Bar.jar").deleteOnExit();
15
16 SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
17 };
18 }
19 }
这将成为执行Foo.jar
:
$ java -jar Foo.jar
Hello! I'm Bar!
Exception in thread "main" java.lang.NoClassDefFoundError: playground/modulae/Foo$1
at playground.modulae.Foo.main(Foo.java:16)
Caused by: java.lang.ClassNotFoundException: playground.modulae.Foo$1
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
在这种情况下,Bar.jar
仍会被删除,但当我尝试创建新的SimpleFileVisitor
时,它会以NoClassDefFoundError
崩溃。似乎关闭Bar.jar
中的ClassLoader会导致Foo.jar
的某些事情变得混乱,但我还没有找到任何其他方法可以让我从{{1}删除Bar.jar
}}
此示例的一般结构非常类似于实际应用程序中发生的情况,后者稍后使用Foo.jar
。我尝试过创建一些其他类而不是java.nio.file.SimpleFileVisitor
,但没有一个崩溃,但我想这取决于它们是否在java.no.file.SimpleFileVisitor
调用Foo
之前由ClassLoader加载
在搞乱了类加载器(我仍然不完全理解)后,我有一些似乎已经足够了。但是,我担心我正在做一些有点狡猾的事情,并且有一种更“正确”的方式来处理我正在做的类加载器操作。
所以现在的设置是:
Bar
中使用Foo
方法和main
方法使用了课程speak()
。Foo.jar
中使用简单Bar
方法的课程sayHello()
。Bar.jar
函数中,我使用自定义类加载器创建main
的实例foo
并调用Foo
foo.speak()
我创建foo.speak()
bar
的实例,如下所示:Bar
&lt; - 请注意,这里没有任何反映或任何幻想。然后我拨打Bar bar = new Bar()
bar.sayHello()
我致电bar.sayHello()
this.getClass().getClassLoader().close()
,删除main
然后是代码:
Bar.jar
public class Foo {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
System.out.println("Foo.main: Class path: " + System.getProperty("java.class.path"));
URLClassLoader initialClassLoader = (URLClassLoader) Foo.class.getClassLoader();
System.out.println("Foo.main: initialClassLoader = " + initialClassLoader);
System.out.println("Foo.main: initialClassLoader.getURLs() = " + Arrays.toString(initialClassLoader.getURLs()));
File fooJar = new File("./Foo.jar");
File barJar = new File("./Bar.jar");
CustomClassLoader customClassLoader = new CustomClassLoader(new URL[]{fooJar.toURI().toURL(), barJar.toURI().toURL()});
System.out.println("Foo.main: customClassLoader = " + customClassLoader);
System.out.println("Foo.main: customClassLoader.getURLs() = " + Arrays.toString(customClassLoader.getURLs()));
Class<?> fooClass = customClassLoader.findClass("Foo");
Object foo = fooClass.newInstance();
Method speak = fooClass.getMethod("speak");
speak.invoke(foo);
barJar.deleteOnExit();
SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
};
}
public void speak() {
System.out.println("Foo.speak()");
URLClassLoader classLoader = (URLClassLoader) this.getClass().getClassLoader();
System.out.println("Foo.speak(): classLoader = " + classLoader);
System.out.println("Foo.speak(): classLoader.getURLs() = " + Arrays.toString(classLoader.getURLs()));
Bar bar = new Bar();
bar.sayHello();
}
}
public class Bar {
public void sayHello() {
System.out.println("Bar.sayHello()");
URLClassLoader classLoader = (URLClassLoader) this.getClass().getClassLoader();
System.out.println("Bar.sayHello(): classLoader = " + classLoader);
System.out.println("Bar.sayHello(): classLoader.getURLs() = " + Arrays.toString(classLoader.getURLs()));
try {
classLoader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class CustomClassLoader extends URLClassLoader {
public CustomClassLoader(URL[] urls) {
super(urls);
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}
并删除$ java -jar Foo.jar
Foo.main: Class path: Foo.jar
Foo.main: initialClassLoader = sun.misc.Launcher$AppClassLoader@1f96302
Foo.main: initialClassLoader.getURLs() = [file:/Foo.jar]
Foo.main: customClassLoader = CustomClassLoader@a298b7
Foo.main: customClassLoader.getURLs() = [file:/Foo.jar, file:/Bar.jar]
Foo.speak()
Foo.speak(): classLoader = CustomClassLoader@a298b7
Foo.speak(): classLoader.getURLs() = [file:/Foo.jar, file:/Bar.jar]
Bar.sayHello()
Bar.sayHello(): classLoader = CustomClassLoader@a298b7
Bar.sayHello(): classLoader.getURLs() = [file:/Foo.jar, file:/Bar.jar]
。
正如我们所见,Bar.jar
可以在Bar
内引用而没有任何反映,它已加载了正确的Foo.jar
,CustomClassLoader
可以成功从Bar.jar
中删除调用Foo
之后{1}}。
现在,这解决了我的问题。但是,我不确定我是否正确解决了bar.sayHello()
。事实上,CustomClassLoader
有效地做的唯一事情就是公开CustomClassLoader
。我尝试了很多不同的方法来实现findClass()
,但却无法让它工作,只是偶然我尝试了loadClass()
而且它有效。
有没有人对如何使用findClass()
有任何意见?
答案 0 :(得分:0)
好的,所以我会这样做:
我会定义一个Mac OS X 10.12.5
Chrome Browser 59.0.3071.115
npm 5.0.4
cucumber@2.3.1
nightwatch@0.9.16
nightwatch-cucumber@7.1.10
v8.0.0
使用的界面,并由Foo
实施:
Bar
接口必须位于Foo.jar。
public interface BarIntf {
public void sayHello();
}
课程将实施Bar
:
BarIntf
现在,我没有将Foo.jar和Bar.jar放在类路径中,而是只放入Foo.jar,并在程序public class Bar implements BarIntf {
@Override
public void sayHello() {
System.out.println("Hello! I'm Bar!");
}
}
中创建一个类加载器,只是为了加载Foo
类
看起来像这样:
Bar
更新:
使用反射(无需接口):
public class Foo {
public static void main(String[] args) {
File jar = new File("./Bar.jar");
try (URLClassLoader cl = new URLClassLoader(
new URL[] {jar.toURI().toURL()})) {
Class<?> clazz = cl.loadClass("bar.Bar");
BarIntf bar = (BarIntf)clazz.newInstance();
bar.sayHello();
// ...a lot of very useful things
} catch (IOException | ClassNotFoundException
| InstantiationException | IllegalAccessException ex) {
Logger.getLogger(Foo.class.getName()).log(Level.SEVERE, null, ex);
} finally {
jar.delete();
}
}
}