【自己动手写JVM】04.类文件与寻路之旅(代码实现ClassLoader)

188 阅读5分钟

概览

上一篇文章中,bb了一长串类加载器寻找原理,现在终于开始动手写啦!在这篇文章中,将会创建一个ClassLoader.cpp文件,编写代码实现如下功能
1.编写类文件搜寻器,在扫描类路径下的文件,通过全限定类名,找到相应的类文件
2.编写启动、扩展、应用类加载器,实现双亲委派机制

提前说明:代码均来自本人已经实现的jvm的源码里面。为了方便讲解会有所删改

涉及内容

类加载器的实现、双亲委派机制、全限定类名判断

一、代码实现

1.准备工作

创建一个CPP文件,就叫ClassLoader(类加载器)好了。关于类文件加载的所有代码都放在这个文件下。

2.类文件搜寻器

类文件搜寻器的作用就是遍历某路径下的文件,根据特定条件查找文件。如果找到该文件,则返回路径。这里我们降低难度,不从压缩包里面找.class文件(毕竟要导入第三方库),也不判断包名,通过类名.class查找文件就行了
这个简单,不就是获取该路径下的所有子目录和文件,判断文件是否符合要求,对子目录进行查找么。递归就完事了(递归yyds!

string fileFinder(string path, string className){


  //文件句柄
  long hFile = 0;
  //文件信息
  struct _finddata_t fileinfo; 
  string p; 
  if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
    do {
      if ((fileinfo.attrib & _A_SUBDIR)) { //比较文件类型是否是文件夹
        if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) {
          //递归搜索
          return fileFinder(p.assign(path).append("\\").append(fileinfo.name), className);
        }
      }
      else {
		  if(className == fileinfo.name){
			  return p.assign(path).append("\\").append(fileinfo.name);
		  }
      }
    } while (_findnext(hFile, &fileinfo) == 0); //寻找下一个,成功返回0,否则-1
    _findclose(hFile);
  }
  
	return "";
}

3.类加载器与双亲委派

编写三个类:BootstrapClassLoader、ExtensionClassLoader、ApplicationClassLoader。其中ApplicationClassLoader继承ExtensionClassLoader,ExtensionClassLoader继承BootstrapClassLoader。加载类时首先调用父类的方法,若父类未找到,则扫描自己的类路径去寻找。
其中BootstrapClassLoader的类路径为E:\jdk_sf-1.3.1\jre\lib
ExtensionClassLoader的类路径为E:\jdk_sf-1.3.1\jre\ext
ApplicationClassLoader的类路径为E:\jdk_sf-1.3.1\jre\app1E:\jdk_sf-1.3.1\jre\app2
这里为了方便观察,添加了一些输出语句进行测试。

class BootstrapClassLoader{
	vector<string> bootPaths;

	public:
                //以后改成读取配置文件的方式
		BootstrapClassLoader(){
			bootPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\lib");
		}

		string loadClass(string className){
			string path;

			for(int i=0;i<bootPaths.size();++i){
				path = fileFinder(bootPaths[i], className);

			  if(!(path == "")){
				printf("BootstrapClassLoader : 扫描ClassPath %s 加载类 %s 成功,路径 %s \n",bootPaths[i].c_str(),className.c_str(),path.c_str());
				return path;
			  }else{
				printf("BootstrapClassLoader : 扫描ClassPath %s 加载类 %s 失败 \n",bootPaths[i].c_str(),className.c_str());
			  }
			}
			return path;
		}
};

class ExtensionClassLoader : public BootstrapClassLoader{
	vector<string> extPaths;
	BootstrapClassLoader* bootLoader;

	public:
                //以后改成读取配置文件的方式
		ExtensionClassLoader(){
			bootLoader = new BootstrapClassLoader();
			extPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\ext");
		}

