自定义类加载器 与 热加载

项目:MyMain:

package lc;

/**
 * Created by sunyuming on 19/1/14.
 */
public class B {}

 

package lc;

/**
 * Created by sunyuming on 19/1/14.
 */
public class A {

    static {
        System.out.println(" A 加载");
    }

    public static void teststatic() {
        ;
    }

    public A() {
        // 隐式加载B
        B b = new B();

        // 打印B的类加载器
        System.out.println("B:--" + b.getClass().getClassLoader());
    }
}

 

public class Main {

    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException {


//        System.out.println(System.getProperty("java.class.path"));
//        System.out.println(System.getProperty("user.dir"));

        String dir = "file:/Users/sunyuming/Documents/tool/jars//MySub-1.0.0-jar-with-dependencies.jar";
        URL url = new URL(dir);
        URL[] urls2 = {url};

        // 若不指定parent参数,则默认由系统类加载器担任自定义类加载器的父加载器,输出parent:sun.misc.Launcher$AppClassLoader@3764951d
        MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2);
        System.out.println("parent:--" + myUrlClassLoader.getParent());

        // 由于A在父加载器(系统类加载器)classpath下,根据双亲委派,优先由父加载器加载,输出A:sun.misc.Launcher$AppClassLoader@3764951d
        Class CA = myUrlClassLoader.loadClass("lc.A");
        System.out.println("A:--" + CA.getClassLoader());

        CA.newInstance();

        // 打破双亲委派机制,直接使用findclass绕开从父加载器寻找并由父加载器加载这一步,输出A:lc.Main$MyUrlClassLoader@5acf9800
        // 当类加载请求到来时,先从缓存中查找该类对象,如果存在直接返回,如果不存在则交给该类加载去的父加载器去加载,倘若没有父加载则交给顶级启动类加载器去加载,最后倘若仍没有找到,则使用findClass()方法去加载
        CA = myUrlClassLoader.findClass("lc.A");
        System.out.println("A:--" + CA.getClassLoader());

        // 实例化A,此时触发A的static代码块和B的加载,输出A:lc.Main$MyUrlClassLoader@5acf9800
        // 由于B在父加载器classpath下,优先由系统类加载器加载,输出B:sun.misc.Launcher$AppClassLoader@3764951d
        CA.newInstance();
    }


    private static class MyUrlClassLoader extends URLClassLoader {

        public MyUrlClassLoader(URL[] urls) {
            super(urls);
        }

/**
* 改写protected,使同包可访问findClass
* 注意,java的protected与c++不同,同包和子类都可访问
* @param name
* @return
* @throws ClassNotFoundException
*/

        @Override
        protected Class<?> findClass(final String name) throws ClassNotFoundException {
            return super.findClass(name);
        }
    }
}

 

项目:MySub

 

package lc;

/**
 * Created by sunyuming on 19/1/14.
 */
public class B {}

 

package lc;

/**
 * Created by sunyuming on 19/1/14.
 */
public class A {

    static {
        System.out.println(" A 加载");
    }

    public static void teststatic() {
        ;
    }

    public A() {
        // 隐式加载B
        B b = new B();

        // 打印B的类加载器
        System.out.println("B:--" + b.getClass().getClassLoader());
    }
}

 

mvn clean package
 cp MySub/target/MySub-1.0.0-jar-with-dependencies.jar ~/Documents/tool/jars/

 

输出:

parent:--sun.misc.Launcher$AppClassLoader@49476842
A:--sun.misc.Launcher$AppClassLoader@49476842
父 A 加载
B:--sun.misc.Launcher$AppClassLoader@49476842
A:--lc.Main$MyUrlClassLoader@4517d9a3
子 A 加载
B:--sun.misc.Launcher$AppClassLoader@49476842

 

显式以自定义加载器加载~/Downloads/jars/中的jar包,模拟热加载jar包

实例化A

在A中以new的方式引用B,虚拟机隐式加载类B

依据双亲委派,首先委托AppClassLoader寻找classpath,找到后直接加载

若B也要实现热加载,必须:

myUrlClassLoader.findClass("lc.B");

 

因此:

虽然findclass能够在需要被热加载的jar包顶层入口类,完成热加载,但是其中的众多引用类已被AppClassLoader加载,若不一一指定自定义加载器并且使用findClass,则无法实现热加载,因此仍然建议将待热加载的jar包隔离在上层类加载器classpath之外,使之不可见,这样,其中的众多隐式加载也会被自定义类加载器所进行,从而实现热加载。

 

2019.12.19补充

快捷方式:指定线程的类加载器也许可以? 

link:new操作时调用当前线程的类加载器,还是调用方的类加载器 /  两种类别的类加载器

 

posted on 2019-01-14 23:31  silyvin  阅读(426)  评论(0编辑  收藏  举报