这是一篇关于项目经验积累的文章,在实际的项目中我们常常会遇到很多的问题,可能有很多种不同的解决办法,但是将自己的解决办法通过文字的方式记录下来,不失为一种经验值积累的好方法,毕竟好记性不如烂笔头嘛,下面就开始我们的正题,go!!!
这几天,非非做公司的项目,需要做如下的一个需求,对于我一个基本没有项目经验的实习生来说,这可把我给难住了,终于在我翻了几十篇博客后,给出了我的解决方法:
需求如下:
- 有这么一个需求,我们提供一个接口标准,写成了一个jar,这个标准给不同的厂商去实现,实现完了打成一个jar,然后再把这个jar放到我们平台上运行, 具体厂商是怎么实现的我们不关心,我们需要做到的是只依赖于这个接口,而不依赖于具体的实现。而且我们希望厂商实现完了这些接口打成的jar放到他们的服务器上,或者修改了这些jar的实现,从新发布到服务器上,我们不需要重新发布项目。 即实现一个动态加载jar的功能。
上述需求,我们需要三个项目:
- 第一个就是这个接口项目,我们定义接口标准,打成jar给其他厂商。
- 第二个就是厂商实现了这个jar的接口,将实现了这些接口的项目打成一个jar交给我们,或者发布到他们的服务器上。
- 第三个就是我们的平台项目。
解决办法:
基于上述需求我们的做法:首先我们需要把我们定义的接口jar引入到我们的项目依赖中。其次我们需要动态的去加载第三方厂商的jar,动态的去获取实现类。这个实现类的jar,可能在某个磁盘的某个位置,或者就放在项目的某个目录下,或者在远程服务器上面,总之没有将他加入到项目的依赖中去(一般在远程服务器上面,随便别人怎么玩儿,与我无关,我只依赖于接口)。然后通过我们定义的接口去调用具体的实现。
具体解决步骤如下:
- 第一步,通过文件操作,将这个jar文件加载到内存中。
- 第二步,通过自定义classLoader将jar文件中的接口的实现类加载到JVM。
- 第三步:通过classLoader获取到jar文件具体的实现类。
- 第四步:通过类反射生成对应的实例化对象
- 第五步:然后通过这个具体的实现类的实例对象就可以调用这个jar中的方法
到此基本完成了上述需求。
参考Demo代码如下:
// 定义第三方jar文件的路径,此处是相对路径,当然也可以写绝对路径,也可以是远程服务器路径。
String path = "/adapterClassLoader/lib/adapter-0.0.1-SNAPSHOT.jar";
//获取 JarFile
JarFile jarFile = new JarFile(new File(path));
//获取这个jar的URL 固定写法
URL url = new URL("file:" + path);
//定义ClassLoader 并将URL给ClassLoader,这个URL是个数组,当然此处我只有一个URL,直接这么写的。
ClassLoader loader = new URLClassLoader(new URL[] { url });
//定会 JarEntry 这个就是说,我们这个jar中有很多的类
Enumeration<JarEntry> es = jarFile.entries();
//然后我们需要遍历这次entry节点,获取到每一个类,然后进行操作
while (es.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) es.nextElement();
String name = jarEntry.getName();
if (name != null && name.endsWith(".class")) { // 只解析了.class文件
//将类加载到JVM
Class<?> c = loader.loadClass(name.replace("/", ".").substring(0, name.length() - 6)); // 处理 .class后缀名
//反射获取某个类对象的实例 直接c.newInstance()方法已经被废弃了。
Object object = c.getDeclaredConstructor().newInstance();
//此处为什么可以进行类型转换,因为我们将接口定义的jar引入到了项目的依赖中。否则是无法进行转换的,我们只有通过其他的方式进行方法调用。
AdapterService adapterService = (AdapterService) object;
//调用接口定义的方法
adapterService.eatMeat();
//其实到这里我们的功能需求就已经实现了,当然通过反射,我们还可以获取到这个类的更多信息,此处就不一一罗列了。
}
}
当然有些需求需要我们动态的去加载jar中的某个类,而不是整个jar文件,那么我们可以通过以下的方式可以拿到想要的类
//动态的去加载jar文件中的某一个类
URL url = new URL("file:C:\\Users\\Administrator\\IdeaProjects\\test\\data.jar");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { url });
Class<?> c1 = urlClassLoader.loadClass("AdapterServiceImpl");
Object newInstance = c1.getConstructor().newInstance();
AdapterService AdapterService= (AdapterService) newInstance;
AdapterService.eatMeat();
通过以上方法,非非我完美的解决了这个需求,老大夸我可真是个小机灵鬼,嘿嘿嘿。
作者感言:
在实际的项目需求中,我们会遇到各种各样的问题,在网上我们也能搜到各种解决方案,可能有的适合自己,有的不适合,这是需要我们去辨别的。我自己遇到的问题,可能也是别人会遇到的问题,自己只是提供了一种解决方案,希望可以帮助到其他人。对于自己的话,也是一个成长的记录,等几年回过头来看,都是自己的程序人生的点点滴滴,说不定会有许多感慨了,可能以后的文章内容不再局限于知识点的讲解了,慢慢的会有一些解决问题的文章出来,记录自己在实际项目中遇到的一些问题以及解决方案。顺便提一句,2020年的秋招已经来到,希望各位小伙伴们可以找到自己心仪的工作,当然非非自己也要努力了,争取早日拿到offer,还可以过个开心的年,哈哈。