通常动态加载Jar文件,是手动调用UrlClassLoader去加载,然后调用loader的loadClass获取到Class的引用,之后调用反射newInstance创建一个对象实例。
这种方式写出来的代码太繁琐,并且newInstance返回的对象需要强制类型转换,并且对于IDE来说都是也是不友好的,不利于代码的批量重构。
所以这种手动加载加载Jar的方式直接放弃了。
最终的解决方案是动态设置JVM的classPath,并把classPath指定到远程的Jar包。
1.我们把UserService封装到一个Jar,然后把Jar放到Http服务器的目录。
package com.sun.bean; public class User { private String id; public User(String id) { this.id = id; } public void sayHello() { System.out.println("Hello everyone, my name is "+id); } }
package com.sun.service; import com.sun.bean.User; public final class UserService { public static User getUserById(String id) { return new User(id); } }然后编译打包成UserService.jar,并放到本地Web服务器的根目录。
2.下面我们编写使用案例,留意setClassPath方法
package com.sun.bootstrap; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import com.sun.bean.User; import com.sun.service.UserService; public class Bootstrap { public final static void setClassPath(String path) { try { URI uri = new URI(path); URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class }); add.setAccessible(true); add.invoke(classLoader, uri.toURL()); } catch (Exception exp) { exp.printStackTrace(); } } public static void main(String[] args) { Bootstrap.setClassPath("http://localhost/UserService.jar"); User jim = UserService.getUserById("JimGreen"); jim.sayHello(); } }
从Bootstrap.java的代码中可以发现,我们直接使用new UserService,而不是通过ClassLoader.loadClass().newInstance()的方式去创建UserService对象实例。
3.编译Bootstrap类,这一步是关键,在编译时一定要指定classPath,否则编译不通过
javac -classpath /Users/M/workspace/lua/UserService.jar com/sun/bootstrap/Bootstrap.java
最后在com/sun/bootstrap路径下生成了Bootstrap.class文件。
4.执行Bootstrap类
java com/sun/bootstrap/Bootstrap结果打印:
Hello everyone, my name is JimGreen总结上述的方式, 我们可以按照平常的方式写代码(区别于loadClass的方式获取Class,然后newInstance),关键点在于调用了SystemClassLoader.addURL添加类加载路径,这样JVM可以主动帮我们 动态加载远程jar包。