	string loadClass(string className){

			printf("ExtensionClassLoader : 委托 BootstrapClassLoader 加载类 %s\n",className.c_str());
			string path =BootstrapClassLoader::loadClass(className);
			if(!(path == "")){
				printf("ExtensionClassLoader : 委托 BootstrapClassLoader 加载类 %s成功! 路径 %s\n",className.c_str(), path.c_str());
				return path;
			}else{
				printf("ExtensionClassLoader : 委托 BootstrapClassLoader 加载类 %s失败!\n",className.c_str());
			}



			for(int i=0;i<extPaths.size();++i){
				path = fileFinder(extPaths[i], className);
				if(!(path == "")){
					printf("ExtensionClassLoader : 扫描ClassPath %s 加载类 %s 成功,路径 %s \n",extPaths[i].c_str(),className.c_str(),path.c_str());
					return path;
				}else{
					printf("ExtensionClassLoader : 扫描ClassPath %s 加载类 %s 失败 \n",extPaths[i].c_str(),className.c_str());
				}
			}
			return "";
		}
};

class ApplicationClassLoader : public ExtensionClassLoader{
	vector<string> appPaths;

	public:
                //以后改成读取配置文件的方式
		ApplicationClassLoader(){
			appPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\app1");
			appPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\app2");
		}

		string loadClass(string className){

			printf("ApplicationClassLoader : 委托 ExtensionClassLoader 加载类 %s\n",className.c_str());
			string path = ExtensionClassLoader::loadClass(className);
			if(!(path == "")){
				printf("ApplicationClassLoader : 委托 ExtensionClassLoader 加载类 %s成功! 路径 %s\n",className.c_str(), path.c_str());
				return path;
			}else{
				printf("ApplicationClassLoader : 委托 ExtensionClassLoader 加载类 %s失败!\n",className.c_str());
			}


			for(int i=0;i<appPaths.size();++i){
				path = fileFinder(appPaths[i], className);
				if(!(path == "")){
					printf("ApplicationClassLoader : 扫描ClassPath %s 加载类 %s 成功,路径 %s \n",appPaths[i].c_str(),className.c_str(),path.c_str());
					return path;
				}else{
					printf("ApplicationClassLoader : 扫描ClassPath %s 加载类 %s 失败 \n",appPaths[i].c_str(),className.c_str());
				}
			}
			return "";
		}
};

4.代码测试

在ClassLoader.cpp里编写一个main函数,测试一下我们的类加载器。

样例1 —— 测试双亲委派机制查找流程

E:\jdk_sf-1.3.1\jre\ext\Test下创建Aclass.class,

image.png

使用加载器搜寻该文件:

int main(){
	ApplicationClassLoader* loader = new ApplicationClassLoader();
	string path = loader->loadClass("Aclass.class");
	return 0;
}

可以看到该文件被ExtensionClassLoader成功找到: image.png

样例2 —— 查找一个在所有类路径下都不存在的类NotFind.class

int main(){
	ApplicationClassLoader* loader = new ApplicationClassLoader();
	string path = loader->loadClass("NotFind.class");
	return 0;
}

类加载器穷尽一切可能也找不到这个类。万恶的ClassNotFound异常要出来啦 image.png

样例3 —— 测试双亲委派核心类库防篡改

E:\jdk_sf-1.3.1\jre\libE:\jdk_sf-1.3.1\jre\app2下都创建Test.class

image.png image.png

int main(){
	ApplicationClassLoader* loader = new ApplicationClassLoader();
	string path = loader->loadClass("Test1.class");
	return 0;
}

可以看到,当E:\jdk_sf-1.3.1\jre\lib下的Test1.class被找到后,类加载器直接无视低级别目录E:\jdk_sf-1.3.1\jre\app2Test1.class,证明双亲委派是成功的。

image.png

结语与代码整合

本篇文章我们初步实现了类加载器的功能,虽然没有实现类文件的包名判断jar文件下类文件的获取,而且类路径写死在代码,而不是做成配置文件,也不太规范。但对类加载器的主要功能并不影响,这些可以后续改进。
把ClassLoader.cpp代码整合一下,先把输出语句注释掉,ClassLoader不输出寻找过程,只输出找到文件的路径

#include <stdio.h>
#include <io.h>
#include <string>
#include <vector>
#include <fstream>

using namespace std;

/**
 * 类搜寻器 
 * 
 **/
