一个jar包加载的问题

问题描述

最近在storm上面做开发,碰到一个问题:bolt需要动态加载jar,然后调用新加载jar包的方法。结果发现一个问题,在这个jar中会调用mybatis的函数,结果问题就出现了: mybatis的class无法加载,抛出ClassNotFoundException,
因为storm本身的存在classpath的问题,采取了网友给出的方法,使用assembly的方法打包,但是问题还是无法解决。刚开始怀疑是storm的问题(无知的一种表现),后来仔细走读代码,发现在classloader的时候:

    public static TestIfc load (String jarPath, String mainCls, String args)
    {
        URLClassLoader myClassLoader1 = null;
        try{
               URL url1 = new URL("file:" + jarPath);  
               myClassLoader1 = new URLClassLoader(new URL[] { url1 }, Thread.currentThread()  
                        .getContextClassLoader());  
                Class<?> myClass1 = myClassLoader1.loadClass(mainCls);  
                TestIfc action1 = (TestIfc) myClass1.newInstance();  
                return action1; 
        }
        catch(Exception e)
        {
            e.printStackTrace();

        }finally{
            if(myClassLoader1 != null){
                try {
                    myClassLoader1.close(); //这里不知道为啥加了一个close函数
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return null;

    }

试着将finaly的代码屏蔽掉,问题解决。

分析

在《深入理解Java虚拟机 JVM高级特性与最佳实践》中提到“遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行初始化,则需要先触发初始化”,初始化的时候,就会调用loadClass代码。 而我们的代码抛出的异常也是loadClass抛出。
查看loadClass的源码,网上查找了一下关于这部代码的注 (http://www.cnblogs.com/ericchen/archive/2011/01/15/1936130.html

protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    // 首先检查,jvm中是否已经加载了对应名称的类,findLoadedClass(String )方法实际上是findLoadedClass0方法的wrapped方法,做了检查类名的工

       //作,而findLoadedClass0则是一个native方法,通过底层来查看jvm中的对象。

    Class c = findLoadedClass(name);

    if (c == null) {//类还未加载

        try {

        if (parent != null) {

                    //在类还未加载的情况下,我们首先应该将加载工作交由父classloader来处理。

            c = parent.loadClass(name, false);

        } else {

             //返回一个由bootstrap class loader加载的类,如果不存在就返回null
            c = findBootstrapClassOrNull(name);
        }
        } catch (ClassNotFoundException e) {

                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {

            // If still not found, then invoke findClass in order

            // to find the class.

            c = findClass(name);//这里是我们的入手点,也就是指定我们自己的类加载实现,也就是调用URLClassLoader的findClass()

        }

    }

    if (resolve) {

        resolveClass(c);//用来做类链接操作

    }

    return c;

    }

再次查看URLClassLoader的findClass函数

    protected Class<?> findClass(final String name)
         throws ClassNotFoundException
    {
        try {
            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class>() {
                    public Class run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                         //从ucp中查找到path资源
                        Resource res = ucp.getResource(path, false);   
                            .... 
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
    }

而ucp的值是在URLClassLoader初始化的时候创建的:

    public URLClassLoader(URL[] urls, ClassLoader parent) {
        super(parent);
        ... .... 
        ucp = new URLClassPath(urls);
        this.acc = AccessController.getContext();
    }

而在我们的代码中调用close()函数,将将ucp close掉

    public void close() throws IOException {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkPermission(new RuntimePermission("closeClassLoader"));
        }
        List<IOException> errors = ucp.closeLoaders();

        ... ... 
    }

正是因为ucp的值被close掉,因此导致URLClassLoader在find的时候,无法查找到相应的class,因此抛出异常。

总结

URLClassLoader一般属于用户的cloassloader,而在这个loader中,JAVA为将jar包的信息都存放在ucp的这个URLClassPath中。 而在加载的时候,首先通过双亲委派的模式加载,当父加载器无法加载的时候,会调用用户加载器。 而这个用户加载器,就通过URLClassPath进行查找,如果找到,则加载,无法无法找到则抛出异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值