understanding urlclassloader, how to access a loaded jar's classes

江枫思渺然 提交于 2019-12-11 19:09:13

问题


I am trying to understand how to access/make available a jar file using URLClassLoader.

Firstly I am loading the jar file with

    package myA;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import org.jgroups.JChannel;



public class loader {

    JChannel channel;

        String user_name=System.getProperty("user.name", "n/a");


        private void start() throws Exception {

            channel=new JChannel(); // use the default config, udp.xml

            channel.connect("ChatCluster");

        }

    public void loadMe()throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        URL classUrl;
        classUrl = new URL("file:///home/myJars/jgroups-3.4.2.Final.jar");
        URL[] classUrls = { classUrl };
        URLClassLoader ucl = new URLClassLoader(classUrls);
        Class<?> c = ucl.loadClass("org.jgroups.JChannel");
        for(Field f: c.getDeclaredFields()) {
            System.out.println("Field name=" + f.getName());
            }
        Object instance = c.newInstance();  
        //Method theMethod = c.getMethod("main");
        //theMethod.invoke(instance);
        }

     public static void main(String[] args) throws Exception {
         new loader().loadMe();
         new  loader().start();

     }
}

the printout shows the declared fields that are in jgroups-3.4.2.Final.jar, however it then throws a classnotfound error.

java -cp myA.jar myA.loader
Field name=DEFAULT_PROTOCOL_STACK
Field name=local_addr
Field name=address_generator
Field name=name
Field name=cluster_name
Field name=my_view
Field name=prot_stack
Field name=state_promise
Field name=state_transfer_supported
Field name=flush_supported
Field name=config
Field name=stats
Field name=sent_msgs
Field name=received_msgs
Field name=sent_bytes
Field name=received_bytes
Field name=probe_handler
Exception in thread "main" java.lang.NoClassDefFoundError: org/jgroups/JChannel
        at myA.loader.start(loader.java:23)
        at myA.loader.main(loader.java:45)
Caused by: java.lang.ClassNotFoundException: org.jgroups.JChannel
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        ... 2 more

I don't understand why the printout shows that the class is loaded but then it is not found?

thx Art


回答1:


Your code has maybe a couple of problems. First, you instantiate the loader 2 times within main, so the second instance is independent from the first one and might not be aware that the first one loaded the class file definition of JChannel.

Moreover you've defined JChannel as a member of loader before, therefore the JRE should require the class definition for it at startup - else it should not know what this field should be. I've replaced it with the class you've loaded via the URLClassLoader which you should instantiate in start().

package myA;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import org.jgroups.JChannel;

public class Loader 
{
    Class<?> clazz;
    String user_name=System.getProperty("user.name", "n/a");

    private void start() throws Exception 
    {
        if (this.clazz == null)
            throw new Exception("Channel class was not loaded properly");

        Object channel  = this.clazz.newInstance(); // use the default config, udp.xml
        Method chatCluster = this.clazz.getDeclaredMethod("connect", new Class[] { String.class });
        chatCluster.invoke(channel, "ChatCluster");
    }

    public void loadMe() throws Exception
    {
        URL classUrl;
        classUrl = new URL("file:///home/myJars/jgroups-3.4.2.Final.jar");
        URL[] classUrls = { classUrl };
        URLClassLoader ucl = new URLClassLoader(classUrls);
        Class<?> c = ucl.loadClass("org.jgroups.JChannel");
        for(Field f: c.getDeclaredFields()) 
        {
            System.out.println("Field name=" + f.getName());
        }
        this.clazz = c;
        Object instance = c.newInstance();  
        //Method theMethod = c.getMethod("main");
        //theMethod.invoke(instance);
     }

     public static void main(String[] args) throws Exception 
     {
         Loader loader = new Loader();
         loader.loadMe();
         loader.start();
     }
}

You should further add some error handling to the code.




回答2:


Exception in thread "main" java.lang.NoClassDefFoundError: org/jgroups/JChannel
    at myA.loader.start(loader.java:23)

The code fails on this line:

       channel=new JChannel(); // use the default config, udp.xml

The type JChannel is not visible to loader's ClassLoader. This will be obvious if you try:

       loader.class
             .getClassLoader()
             .loadClass("org.jgroups.JChannel");

You should not have any compile-time references to a dependency that will not be on the type's classpath at runtime.

Loading with a new child ClassLoader does not add that type to some global class pool. The loaders are hierarchical with child-parent relationships.



来源:https://stackoverflow.com/questions/21073986/understanding-urlclassloader-how-to-access-a-loaded-jars-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!