如何打破双亲委派机制?

2022-06-15 12:36:22 浏览数 (1)

上文:jdk-Launcher源码学习


背景

上文说过,jdk是通过双亲委派机制实现类的加载,但是这个加载效率及场景存在弊端,所以本文借鉴tomcat的实现方式去打破双亲委派机制实现自定义类加载器来模似tomcat的类加载方式。

实现方式

实现思想:首先继承ClassLoader,然后通过classLoader进行重写findClass实现。

代码语言:javascript复制
package com.classloader.simulate;

import com.encryption.demo.EncryptionUtils;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author hong
 * @version 1.0
 * @description: 模似打破双亲委派机制
 * @date 2022/4/16 18:50
 */
public class SimulateTomcatClassLoader  {

    static class MySelfClassLoader extends ClassLoader{
        /**
         * 类路劲
         **/
        private String classPath;

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

        private byte[] loadByClassName(String className) throws Exception {
            if (StringUtils.isEmpty(className)) {
                throw new Exception("类名不能为空!");
            }
            //替换路劲格式
            className = className.replaceAll("\.", "/");
            //获取文件信息
            FileInputStream fis = new FileInputStream(classPath   "/"   className   ".class");
            //获取长度
            int len = fis.available();
            //文件信息
            byte[] data = new byte[len];
            //读写文件信息
            fis.read(data);
            fis.close();
            return data;

        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByClassName(name);
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

        /**
         * 重写类加载方法,在这里打破双亲委派
         * @param name
         * @param resolve
         * @return
         * @throws ClassNotFoundException
         */
        @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) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //这里重写逻辑 如果不是自定义类开头就用默认类加载器,如果是就有自定义类加载器加载
                    if(!name.startsWith("com.classloader")){
                        c = this.getParent().loadClass(name);
                    }else{
                        c = findClass(name);
                    }

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


    public void test(){
        System.out.println("测试类!");
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //获取项目路劲
        File file = new File(EncryptionUtils.class.getResource("/").getPath());
        MySelfClassLoader mySelfClaasLoader = new MySelfClassLoader(file.getPath());
        Class<?> loadClass = mySelfClaasLoader.loadClass("com.classloader.simulate.SimulateTomcatClassLoader");
        //获取实例对象
        Object obj = loadClass.newInstance();
        //获取方法列表
        Method[] declaredMethods = loadClass.getDeclaredMethods();
        System.out.println(declaredMethods);
        //获取方法
        Method method = loadClass.getMethod("test", null);
        method.invoke(obj,null);
        System.out.println(loadClass.getClassLoader().getClass().getName());;
    }
}

结果

代码语言:javascript复制
测试类!
com.classloader.simulate.SimulateTomcatClassLoader$MySelfClassLoader

最后

很多容器都是打破Jvm的双亲委派机制来实现的,比如tomcat,如果不打破的话根据无法部署多个项目,所以打破双亲委派机制也是一种业务场景的需要。而通过了解和实现打破双亲委派机制来学习tomcat的实现,有效提升对jvm底层的实现了解。

参考文章:

https://www.jianshu.com/p/7706a42ba200

https://cwiki.apache.org/confluence/display/tomcat/FAQ

https://blog.csdn.net/lisheng5218/article/details/111475536

0 人点赞