善忘技术夹 Logo
善忘技术夹
技术文章 · 2223 阅读

JAVA虚拟机类加载器加载顺序

JAVA虚拟机类加载器加载顺序

默认类加载顺序(父类优先)

Websphere采用的是父类优先的类加载顺序。通过websphere控制台——故障诊断——类装入器查看器 我们可以看到一个应用在websphere上部署完成启动后真正形成的类加载层次: 类加载层次是:

JDK扩展装入器(也就是java类加载器中的扩展加载器(Extensions))——应用程序装入器应用程序加载器(Application)——OSGI(was6.1新特性)装入、引导程序、类保护器——组合类装入器——组合类装入器

改变类加载顺序(应用程序优先)

“应用程序优先”的类加载顺序的结果是: 引导加载器(Bootstrap)——原来最低级的web和module加载器——扩展加载器(Extensions)——应用程序加载器(Application)——was扩展classloader、WAS应用程序类加载器

类加载器有一个重要的属性:委托模式(Delegation Mode,有时也称为加载方式:Classloader mode)。委托模式决定了类加载器在查找一个类的时候,是先查找类加载器自身指定的类路径还是先查找父类加载器上的类路径。 类加载器的委托模式有两个取值:

Parent_First:在加载类的时候,在从类加载器自身的类路径上查找加载类之前,首先尝试在父类加载器的类路径上查找和加载类。 Parent_Last:在加载类的时候,首先尝试从自己的类路径上查找加载类,在找不到的情况下,再尝试父类加载器类路径。

程序实现类改变JAVA的父类优先加载顺序

public class MCFClassLoader extends URLClassLoader {
    public MCFClassLoader(URL[] urls) {
        super(urls);
    }

    public MCFClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    public MCFClassLoader(URL[] urls, ClassLoader parent,
            URLStreamHandlerFactory factory) {
        super(urls, parent, factory);
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        Class c = findLoadedClass(name);
        if (c == null) {
            try {
                c = findClass(name);
            } catch (ClassNotFoundException e) {
                return super.loadClass(name);
            }
        }
        return c;
    }
}

通过上面的ClassLoader就解决了我们遇到的问题。 思考:通过扩展URLClassLoader可以实现好多有趣的功能,如支持多父、支持加载顺序配置等等。 作为补充给出一个使用上面的ClassLoader的示例代码:

public class TestClassLoad {
public static void main(String... args) {
    try {
        // MCFClassLoader mcl = new MCFClassLoader(new URL[] { new URL(
        // \"file:///C:/Users/sily/Desktop/test.jar\") },
        MCFClassLoader mcl = new MCFClassLoader(
                new URL[] {
                        new URL(
                                \"file:///I:/workspace2/spring/target/classes/hello/Father.class\"),
                        new URL(
                                \"file:///I:/workspace2/spring/target/classes/hello/Child.class\") },
                Father.class.getClassLoader());
        Class<Child> c = (Class<Child>) mcl.loadClass(\"hello.Child\");
        Child cc = c.newInstance();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

} 上面的例子说明当jar或.class中有需要加载的类时就从这些jar包里加载,即使所在的container里有同样的类,这样可在一定程度上避免jar包版本冲突的问题! 上面的方式也可以用来程序中实现动态添加jar包,并通过反射调用对应的方法。实现动态加载,不需要重启服务器