1. 需要了解 java 可变参数
2. 需要了解 java 反射知识
比如允许用户在线提交 jar 包测试,插件化开发,tomcat 等 web 容器,热更新等
每个类加载器有自己的名字空间,对于同一个类加载器实例来说,名字相同的类只能存在一个,并且仅加载一次。不管该类有没有变化,下次再需要加载时,它只是从自己的缓存中直接返回已经加载过的类引用。
package com.carl.test;
public class TestJar {
private String hi = "huhu2";
public String sayHi() {
System.out.println("sayHi:" + hi);
return hi;
}
public String sayBye(String bye) {
System.out.println("sayBye:" + bye);
return bye;
}
}
package com.carl.classloaderdemo;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassloader {
// public static void classLoader(File file, String className, String methodName, Object[] args, Class<?>... parameterTypes) {
public static void classLoader(File file, String className, String methodName, Object[] args, Class<?>[] parameterTypes) {
try {
URL url = file.toURI().toURL();
// URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {url});
//得到系统类加载器,利用该加载器加载指定路径下的jar包
URLClassLoader urlClassLoader= (URLClassLoader) ClassLoader.getSystemClassLoader();
Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{ URL.class});
add.setAccessible(true);
add.invoke(urlClassLoader, new Object[] {url});
urlClassLoader.loadClass(className);
Class<?> c = urlClassLoader.loadClass(className);
//列出所有方法
// Method[] methods = c.getMethods();
// for (Method m : methods) {
// System.out.println(m.getName());
// }
if (args == null) {
c.getMethod(methodName, parameterTypes).invoke(c.newInstance());
} else {
c.getMethod(methodName, parameterTypes).invoke(c.newInstance(), args);
}
// urlClassLoader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//无参方法的例子
classLoader(new File("E:\\ABC\\TestJar.jar"), "com.carl.test.TestJar", "sayHi", null, null);
//有参方法的例子
classLoader(new File("E:\\ABC\\TestJar.jar"), "com.carl.test.TestJar", "sayBye", new String[]{"byebye"}, new Class<?>[]{String.class});
}
}
通过编写一个类继承自 ClassLoader,并重写 findClass 方法
package com.carl.classloaderdemo;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class MyClassloader2 extends ClassLoader {
private String fileName;
public MyClassloader2(String fileName) {
this.fileName = fileName;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File file = new File(fileName);
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
while ((len = fis.read()) != -1) {
bos.write(len);
}
byte[] data = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name, data, 0, data.length);
} catch (Exception ex) {
ex.printStackTrace();
}
return super.findClass(name);
}
}
package com.carl.classloaderdemo;
import java.lang.reflect.Method;
public class ClassloaderTest {
public static void main(String[] args) {
MyClassloader2 myClassloader = new MyClassloader2("E:\\devworkspace\\170807\\TestJar\\bin\\com\\carl\\test\\TestJar.class");
try {
//加载class文件
Class<?> c = myClassloader.loadClass("com.carl.test.TestJar");
if(c != null){
try {
Object obj = c.newInstance();
// Method method = c.getDeclaredMethod("sayHi", null);
Method method = c.getMethod("sayHi", null);
//通过反射调用Test类的say方法
method.invoke(obj, null);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
当我们的工具已经运行起来后,想要再去调用额外的 jar 包的时候,就需要使用 classloader 去加载这些 jar 包,并通过反射调用