jvm系列文章之类加载器篇
1.java类加载器的分类:
A.自定义类加载器,B.应用程序类加载器,C.扩展类加载器,D.启动类加载器
2.jdk默认的类加载器按照加载顺序加载类的过程为:A类加载器中寻找类,若有则返回该类,否则将它委派给B加载器中寻找已加载的类,若找到则返回,否则B加载器将它委派给C类加载器加载,若找到则返回,否则继续委派给D加载器让它在自己的加载类列表中寻找,若都未找到,则说明该指定的类未被任何一个类加载器加载过,则D类加载器试图在自己的类路径下寻找要加载的类,找到则加载,若类路径下未找到则返回给C类加载器,让它继续加载,同样的逻辑,一直到A类加载器在自己的加载路径下加载,加载完成我们就能通过该类的句柄进行后续的操作
3.自定义类加载器,只需要基础classloader类并重写findclass方法,以下是我写的简单测试:
package mytomcat;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class myclassloader extends ClassLoader{
private static String base = null;
public myclassloader(String base) {
myclassloader.base = base;
}
public static String getBase() {
return base;
}
public static void setBase(String base) {
myclassloader.base = base;
}
public Class<?> findClass(String name){
byte bt[] = null;
try {
bt = getClassFile(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return defineClass(bt, 0, bt.length);
}
private byte[] getClassFile(String name) throws IOException {
BufferedInputStream bs = new BufferedInputStream(new FileInputStream(new File(base+name)));
byte[] b = new byte[bs.available()];
bs.read(b);
bs.close();
return b;
}
public static Object doexecute(Class c,String method) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Object rt = null;
Object ins = c.newInstance();
Method m = c.getDeclaredMethod(method, null);
rt = m.invoke(ins, null);
return rt;
}
public static void usemyclassload(String req[]) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Map<String,String> webappurlsMap = new HashMap();
Map<String,myclassloader> webappClassloaderMap = new HashMap();
Map<String,Map<String,Class<?>>> webappdependMap = new HashMap();
webappurlsMap.put("project","D:\\Repository\\workspaces\\workspace\\project\\src\\" );
webappurlsMap.put("project1","D:\\Repository\\workspaces\\workspace\\project1\\src\\" );
webappurlsMap.forEach((k,v)->{
webappClassloaderMap.put(k, new myclassloader(v));
Map m = new HashMap();
m.put(k, webappClassloaderMap.get(k).findClass("jvm\\test.class"));
webappdependMap.put(k, m);
});
for(int i=0;i<req.length;i++) {
String keys[] = req[i].split(",");
Class c = webappdependMap.get(keys[0]).get(keys[0]);
System.out.println(c.getClassLoader());
doexecute(c, "say");
}
}
public static void usejdkclassload(String req[]) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Map<String,String> webappurlsMap = new HashMap();
Map<String,myclassloader> webappClassloaderMap = new HashMap();
Map<String,Map<String,Class<?>>> webappdependMap = new HashMap();
webappurlsMap.put("project","D:\\Repository\\workspaces\\workspace\\project\\src\\" );
webappurlsMap.put("project1","D:\\Repository\\workspaces\\workspace\\project1\\src\\" );
webappurlsMap.forEach((k,v)->{
Map m = new HashMap();
try {
m.put(k, Class.forName(v+"jvm\\test.class"));
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
webappdependMap.put(k, m);
});
for(int i=0;i<req.length;i++) {
String keys[] = req[i].split(",");
Class c = webappdependMap.get(keys[0]).get(keys[0]);
System.out.println(c.getClassLoader());
doexecute(c, "say");
}
}
public static void main(String[] args) throws Exception{
String req[] = {
"project,jvm.test",
"project,jvm.test",
"project,jvm.test",
"project1,jvm.test",
"project1,jvm.test",
"project1,jvm.test",
};
//使用自定义类加载器执行
usemyclassload(req);
//使用jdk类加载器加载
//usejdkclassload(req);
}
}
3.打破双亲委派:有时候当类的版本升级等在项目中存在相同版本的类,但jvm不允许类路径重复,则如果该类已被加载到jvm中,则其他程序调用时就会直接拿到该版本的类,但我们的初衷是要保证同名称的类多版本共存,这时候我们就有必要打破双亲委派机制,打破的关键就是在自定义的classloader中重写loadclass方法逻辑,不让他执行父加载器中查找过程,就可以跳过双亲委派机制,在自定义类加载器中查找该类,不需要逐层向上委派,若找不到,则直接由该类加载器创建,放入自己的类注册表中,另一个版本的可以再创建类加载器加载进自己的类注册表中,这就是tomcat做的事情,最后类的访问可以根据版本号从不同的类加载器中找出对应版本的类供开发者使用,大致代码如下:
package mytomcat;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class myclassloader2 extends ClassLoader{
private static String base = null;
private List<String> myClassList;
public myclassloader2(String base,List<String> myClassList) {
myclassloader2.base = base;
this.myClassList = myClassList;
}
public static String getBase() {
return base;
}
public static void setBase(String base) {
myclassloader2.base = base;
}
public Class<?> findClass(String name){
byte bt[] = null;
try {
bt = getClassFile(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return defineClass(bt, 0, bt.length);
}
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) {
boolean uses[] = {false};
myClassList.forEach(s->{
uses[0]=name.startsWith(s)?true:false;
});
if(uses[0])
c = findClass(name);
else
c = this.getParent().loadClass(name);
}
//if(name)
if (resolve) {
resolveClass(c);
}
return c;
}
}
private byte[] getClassFile(String name) throws IOException {
BufferedInputStream bs = new BufferedInputStream(new FileInputStream(new File(base+name)));
byte[] b = new byte[bs.available()];
bs.read(b);
bs.close();
return b;
}
public static Object doexecute(Class c,String method) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Object rt = null;
Object ins = c.newInstance();
Method m = c.getDeclaredMethod(method, null);
rt = m.invoke(ins, null);
return rt;
}
public static void usemyclassload(String req[]) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Map<String,String> webappurlsMap = new HashMap();
Map<String,myclassloader2> webappClassloaderMap = new HashMap();
Map<String,Map<String,Class<?>>> webappdependMap = new HashMap();
webappurlsMap.put("project","D:\\Repository\\workspaces\\workspace\\project\\src\\" );
webappurlsMap.put("project1","D:\\Repository\\workspaces\\workspace\\project1\\src\\" );
List<String> rule1 = new ArrayList<String>();
rule1.add("jvm.");
webappurlsMap.forEach((k,v)->{
webappClassloaderMap.put(k, new myclassloader2(v,rule1));
Map m = new HashMap();
m.put(k, webappClassloaderMap.get(k).findClass("jvm\\test.class"));
webappdependMap.put(k, m);
});
for(int i=0;i<req.length;i++) {
String keys[] = req[i].split(",");
Class c = webappdependMap.get(keys[0]).get(keys[0]);
System.out.println(c.getClassLoader());
doexecute(c, "say");
}
}
public static void usejdkclassload(String req[]) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Map<String,String> webappurlsMap = new HashMap();
Map<String,myclassloader2> webappClassloaderMap = new HashMap();
Map<String,Map<String,Class<?>>> webappdependMap = new HashMap();
webappurlsMap.put("project","D:\\Repository\\workspaces\\workspace\\project\\src\\" );
webappurlsMap.put("project1","D:\\Repository\\workspaces\\workspace\\project1\\src\\" );
webappurlsMap.forEach((k,v)->{
Map m = new HashMap();
try {
m.put(k, Class.forName(v+"jvm\\test.class"));
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
webappdependMap.put(k, m);
});
for(int i=0;i<req.length;i++) {
String keys[] = req[i].split(",");
Class c = webappdependMap.get(keys[0]).get(keys[0]);
System.out.println(c.getClassLoader());
doexecute(c, "say");
}
}
public static void main(String[] args) throws Exception{
String req[] = {
"project,jvm.test",
"project,jvm.test",
"project,jvm.test",
"project1,jvm.test",
"project1,jvm.test",
"project1,jvm.test",
};
//使用自定义类加载器执行
usemyclassload(req);
//使用jdk类加载器加载
//usejdkclassload(req);
}
}

浙公网安备 33010602011771号