URLClassLoader 加载同名类

前言

           URLClassLoader作为最常用、使用最广泛的类加载器,在java世界中扮演着举足轻重的角色。作为码农有必要对它的功能和用途有一定的了解,才能在java的世界里自在遨游。类加载器作为java中核心的要素,遇到一些疑难杂症时从它入手总能找到解决方法。相信小伙伴们经常会遇到jar包冲突的问题,这种情况下一般解决办法就是去掉其它版本的jar包最终只保留一个版本。那么能不能多个版本共存呢,答案是肯定的。问题还是出在类加载器身上,由于双亲委托机制的存在,对于包含相同类名的jar包,class类实例只会选择优先被加载的jar包,后者则会被无视。如果要实现多版本jar包共存的目的,只能让这些jar包分别被不同的类加载器加载。

一、系统类加载器 AppClassLoader和URLClassLoader

       一般的,系统类加载器是AppClassLoader,当然也可以通过jvm选项Djava.system.class.loader。AppClassLoader类继承于URLClassLoader类,URLClassLoader加载的路径包含 文件目录、jar包、网络路径,大多数情况下我们使用的是前两者。

       一般通过在URLClassLoader的类构造器中指定URL数组来初始化路径的加载,当然也可以使用反射的方式来动态加载路径。反射方式也有两种,其一是通过URLClassLoader的成员变量ucp的addURL方法加载,其二是直接通过URLClassLoader类本身的addURL进行加载。 AppClassLoader还可以通过 appendToClassPathForInstrumentation(String) 方法进行加载

当然本质还是调用了URLClassLoader的addURL来加载。

二、如何加载同名类

       1、创建URLClassLoader类的子类,并重写loadClass方法,并且维护一个集合类的变量。

当要加载的类名已经在集合变量中存在,就跳过从父加载器加载的步骤。

             因为类加载器都继承自ClassLoader,下面是它的loadClass方法的实现

      

      在父加载器中加载失败时会转到findClass方法,所以我们可以在重载后的loadClass方法中去掉父加载器加载的过程。

MyURLClassLoader类完整代码如下

package com.suntown.loader;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public abstract class MyURLClassLoader extends URLClassLoader {
    public MyURLClassLoader(URL[] urls, ClassLoader classLoader) {
        super(urls, classLoader);

        filter(urls);
    }

    public MyURLClassLoader(URL[] urls) {
        super(urls);

        filter(urls);
    }

    public MyURLClassLoader(URL[] urls, ClassLoader classLoader, URLStreamHandlerFactory urlStreamHandlerFactory) {
        super(urls, classLoader, urlStreamHandlerFactory);

        filter(urls);
    }

    protected void addURL(URL url) {
        super.addURL(url);
        filter(url);
    }

    private Set<String> classNameSet = new HashSet<String>();

    public abstract boolean isOverrideURL(URL url);

    private void filter(URL url){
        if(isOverrideURL(url)){
            try{
                File f = new File(url.getPath());
                if(f.exists()){
                    JarFile jarFile = new JarFile(f.getAbsolutePath());
                    Enumeration<JarEntry> et = jarFile.entries();
                    while (et.hasMoreElements()) {
                        JarEntry ele = et.nextElement();
                        if (ele.getName().endsWith(".class")) {
                            String name = ele.getName().substring(0, ele.getName().length() - 6).replace("/", ".");
                            classNameSet.add(name);
                        }
                    }
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }

    private void filter(URL[] urls){
        for(URL url : urls){
            filter(url);
        }
    }

    public Class loadClass(String name) throws ClassNotFoundException {
        if(classNameSet.contains(name)){
            return super.findClass(name);
        }else{
            return super.loadClass(name);
        }
    }
}

三、运行验证

分别使用AppClassLoader、URLClassLoader和MyURLClassLoader类加载同一个jar包中的一个类

并分别打印该类所属的ClassLoader。

测试代码如下

        

package loader;

import sun.misc.URLClassPath;

import java.io.File;
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;

public class LoaderTest{
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, MalformedURLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
        URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        URLClassLoader userloader = new URLClassLoader(new URL[]{});
        URLClassLoader myloader = new MyURLClassLoader(new URL[]{}) {
            @Override
            public boolean isOverrideURL(URL url) {
                return true;
            }
        };

        Method mdAddURL = URLClassLoader.class.getDeclaredMethod("addURL",java.net.URL.class);
        mdAddURL.setAccessible(true);

        URL url = new File("f:\\git\\test\\ojdbc6.jar").toURI().toURL();
        mdAddURL.invoke(sysloader,new Object[]{url});
        mdAddURL.invoke(userloader,new Object[]{url});
        mdAddURL.invoke(myloader,new Object[]{url});

        Class c1 = sysloader.loadClass("oracle.sql.CHAR");
        System.out.println(c1.getClassLoader());

        Class c2 = userloader.loadClass("oracle.sql.CHAR");
        System.out.println(c2.getClassLoader());

        Class c3 = myloader.loadClass("oracle.sql.CHAR");
        System.out.println(c3.getClassLoader());
    }
}

运行结果如下

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天水麒麟姜伯约

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值