Custom ClassLoader, how to use?

Minisha picture Minisha · Aug 13, 2017 · Viewed 10.9k times · Source

I am trying to use a custom class loader to load all the dependencies needed for the application. I've implemented the customerClassLoader following the site: https://www.javacodegeeks.com/2013/03/java-handmade-classloader-isolation.html

However, I dont understand how to tell my application to use the custom classLoader whenever needed.

For instance: Lets say, I have a method to make http request like below. How can I tell the application to use the custom classLoader to load the required jars?

private HttpResponse get() {
    HttpClient client = HttpClientBuilder.create().build();
    HttpGet request = new HttpGet(url);
    HttpResponse response = client.execute(request);
    return response;
}

Answer

dabaicai picture dabaicai · Aug 13, 2017

Java uses ClassLoader implicitly when you use new, import keyword, the jvm will use the current class's classloader to load the dependent classes, so you can use the custom classloader to load a bootstrap class explicitly by using classloader.loadclass, and the bootstrap just runs a method belonging to your target class instance. An example follows.

There is a class Target that depends on the class DateFormatter which is included in the spring-context, and has a method named 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);
    }
}

Next, we compile and package the above code as a jar named target.jar, which is stored at D:\\test\\target.jar.

Next, we declare a class BootStrap in another jar that will call the method start of Target instance. The BootStrap class will dynamically load the target.jar and spring-context jar files by the same classloader which is a URLClassLoader instance. Because of this, the method start in Target instance can access the DateFormatter class that is defined in spring-context.

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);
    }
}

Finally, run the BootStrap main method. There are two important thing:

  1. The BootStrap class and Target class don't belong to a same jar file.
  2. The target.jar is not stored in CLASSPATH path.

These 2 point can make sure that the AppClassLoader can not find and load the Target class. Because of the mechanism of class loader, jvm will use the custom load the Target. Of course, you can guarantee it by changing the URLClassLoader classLoader = new URLClassLoader(new URL[]{url,url2}); to URLClassLoader classLoader = new URLClassLoader(new URL[]{url, url2}, ClassLoader.getSystemClassLoader().getParent());

And we can see the result:

java.net.URLClassLoader@e9e54c2
org.springframework.format.datetime.DateFormatter@4dd8dc3

That means we can access the DateFormatter instance which is defined in spring-context jar file successfully, while the spring-context is not stored in CLASSPATH, but we are using the custom clasloader to load and use it.