1. 程式人生 > >如何改變類載入的雙親委派模式

如何改變類載入的雙親委派模式

package com.stand.job.common.classload;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import com.stand.job.common.utils.ArrayUtils;

/**
 * 
 * @author chichaofan
 *
 *         如果delegateLoad 為false載入順序如下
 * 
 *         bootstrapsclassload -> extclassload -> 自定義各種類載入器 -> systemclassload
 * 
 *         如果delegateLoad 為true為預設類載入器載入順序如下
 * 
 *         bootstrapsclassload -> extclassload ->systemclassload -> 自定義各種類載入器
 */
public class StandJobClassLoader extends URLClassLoader {
	// 是否遵守雙親模式,預設雙親
	private boolean delegateLoad = true;

	private ClassLoader javaseClassLoader;

	StandJobClassLoader(boolean delegateLoad) {
		super(new URL[] {});
		this.delegateLoad = delegateLoad;
		setJavaseClassLoader();
	}

	StandJobClassLoader(ClassLoader parent, boolean delegateLoad) {
		super(new URL[] {}, parent);
		this.delegateLoad = delegateLoad;
		setJavaseClassLoader();
	}

	// 拷貝tomcat程式碼
	private void setJavaseClassLoader() {

		ClassLoader j = String.class.getClassLoader();
		if (j == null) {
			j = getSystemClassLoader();
			while (j.getParent() != null) {
				j = j.getParent();
			}
		}
		this.javaseClassLoader = j;
	}

	public void addJars(File[] files) {
		if (ArrayUtils.isEmpty(files)) {
			return;
		}

		for (File file : files) {

			addJar(file);

		}
	}

	public void addJar(File file) {

		if (file != null && file.isFile() && file.getName().toLowerCase().endsWith(".jar")) {

			try {
				addURL(file.toURI().toURL());
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}

		}
	}

	protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
		// 如果遵守雙親,呼叫原來
		if (delegateLoad) {

			return super.loadClass(name, resolve);

		}
		Class<?> clazz = null;

		synchronized (getClassLoadingLock(name)) {
			/**
			 * 判斷是否已經載入
			 */
			try {
				clazz = findLoadedClass(name);
				if (clazz != null) {
					if (resolve) {
						resolveClass(clazz);
					}
					return clazz;
				}
			} catch (Throwable e) {
			}
			/**
			 *  先有bootstrapsclassload和 extclassload載入
			 */
			try {
				clazz = javaseClassLoader.loadClass(name);
				if (clazz != null) {
					if (resolve) {
						resolveClass(clazz);
					}
					return clazz;
				}
			} catch (Throwable e) {
			}
			/**
			 * 再是最外層自定義classlaod載入
			 */
			try {
				clazz = super.findClass(name);

				if (clazz != null) {
					if (resolve) {
						resolveClass(clazz);
					}
					return clazz;
				}
			} catch (Throwable e) {
			}
			/**
			 * 最外層載入不了,呼叫預設後續的載入器繼續載入
			 */
			try {
				clazz = super.loadClass(name, resolve);
				if (clazz != null) {
					if (resolve) {
						resolveClass(clazz);
					}
					return clazz;
				}
			} catch (Throwable e) {
			}
			/**
			 * 載入不了跑出異常
			 */
			throw new ClassNotFoundException();
		}
	}

}

測試例子:

測試類

package com.stand.job.example;

public class JobServiceTest {

	public void run() {
		System.out.println("hello world" + getClass().getClassLoader().getClass());
	}

}

測試main,在當前測試專案中加入以上jar包,執行以下程式碼

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		File file = new File("/Users/chichaofan/Documents/doc/workspace/example/target/");
		File[] files = file.listFiles(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {

				if (name.toLowerCase().endsWith(".jar")) {

					return true;
				}
				return false;

			}
		});
		for (File file2 : files) {
			StandJobClassLoader jobClassLoader = StandJobClassLoaderFactory.newInstance(false);

			jobClassLoader.addJar(file2);
			Object o = jobClassLoader.loadClass("com.stand.job.example.JobServiceTest").newInstance();
			ReflectUtils.findMethod(o.getClass(), "run").invoke(o);
		}

	}

當delegateLoad為false時輸出

hello worldclass com.stand.job.common.classload.StandJobClassLoader

當delegateLoad為true時輸出

hello worldclass sun.misc.Launcher$AppClassLoader

 

由此看出當delegateLoad為false時載入器的載入順序被改變了