自定义类加载器

一、为了学会自定义类加载器,我们需要先了解双亲委派机制的原理

 

二、自定义类加载器

1.demo结构 如图所示:

2.先编写一个,普通的方法类,供类加载器加载

package com;

public class TwoNum {
    //返回两数之和
    public int twoNum(Integer a,Integer b){
        return a+b;
    }

    public static void main(String[] args) {
        TwoNum twoNum=new TwoNum();
        System.out.println(twoNum.twoNum(1,2));
    }
}

 对这个TwoNum类编译后,放在com文件夹下,因为有双亲委派的限制,必须删除,系统target目录下编译好的TwoNum文件

 

2.想要自定义类加载器,必须继承ClassLoader类,重写里面的findClass()方法

package com;

import java.io.*;

public class MyClassLoader extends ClassLoader{
    private String codePath;

    public MyClassLoader(ClassLoader parent, String codePath) {
        super(parent);
        this.codePath = codePath;
    }
    public MyClassLoader( String codePath) {
        this.codePath = codePath;
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        BufferedInputStream bis=null;
        ByteArrayOutputStream bos=null;
        /*
            File.separator代表的是//
            codePath是 C:\\Users\\19878\\IdeaProjects\\learn\\
            假如 测试时传过来的name是 com.TwoNum 是包名+类名
            codePath 拼接后是 C:\\Users\\19878\\IdeaProjects\\learn\\com\\TwoNum.class
            需要把编译后的TwoNum.class 文件放在项目下自己建个com包,把TwoNum.class放进去
        */
        codePath=codePath+name.replace(".", File.separator)+".class";
        byte[] bytes=new byte[1024];
        int line=0;
        try{
            //读取编译后的文件
            bis=new BufferedInputStream(new FileInputStream(codePath));
            bos=new ByteArrayOutputStream();
            while((line= bis.read(bytes))!=-1){
                bos.write(bytes,0,line);
            }
            bos.flush();
            bytes=bos.toByteArray();
            return defineClass(null,bytes,0,bytes.length);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                bis.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return super.findClass(name);
    }

}

3.编写测试类

package com;

import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws Exception {
        String codePath="C:\\Users\\19878\\IdeaProjects\\learn\\";
        MyClassLoader myClassLoader=new MyClassLoader(codePath);
        Class<?> aClass = myClassLoader.loadClass("com.TwoNum");
        System.out.println("测试字节码是由"+aClass.getClassLoader().getClass().getName()+"加载的。。");
        //利用反射实例化对象,和调用TwoNum类里面的twoNum方法
        Object o = aClass.newInstance();
        Method twoNum = aClass.getDeclaredMethod("twoNum", Integer.class, Integer.class);
        Object invoke = twoNum.invoke(o, 10, 23);
        System.out.println(invoke);
    }
}

结果测试正确!

 

4.上面,依然是存在双亲委派机制的,如果不删除,target目录下系统生成的class文件,依旧会被ApplicationClassLoader先加载,而走不到自定义的类加载器,想要打破双亲委派机制,需要重写loadClass()方法

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t1 = System.nanoTime();
                //如果包名是com开头的,调用自定义类的findClass方法,否则调用父类的loadClass方法
                if(name.startsWith("com")){
                    c = this.findClass(name);
                }else{
                    c=this.getParent().loadClass(name);
                }
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

结果如下,只要是com包下的class文件都是被自定义类加载器,进行加载

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值