string fileFinder(string path, string className){


  //文件句柄
  long hFile = 0;
  //文件信息
  struct _finddata_t fileinfo; 
  string p; 
  if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
    do {
      if ((fileinfo.attrib & _A_SUBDIR)) { //比较文件类型是否是文件夹
        if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) {
          //递归搜索
          return fileFinder(p.assign(path).append("\\").append(fileinfo.name), className);
        }
      }
      else {
		  if(className == fileinfo.name){
			  return p.assign(path).append("\\").append(fileinfo.name);
		  }
      }
    } while (_findnext(hFile, &fileinfo) == 0); //寻找下一个,成功返回0,否则-1
    _findclose(hFile);
  }
  
	return "";
}

class BootstrapClassLoader{
	vector<string> bootPaths;

	public:
		//以后改成读取配置文件的方式
		BootstrapClassLoader(){
			bootPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\lib");
		}

		string loadClass(string className){
			string path;

			for(int i=0;i<bootPaths.size();++i){
				path = fileFinder(bootPaths[i], className);

			  if(!(path == "")){
				//printf("BootstrapClassLoader : 扫描ClassPath %s 加载类 %s 成功,路径 %s \n",bootPaths[i].c_str(),className.c_str(),path.c_str());
				return path;
			  }else{
				//printf("BootstrapClassLoader : 扫描ClassPath %s 加载类 %s 失败 \n",bootPaths[i].c_str(),className.c_str());
			  }
			}
			return path;
		}
};

class ExtensionClassLoader : public BootstrapClassLoader{
	vector<string> extPaths;
	BootstrapClassLoader* bootLoader;

	public:
		//以后改成读取配置文件的方式
		ExtensionClassLoader(){
			bootLoader = new BootstrapClassLoader();
			extPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\ext");
		}

	string loadClass(string className){

			//printf("ExtensionClassLoader : 委托 BootstrapClassLoader 加载类 %s\n",className.c_str());
			string path =BootstrapClassLoader::loadClass(className);
			if(!(path == "")){
				//printf("ExtensionClassLoader : 委托 BootstrapClassLoader 加载类 %s成功! 路径 %s\n",className.c_str(), path.c_str());
				return path;
			}else{
				//printf("ExtensionClassLoader : 委托 BootstrapClassLoader 加载类 %s失败!\n",className.c_str());
			}



			for(int i=0;i<extPaths.size();++i){
				path = fileFinder(extPaths[i], className);
				if(!(path == "")){
					//printf("ExtensionClassLoader : 扫描ClassPath %s 加载类 %s 成功,路径 %s \n",extPaths[i].c_str(),className.c_str(),path.c_str());
					return path;
				}else{
					//printf("ExtensionClassLoader : 扫描ClassPath %s 加载类 %s 失败 \n",extPaths[i].c_str(),className.c_str());
				}
			}
			return "";
		}
};

class ApplicationClassLoader : public ExtensionClassLoader{
	vector<string> appPaths;

	public:
		//以后改成读取配置文件的方式
		ApplicationClassLoader(){
			appPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\app1");
			appPaths.push_back("E:\\jdk_sf-1.3.1\\jre\\app2");
		}

		string loadClass(string className){

			//printf("ApplicationClassLoader : 委托 ExtensionClassLoader 加载类 %s\n",className.c_str());
			string path = ExtensionClassLoader::loadClass(className);
			if(!(path == "")){
				//printf("ApplicationClassLoader : 委托 ExtensionClassLoader 加载类 %s成功! 路径 %s\n",className.c_str(), path.c_str());
				return path;
			}else{
				//printf("ApplicationClassLoader : 委托 ExtensionClassLoader 加载类 %s失败!\n",className.c_str());
			}


			for(int i=0;i<appPaths.size();++i){
				path = fileFinder(appPaths[i], className);
				if(!(path == "")){
					//printf("ApplicationClassLoader : 扫描ClassPath %s 加载类 %s 成功,路径 %s \n",appPaths[i].c_str(),className.c_str(),path.c_str());
					return path;
				}else{
					//printf("ApplicationClassLoader : 扫描ClassPath %s 加载类 %s 失败 \n",appPaths[i].c_str(),className.c_str());
				}
			}
			return "";
		}
};

int main(){
	ApplicationClassLoader* loader = new ApplicationClassLoader();
	string path = loader->loadClass("Test1.class");
	printf("%s\n",path.c_str());
	return 0;
}