概览
上一篇文章中,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\app1和E:\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,
使用加载器搜寻该文件:
int main(){
ApplicationClassLoader* loader = new ApplicationClassLoader();
string path = loader->loadClass("Aclass.class");
return 0;
}
可以看到该文件被ExtensionClassLoader成功找到:
样例2 —— 查找一个在所有类路径下都不存在的类NotFind.class
int main(){
ApplicationClassLoader* loader = new ApplicationClassLoader();
string path = loader->loadClass("NotFind.class");
return 0;
}
类加载器穷尽一切可能也找不到这个类。万恶的ClassNotFound异常要出来啦
样例3 —— 测试双亲委派核心类库防篡改
在E:\jdk_sf-1.3.1\jre\lib,E:\jdk_sf-1.3.1\jre\app2下都创建Test.class
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\app2的Test1.class,证明双亲委派是成功的。
结语与代码整合
本篇文章我们初步实现了类加载器的功能,虽然没有实现类文件的包名判断和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;
}