(三)自定义加载器 (类加载机制 第三篇)

首先一个问题:为什么要自定义类加载器?

我们大家都知道,java内部已经有3大加载器,可以满足大部分类的加载场景了,为什么还要来自定义加载器呢?这个问题,我们来分析一下当前类加载的一些弊端。

  • 非当前classpath下的类,我们当前的类加载器是无法进行加载的,如果在数据库,在远程服务器上的class,等等

  • 我们当前类为了更加的安全,我们需要对其进行加密操作


以上的问题,当前的类加载是无法处理,所以我们需要自己自定义类加载。

那么怎么自定义类加载器呢?

首先,分析ClassLoader类的加载方法

//双亲委派模型的工作过程源码
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } 
        catch (ClassNotFoundException e) {
            //父类加载器无法完成类加载请求
        }
 
        if (c == null) {
            //子加载器进行类加载 
            c = findClass(name);
        }
    } 
    if (resolve) {
        //判断是否需要链接过程,参数传入
        resolveClass(c);
    }
    return c;
}

这段代码就是双亲委派机制的最核心的代码,上几篇笔者都有讲过,这里就不展开讲了。 今天,主要讲一下**findclass(name)**这个方法。

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

找到这段代码,发现没有实现体,说话这个需要子类来进行实现。

分析到这里,我们得出了一个结论:

从上面源码看出,调用loadClass时会先根据委派模型在父加载器中加载,如果加载失败,则会调用当前加载器的findClass来完成加载。

因此我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法,下面是一个实际例子,在该例中我们用自定义的类加载器去加载我们事先准备好的class文件。

自定义类加载器开始

1 第一步:继承ClassLoader

defineClass方法可以把二进制流字节组成的文件转换为一个java

package com.shendu;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader {
    public MyClassLoader() {

    }

    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
        File file = new File("D:/User.class");
        try{
            byte[] bytes = getClassBytes(file);
            //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class
            Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
            return c;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return super.findClass(name);
    }

    private byte[] getClassBytes(File file) throws IOException {
        
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);

        while (true){
            int i = fc.read(by);
            if (i == 0 || i == -1)
                break;
            by.flip();
            wbc.write(by);
            by.clear();
        }
        fis.close();
        return baos.toByteArray();
    }
}

2 定义user类


public class User {

    private String name = "shendu-sir";

    private Integer age = 18;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }


    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3 把User类放在D盘下面,用javac 命令将User编译成字节码文件 PS:User一定不要放在类路径下,因为双亲委托机制,这个User类会永远的被AppClassLoader加载。 我们自定义的加载器永远加载不到

在这里插入图片描述

4 编写测试类

package com.shendu;

public class JvmTest03 {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader();

        Class<?> user = myClassLoader.loadClass("User");

        Object o = user.newInstance();

        System.out.println(o);

        System.out.println(o.getClass().getClassLoader());

    }
}

打印:

User{name='shendu-sir', age=18}
com.shendu.MyClassLoader@4554617c

Process finished with exit code 0


由此:从打印结果来看,User这个类是由我们自定义类加载器进行加载的。

预告:下一篇,讲解 如何打破双亲委派

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值