ClassLoader_类加载器

1.

package com.example.demo10;


public class Demo10Application {

    public static void main(String[] args) {
       Calculator calculator = new Calculator(6000);
       double result = calculator.calculate();
       System.out.println(result);
    }

}
package com.example.demo10;

public class Calculator {

    private int number;

    private double multiple = 1.2;

    public double calculate(){
        return this.number * this.multiple;
    }

    public Calculator() {
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Calculator(int number) {
        this.number = number;
    }
}

运行结果:

 

 

 2.从classpath类路径外加载  Calculator 类

package com.example.demo10;


import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class Demo10Application {

    public static void main(String[] args) throws Exception{

        //创建URL对象
        URL url = new URL("file:C:/Minecloud/IDEA_workspace/myapp.jar");
        //创建一个URLClassLoader,负责加载的范围是 {url}
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {url});
        // 到urlClassLoader所负责的范围 寻早并加载 指定的类
        Class clazz = urlClassLoader.loadClass("com.example.Calculator");
        //调用有参构造创建该类的对象,参数是有参构造的参数列表(区分重载)
        Object obj = clazz.getConstructor(int.class).newInstance(20000);
        //获取指定的方法
        Method method = clazz.getMethod("calculate");
        //调用该方法
        double result = (double) method.invoke(obj);

        System.out.println(result);
    }

}
将Calculator打成jar包放到类路径外
package com.example;

public class Calculator {

    private int number;

    private double multiple = 1.2;

    public double calculate(){
        return this.number * this.multiple;
    }

    public Calculator() {
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Calculator(int number) {
        this.number = number;
    }
}

 

运行程序

 

 

3.修改class文件,自定义类加载器

修改class文件

package com.example.demo10;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Demo {
    public static void main(String[] args) throws Exception{
        //原文件
        File originalFile = new File("C:\\Minecloud\\IDEA_workspace\\springboot02\\myapp\\target\\classes\\com\\example\\Calculator.class");
        FileInputStream originalFis = new FileInputStream(originalFile);

        //修改后的目标文件
        File targetFile = new File("C:\\Minecloud\\IDEA_workspace\\Calculator.class");
        FileOutputStream targetFos = new FileOutputStream(targetFile);

        //原文件头部添加一个字节(值为8),形成目标文件
        int code = 8;
        targetFos.write(code);

        //fis.read() = -1 代表已经读到文件的末尾
        while( (code=originalFis.read()) != -1 ){
            targetFos.write(code);
        }

        originalFis.close();
        targetFos.close();

    }
}

将修改后的class文件打成jar包

 自定义类加载器需要实现ClassLoader 

SecureClassLoader 实现了ClassLoader 

自定义类加载器 继承 SecureClassLoader

自定义类加载器从jar中读取修改后的class文件

package com.example.demo10;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.SecureClassLoader;
import java.util.ArrayList;

public class MyClassLoader extends SecureClassLoader {

    private String jarFile;

