Java隐式使用ClassLoader当你使用new,import关键字时,jvm会使用当前类的类加载器来加载依赖类,所以你可以使用自定义类加载器显式加载一个bootstrap类通过使用classloader.loadclass,bootstrap 只是运行属于您的目标类实例的方法。下面是一个例子。
有一个类Target 依赖于包含在spring-context 中的类DateFormatter,并且有一个名为start 的方法。
import org.springframework.format.datetime.DateFormatter;
public class Target {
private static DateFormatter dateFormatter;
public void start(){
System.out.println(this.getClass().getClassLoader());
dateFormatter=new DateFormatter();
System.out.println(dateFormatter);
}
}
接下来,我们将上面的代码编译打包成一个名为target.jar的jar,存放在D:\\test\\target.jar。
接下来,我们在另一个 jar 中声明一个类 BootStrap,它将调用 Target 实例的方法 start。 BootStrap 类将通过相同的classloader 动态加载target.jar 和spring-context jar 文件,这是一个URLClassLoader 实例。因此,Target 实例中的方法start 可以访问spring-context 中定义的DateFormatter 类。
public class BootStrap {
public static void main(String[] args) throws Exception{
URL url = new URL("http://maven.aliyun.com/nexus/content/groups/public/org/springframework/spring-context/4.3.1.RELEASE/spring-context-4.3.1.RELEASE.jar?spm=0.0.0.0.kG1Pdw&file=spring-context-4.3.1.RELEASE.jar");
URL url2= (new File("D:\\test\\target.jar").toURI().toURL());
URLClassLoader classLoader = new URLClassLoader(new URL[]{url,url2});
Class<?> clz = classLoader.loadClass("com.zhuyiren.Target");
Object main = clz.newInstance();
Method test = clz.getMethod("start");
test.invoke(main);
}
}
最后,运行BootStrap main 方法。有两点很重要:
-
BootStrap 类和Target 类不属于同一个 jar 文件。
-
target.jar 未存储在 CLASSPATH 路径中。
这2点可以确保AppClassLoader无法找到并加载Target类。因为类加载器的机制,jvm会使用自定义加载Target。当然,你可以通过将URLClassLoader classLoader = new URLClassLoader(new URL[]{url,url2});改为URLClassLoader classLoader = new URLClassLoader(new URL[]{url, url2}, ClassLoader.getSystemClassLoader().getParent());
来保证
我们可以看到结果:
java.net.URLClassLoader@e9e54c2
org.springframework.format.datetime.DateFormatter@4dd8dc3
这意味着我们可以成功访问定义在spring-context jar 文件中的DateFormatter 实例,而spring-context 没有存储在CLASSPATH 中,但是我们使用自定义的类加载器来加载和使用它。