JVM系列之ClassLoader

类加载器介绍

类加载器负责加载JAVA类的字节码到JAVA虚拟机中,可理解成JVM和字节码代码的桥梁,可以根据指定的类名(如java.lang.Object)来装载class文件的内容到Runtime data area 中的 method area( 方法区域)。

ClassLoader类型

从JVM的角度讲,主要有两种类型加载器:启动类加载器和所有其它的类加载器。启动类加载器是JVM实现的一部分,使用C++语言实现,其它类加载器都由java语言实现,独立于虚拟机外部,并且全部继承抽象类java.lang.ClassLoader。
1、启动类加载器(Bootstrap ClassLoader)
2、其他类加载器(UrlClassLoader、AppClassLoader、ExtClassLoader……)

获取与设置本线程的ClassLoader

//获取ClassLoader
Thread.currentThread().getContextClassLoader();
//设置ClassLoader
Thread.currentThread().setContextClassLoader(ClassLoader);

示例:如maven插件开发,maven中jar都存放在仓库中,插件启动是不会加载到JVM中,这边将需要加载的路径放入到ClassLoader中

/**
     * 将项目的classes加入到插件的ClassLoader中
     * @param project maven 工程信息
     */
    public static void addClassesToClassLoader(MavenProject project) throws
            MojoExecutionException {
        try {
            Set<String> entries = new HashSet<String>();
            //获取所有需编译的元素
            entries.addAll(project.getCompileClasspathElements());
            //获取新的自定义的ClassLoader
            ClassLoader contextClassLoader = getCustomClassloader(new ArrayList<>
                    (entries));
            //设置当前的ClassLoader
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        } catch (DependencyResolutionRequiredException e) {
            throw new MojoExecutionException("Dependency Resolution Required", e);
        }
    }
    private static ClassLoader getCustomClassloader(List<String> entries) {
        List<URL> urls = new ArrayList();
        if (entries != null) {
            //循环获取URL
            Iterator i$ = entries.iterator();
            while(i$.hasNext()) {
                String classPathEntry = (String)i$.next();
                File file = new File(classPathEntry);
                if (!file.exists()) {
                    throw new RuntimeException("RuntimeError.9");
                }
                try {
                    urls.add(file.toURI().toURL());
                } catch (MalformedURLException var6) {
                    throw new RuntimeException("RuntimeError.9");
                }
            }
        }
        //取当前线程的classLoader
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        //新建ClassLoader
        URLClassLoader ucl = new URLClassLoader((URL[])urls.toArray(new
                URL[urls.size()]), parent);
        return ucl;
    }

两个类是否是同一个类

代码演示,通过自定义的ClassLoader加载类,判断是否为同一个的类

public static void main(String[] args) throws Exception {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) +
                            ".class";
                    InputStream is = this.getClass().getResourceAsStream(fileName);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };
        Object customObj = myLoader.loadClass("com.suning.demo.jvm.classloader.ClassLoaderTest").newInstance();
        Object newObj = new ClassLoaderTest();
        System.out.println("new的class:" + newObj.getClass());
        System.out.println("new的class是否为ClassLoaderTest类:"+ (newObj instanceof ClassLoaderTest));

        System.out.println("myLoader加载的class:" + customObj.getClass());
        System.out.println("myLoader加载的class是否为ClassLoaderTest类:"+ (customObj instanceof ClassLoaderTest));
    }

执行结果:

newclassclass com.suning.demo.jvm.classloader.ClassLoaderTest
newclass是否为ClassLoaderTest类:true
myLoader加载的classclass com.suning.demo.jvm.classloader.ClassLoaderTest
myLoader加载的class是否为ClassLoaderTest类:false

可以看出,getClass()返回的值相同,但myLoader加载的class和ClassLoaderTest类不为同一个类

自定义ClassLoader

继承 java.lang.ClassLoader,重写findClass(String)方法

public class OwnDefinedClassLoader extends ClassLoader{
    private String classpath;

    public OwnDefinedClassLoader(String classpath) {
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //a.class格式
        String classFileDir = name.replaceAll("[.]",
                File.separator).concat(".class");
        this.classpath = this.getClasspath().endsWith(File.separator) ?
                this.getClasspath() : this.getClasspath().concat(File.separator);
        //磁盘文件路径
        String classFileUrl = this.getClasspath().concat(classFileDir);
        //获取文件流
        try (InputStream inputStream = new FileInputStream(classFileUrl)) {
            byte[] bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            //调用父类defineClass方法
            return super.defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException(name);
        }
    }

    public String getClasspath() {
        return classpath;
    }

    public static void main(String[] args) throws Exception{
        ClassLoader classLoader = new OwnDefinedClassLoader("E:\\workspace\\demo\\src\\main\\java\\");
        Class<?> aClass1 = classLoader.loadClass("com.suning.demo.entity.Book");
        Object instance = aClass1.newInstance();
        System.out.println(instance);
    }
}

执行结果:

com.suning.demo.entity.Book@5a2e4553

问答

1、“字节码代码”来源渠道有哪些?
“字节码代码”来源并非只能来自磁盘中的.class文件,它是一串二进制的字节流,无论以何种形式存在都可以。 jar、zip、网络传输、jsp文件等都可以是来源。
2、类A中引用了类B,类B将由哪个类加载器来加载?
如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B。
3、如何判断两个类相同?
相同类加载器、相同的全限定名(包路径+类名)。
4、如何获取/指定本线程中的类加载器?
Thread#get/setContextClassLoader(ClassLoader cl)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值