    //重写findClass 自定义如何加载类
    @Override
    protected Class<?> findClass(String name) {
        //类名转换为路径
        String classPath = name.replace(".","/").concat(".class");
        int code;
        byte[] b;
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        try{
            //读取class文件 为 byte数组
            URL url = new URL( "jar:file:\\" + this.jarFile + "!/" + classPath);
            InputStream is = url.openStream();
            while ( (code=is.read()) !=   -1 ){
                bs.write( (byte)code );
            }
            b = bs.toByteArray();


            /*
            defineClass将二进制字节流转换成 Class 对象实例
            参数:
            name:字符串类型,表示要装入作为类名的二进制名称。如 com.example.MyClass
            b:byte 数组类型,表示要装入内存的二进制数据。
            off:int 类型,表示数组起始位置的偏移量(offset)。(这里跳过一个字节才能正确解析,)
            len:int 类型,表示要装入的数组长度。
             */
            return this.defineClass(name,b,1,b.length-1);

        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally {
            try {
                bs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public MyClassLoader() {
    }

    public MyClassLoader(String jarFile) {
        this.jarFile = jarFile;
    }

    public String getJarFile() {
        return jarFile;
    }

    public void setJarFile(String jarFile) {
        this.jarFile = jarFile;
    }
}
package com.example.demo10;

import java.lang.reflect.Method;


public class Demo010 {
    public static void main(String[] args) throws Exception{
        //创建自定义的类加载器
        MyClassLoader classLoader = new MyClassLoader("C:\\Minecloud\\IDEA_workspace\\myapp.jar");

        //加载指定类 (loadClass 中会调用 findClass )
        Class clazz = classLoader.loadClass("com.example.Calculator");

        //调用有参构造创建该类的对象,参数是有参构造的参数列表(区分重载)
        Object obj = clazz.getConstructor(int.class).newInstance(20000);
        //获取指定的方法
        Method method = clazz.getMethod("calculate");
        //调用该方法
        double result = (double) method.invoke(obj);

        System.out.println(result);
    }
}

运行结果:

 

4.

打破双亲委派

ClassLoader中的 loadClass

        1.  findLoadedClass(name)从缓存中查找是否已经加载过该类

        2.没有加载过 且 有父加载器,调用父加载器 loadClass,没有父加载器(即当前为Bootstrap类加载器),调用findBootstrapClassOrNull(name) 从Bootstrap类加载器负责的范围中加载所需的类。从父加载器中没有加载到需要的类,再调用 当前类加载器findClass(name) 去加载所需要的类。

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 t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        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.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

自定义类加载器中重写loadClass

package com.example.demo10;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.SecureClassLoader;


public class MyClassLoader extends SecureClassLoader {

    private String jarFile;

    //重写 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){
                //优先调用当前类加载器的 findClass 加载所需要的类
                c = this.findClass(name);
                // 当前类加载器加载不到所需要的类,再让父加载器去加载
                if( c == null  ){
                    c = super.loadClass(name,resolve);
                }
            }
            return c;
        }


    }

    //重写findClass 自定义如何加载类
    @Override
    protected Class<?> findClass(String name) {
        //类名转换为路径
        String classPath = name.replace(".","/").concat(".class");
        int code;
        byte[] b;
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        try{
            //读取class文件 为 byte数组
            URL url = new URL( "jar:file:\\" + this.jarFile + "!/" + classPath);
            InputStream is = url.openStream();
            while ( (code=is.read()) !=   -1 ){
                bs.write( (byte)code );
            }
            b = bs.toByteArray();


            /*
            defineClass将二进制字节流转换成 Class 对象实例
            参数:
            name:字符串类型,表示要装入作为类名的二进制名称。如 com.example.MyClass
            b:byte 数组类型,表示要装入内存的二进制数据。
            off:int 类型,表示数组起始位置的偏移量(offset)。
            len:int 类型,表示要装入的数组长度。
             */
            return this.defineClass(name,b,0,b.length);

        }catch(Exception e){
            return null;
        }finally {
            try {
                bs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public MyClassLoader() {
    }

    public MyClassLoader(String jarFile) {
        this.jarFile = jarFile;
    }

    public String getJarFile() {
        return jarFile;
    }

    public void setJarFile(String jarFile) {
        this.jarFile = jarFile;
    }
}

类路径下的 Calculator类

package com.example;

public class Calculator {

    private int number;

    private double multiple = 1;

    public double calculate(){
        return this.number * this.multiple;
    }

    public Calculator() {
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Calculator(int number) {
        this.number = number;
    }
}

类路径外 jar 中的 Calculator 类

package com.example;

public class Calculator {

    private int number;

    private double multiple = 1.2;

    public double calculate(){
        return this.number * this.multiple;
    }

    public Calculator() {
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Calculator(int number) {
        this.number = number;
    }
}

 main方法

package com.example.demo10;

import java.lang.reflect.Method;


public class Demo010 {
    public static void main(String[] args) throws Exception{
        //创建自定义的类加载器
        MyClassLoader classLoader = new MyClassLoader("C:\\Minecloud\\IDEA_workspace\\myapp.jar");

        //加载指定类 (loadClass 中会调用 findClass )
        Class clazz = classLoader.loadClass("com.example.Calculator");

        //调用有参构造创建该类的对象,参数是有参构造的参数列表(区分重载)
        Object obj = clazz.getConstructor(int.class).newInstance(20000);
        //获取指定的方法
        Method method = clazz.getMethod("calculate");
        //调用该方法
        double result = (double) method.invoke(obj);

        System.out.println(result);
    }
}

结果:加载的是类路径外 jar 中的Calculator 类

 

如果不重写 loadClass 

结果:加载的是类路径下 的Calculator 类

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值