双亲委派机制
介绍
当类加载器进行加载类的时候,类加载器需要向上委托给上一级的类加载器,上一级继续向上委托,直到启动类加载器。启动类加载器去核心类库中找,如果没有找到该类,则继续向下委派,由下一级扩展类去扩展类库中找,如果也没有继续向下委派,直到找不到,报类未找到异常。
为什么要有双亲委派机制
- 防止核心类库中的类被随意篡改
- 防止类的重复加载
全盘委托机制
当一个类被当前的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;
}
}