一、为了学会自定义类加载器,我们需要先了解双亲委派机制的原理
二、自定义类加载器
1.demo结构 如图所示:
2.先编写一个,普通的方法类,供类加载器加载
package com;
public class TwoNum {
//返回两数之和
public int twoNum(Integer a,Integer b){
return a+b;
}
public static void main(String[] args) {
TwoNum twoNum=new TwoNum();
System.out.println(twoNum.twoNum(1,2));
}
}
对这个TwoNum类编译后,放在com文件夹下,因为有双亲委派的限制,必须删除,系统target目录下编译好的TwoNum文件
2.想要自定义类加载器,必须继承ClassLoader类,重写里面的findClass()方法
package com;
import java.io.*;
public class MyClassLoader extends ClassLoader{
private String codePath;
public MyClassLoader(ClassLoader parent, String codePath) {
super(parent);
this.codePath = codePath;
}
public MyClassLoader( String codePath) {
this.codePath = codePath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
BufferedInputStream bis=null;
ByteArrayOutputStream bos=null;
/*
File.separator代表的是//
codePath是 C:\\Users\\19878\\IdeaProjects\\learn\\
假如 测试时传过来的name是 com.TwoNum 是包名+类名
codePath 拼接后是 C:\\Users\\19878\\IdeaProjects\\learn\\com\\TwoNum.class
需要把编译后的TwoNum.class 文件放在项目下自己建个com包,把TwoNum.class放进去
*/
codePath=codePath+name.replace(".", File.separator)+".class";
byte[] bytes=new byte[1024];
int line=0;
try{
//读取编译后的文件
bis=new BufferedInputStream(new FileInputStream(codePath));
bos=new ByteArrayOutputStream();
while((line= bis.read(bytes))!=-1){
bos.write(bytes,0,line);
}
bos.flush();
bytes=bos.toByteArray();
return defineClass(null,bytes,0,bytes.length);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return super.findClass(name);
}
}
3.编写测试类
package com;
import java.lang.reflect.Method;
public class test {
public static void main(String[] args) throws Exception {
String codePath="C:\\Users\\19878\\IdeaProjects\\learn\\";
MyClassLoader myClassLoader=new MyClassLoader(codePath);
Class<?> aClass = myClassLoader.loadClass("com.TwoNum");
System.out.println("测试字节码是由"+aClass.getClassLoader().getClass().getName()+"加载的。。");
//利用反射实例化对象,和调用TwoNum类里面的twoNum方法
Object o = aClass.newInstance();
Method twoNum = aClass.getDeclaredMethod("twoNum", Integer.class, Integer.class);
Object invoke = twoNum.invoke(o, 10, 23);
System.out.println(invoke);
}
}
结果测试正确!
4.上面,依然是存在双亲委派机制的,如果不删除,target目录下系统生成的class文件,依旧会被ApplicationClassLoader先加载,而走不到自定义的类加载器,想要打破双亲委派机制,需要重写loadClass()方法
@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) {
long t1 = System.nanoTime();
//如果包名是com开头的,调用自定义类的findClass方法,否则调用父类的loadClass方法
if(name.startsWith("com")){
c = this.findClass(name);
}else{
c=this.getParent().loadClass(name);
}
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
结果如下,只要是com包下的class文件都是被自定义类加载器,进行加载