简体   繁体   English

了解urlclassloader,如何访问已加载的jar的类

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

I am trying to understand how to access/make available a jar file using URLClassLoader. 我试图了解如何使用URLClassLoader访问/提供一个jar文件。

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. 打印输出显示jgroups-3.4.2.Final.jar中已声明的字段,但是随后抛出classnotfound错误。

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 艺术

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 . 首先,您在main中实例化加载程序2次,因此第二个实例与第一个实例无关,并且可能不知道第一个实例加载了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. 此外,您之前已将JChannel定义为loader的成员,因此JRE在启动时应为其定义类定义-否则它不应该知道该字段应该是什么。 I've replaced it with the class you've loaded via the URLClassLoader which you should instantiate in start() . 我已将其替换为您通过URLClassLoader加载的类,您应该在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. 您应该进一步在代码中添加一些错误处理。

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 . 加载程序ClassLoader不可见JChannel类型。 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. 使用新的子类ClassLoader进行加载不会将该类型添加到某些全局类池中。 The loaders are hierarchical with child-parent relationships. 加载程序具有子级与父级关系的层次结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM