JVM双亲委派机制

双亲委派机制

介绍

当类加载器进行加载类的时候,类加载器需要向上委托给上一级的类加载器,上一级继续向上委托,直到启动类加载器。启动类加载器去核心类库中找,如果没有找到该类,则继续向下委派,由下一级扩展类去扩展类库中找,如果也没有继续向下委派,直到找不到,报类未找到异常。

JVM双亲委派机制_第1张图片

为什么要有双亲委派机制

  • 防止核心类库中的类被随意篡改
  • 防止类的重复加载

全盘委托机制

当一个类被当前的ClassLoader加载时,该类中的其他类也会被当前ClassLoader加载,除非指明由其他类加载器加载。

如何打破双亲委派机制

通过上图的分析可知,双亲委派的核心在于当加载一个类时,我先交给父类进行加载,如果父类能把这事办了,那么就不需要我进行加载,否则就由自身加载。核心代码如下(源自URLClassLoader):

注:AppClassLoader和ExtClassLoader都继承了URLClassLoader

static class ExtClassLoader extends URLClassLoader {
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            final File[] var0 = getExtDirs();
static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
public final Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // First check if we have permission to access the package. This
        // should go away once we've added support for exported packages.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
        return super.loadClass(name, resolve);
    }

也就是关键在于super.loadclass(name,resolve)方法。

打破思路

如果我们自定义一个ClassLoader,并且复写其中的loadclass方法,直接自己加载类,而不交给上一级类加载器,那么不就成功打破了吗,同时需要注意的是,loadclass方法为双亲委派机制向上委托的过程,findclass方法则为实际的类加载,所以我们也需要对findclass方法进行重写,咱们对应的.class文件目录。实现如下:

package classloader;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MyClassLoader extends ClassLoader{

    private String classPath;

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

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            String path = name.replace(".","/").concat(".class");
            FileInputStream fileInputStream = new FileInputStream(classPath + "/" + path);
            //available()返回字节流中剩余字节数的估计值
            byte[] data = new byte[fileInputStream.available()];
            fileInputStream.read(data);
            //关闭流
            fileInputStream.close();
            //加载此类
            return defineClass(name,data,0, data.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        } catch (IOException e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> c = findLoadedClass(name);
        if (c == null){
            long t1 = System.nanoTime();
            if (!name.startsWith("classloader")){
                c = this.getParent().loadClass(name);
            }else
                c = findClass(name);
            sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
            sun.misc.PerfCounter.getFindClasses().increment();
        }
        if (resolve){
            resolveClass(c);
        }
        return c;
    }
}

测试

测试类

package classloader;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author yangwei
 * 自定义类加载器,打破双亲委派机制
 * */
public class Test2 {
    public static void main(String[] args) {
        try {
            //字节码文件存放目录
            MyClassLoader loader = new MyClassLoader("D:/work/系统/Prep/out/production/Prep");
			//Class aClass = loader.findClass("classloader.JvmAnalyse");
            //指定要加载的全限定类名
            Class<?> aClass = loader.loadClass("classloader.JvmAnalyse");
            //反射创建对象
            Object instance = aClass.newInstance();
            //创建add方法(我在JvmAnalyse中写了一个method方法)
            Method add = aClass.getDeclaredMethod("method", null);
            //调用对象的add方法
            Object obj = add.invoke(instance);
            //打印结果
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

JvmAnalyse类

package classloader;

public class JvmAnalyse {

    public static int method(){
        int num = 100, e = 2;
        return num * e;
    }
}

你可能感兴趣的:(JVM,java,开发语言,后端)