1.openfire的入口main文件在src/java 文件夹下的org.jivesoftware.openfire.starter包中。
2.运行main函数之后openfire会调用start方法,首先是获取到ClassLoader对象。那么什么是ClassLoader对象呢?
下面具体学习ClassLoader的知识。
- 首先ClassLoader作用是加载Class文件到jvm中,供程序使用,java程序可以动态加载类定义,这个动态加载的机制就是通过ClassLoader来实现的。
- ClassLoader 是加载Class文件的(ExtClassLoader和AppClassLoader也在此时被加载),那么ClassLoader又被谁加载呢?是一 个被不是java语言所编写的ClassLoader来加载的,这个ClassLoader就是bootstrapClassLoader(启动类加载 器)。这个加载器在jvm运行的时候加载java核心的api以满足java程序最基本的需求。其中包括用户定义的ClassLoader,用户定义的 ClassLoader就是通过程序创建的ClassLoader,那么也有非程序员创建的ClassLoader,就是jvm自己提供的吧(这句是自己 理解的)。用户自定义的ClassLoader有ExtClassLoader,ExtClassLoader加载java的扩展的api,也就是 /lib/ext中的类。用户自定义的ClassLoader还有AppClassLoader,AppClassLoader用户机器上的 CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序自定义的类由AppClassLoader加载
- ClassLoader 的加载模式:双亲委托模式进行加载。该模式的原理是:某个自定义的ClassLoader加载Class的时候都会先委托他的parnet ClassLoader加载该Class,当parent ClassLoader加载失败,再由当前的ClassLoader加载该Class,但是如果该ClassLoader的parent ClassLoader为null那么该ClassLoader的parent就是bootstrapClassLoader。
-
使用双亲委托模式的优点是:
第一:避免重复加载,当父亲已经加载了该类,那么子ClassLoader就没有必要加载该class了。
第二:安全因素。
3.获取当前类的类类加载器的方法:
public ClassLoader findParentClassLoader(){
//获取父类加载器
ClassLoader parent = Thread.currentThread().getContextClassLoader();
if(parent==null){
parent = this.getClass().getClassLoader();
if(parent==null){
parent = ClassLoader.getSystemClassLoader();
}
}
return parent;
}
4.类加载器的种类:
- bootstrap class Loader(引导类加载器) 用来加载java的核心类库
- extensions class loader(扩展类加载器) 用来加载java的扩展库Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类[ExtClassLoader]
- 系统类加载器(system class loader)Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它[AppClassLoader]
上一节主要学习了jvm的类加载器,这节继续进行,从org.jivesoftware.openfire.starter.ServerStarter文件的第72行进行解读。
System.getProperty("openfire.lib.dir");
上面这句话是什么意思呢,根据字面意思理解应该是获取到当前项目也就是openfire的lib路径
继续往下读,如果存放lib的路径不存在那么就创建一个存放lib的文件夹
同样的通过这个方法可以获取到其他的属性 如下列表
java.version | Java 运行时环境版本 |
java.vendor | Java 运行时环境供应商 |
java.vendor.url | Java 供应商的 URL |
java.home | Java 安装目录 |
java.vm.specification.version | Java 虚拟机规范版本 |
java.vm.specification.vendor | Java 虚拟机规范供应商 |
java.vm.specification.name | Java 虚拟机规范名称 |
java.vm.version | Java 虚拟机实现版本 |
java.vm.vendor | Java 虚拟机实现供应商 |
java.vm.name | Java 虚拟机实现名称 |
java.specification.version | Java 运行时环境规范版本 |
java.specification.vendor | Java 运行时环境规范供应商 |
java.specification.name | Java 运行时环境规范名称 |
java.class.version | Java 类格式版本号 |
java.class.path | Java 类路径 |
java.library.path | 加载库时搜索的路径列表 |
java.io.tmpdir | 默认的临时文件路径 |
java.compiler | 要使用的 JIT 编译器的名称 |
java.ext.dirs | 一个或多个扩展目录的路径 |
os.name | 操作系统的名称 |
os.arch | 操作系统的架构 |
os.version | 操作系统的版本 |
file.separator | 文件分隔符(在 UNIX 系统中是“/”) |
path.separator | 路径分隔符(在 UNIX 系统中是“:”) |
line.separator | 行分隔符(在 UNIX 系统中是“/n”) |
user.name | 用户的账户名称 |
user.home | 用户的主目录 |
user.dir | 用户的当前工作目录 |
上一节我们阅读到了org.jivesoftware.openfire.starter.ServerStarter文件中的第90行,这节继续。
第90行调用unpackArchives(libDir, true);方法。
通过阅读该方法的英文注释大概意思是:转换文件夹中的一些包文件为一个标准的jar文件,在转换jar文件的同时每个被转的包文件就会被删除,如果包文件不存在,那么就什么都不做。
- 过滤文件
带着这句话我们进行阅读。该方法传入了2个参数,第一个是一个lib文件夹,第二个参数是个boolean值true。
File [] packedFiles = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".pack");
}
});
if (packedFiles == null) {
// Do nothing since no .pack files were found
return;
}
以上代码是unpackArchives方法中的146到155行的代码。
上面的第一句话我们大家应该很熟悉,没错,这句话我也经常用,但是我经常用不带参数的方法也就是一般这样用,
File [] packedFiles = libDir.listFiles();
这样是获取到一个文件夹下的所有的文件。
而带参数的根据字面意思大概是过滤文件名称的意思,就是过滤一定规则的文件,而不是显示所有的文件,
过滤用
FilenameFilter
这个接口,一般我们用接口都是通过继承的方法来使用,但是我们现在通过new的方式来使用,其实这种用法还是蛮多的,比如很多注册事件== 很多地方都大量运用了该方法。
但是new接口的时候我们会发现我们就要实现里面所有的方法,少一个方法都不可以。因为
FilenameFilter
接口只有一个方法
accept
所以我们在new的同时就会实现该方法,我们通过该方法直接过滤以某种后缀名的文件就可以了现在我们要列出.pack类型的文件所以我们应该写
return
name.endsWith(
".pack"
);
就可以获取到了。很方便吧。
通过以上代码我们学习一个知识点,那就是获取某个文件夹下的某种格式的文件列表应该用
FilenameFilter
来实现,实现方法是一下代码
File [] packedFiles = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".pack");
}
});
如果没有获取到.pack类型的文件那么什么都不做,直接返回。
-
具体实现把.pack的文件转换为jar文件
上面我们获取到了.pack的文件数据,然后开始遍历该数组,把每个.pack文件转为jar文件。
关键代码如下
InputStream in = new BufferedInputStream(new FileInputStream(packedFile)); JarOutputStream out = new JarOutputStream(new BufferedOutputStream( new FileOutputStream(new File(libDir, jarName)))); Pack200.Unpacker unpacker = Pack200.newUnpacker(); // Print something so the user knows something is happening. if (printStatus) { System.out.print("."); } // Call the unpacker unpacker.unpack(in, out); in.close(); out.close(); packedFile.delete(); unpacked = true;
以上的
packedFile
是遍历每个.pack的文件。就这样把.pack文件转为了jar文件。
第三节中我们阅读了org.jivesoftware.openfire.starter.ServerStarter文件到91行,继续吧!
这节我们跳过108行之前的从108行开始学习,91行到107行相对比较简单。
从第108行到113行主要做了2件事情
第一:加载系统用到的jar包跟zip包到classpath中
第二:通过反射加载org.jivesoftware.openfire.XMPPServer类文件。
一:那么如何加载文件到classpath中呢
- openfire用什么加载文件到classpath中:openfire中用org.jivesoftware.openfire.starter.JiveClassLoader加载文件到classpath中(该类是继承了URLClassLoader)
-
openfire加载文件到classpath的方法:
首先找出jar跟zip类型的文件,代码如下
然后调用父类URLClassLoader的addURL方法加载文件到classpath中,代码如下1 2 3 4 5 6 7 8 9 10 11 12 13 File[] jars = libDir.listFiles(
new
FilenameFilter() {
public
boolean
accept(File dir, String name) {
boolean
accept =
false
;
String smallName = name.toLowerCase();
if
(smallName.endsWith(
".jar"
)) {
accept =
true
;
}
else
if
(smallName.endsWith(
".zip"
)) {
accept =
true
;
}
return
accept;
}
});
1 2 3 4 5 for
(
int
i =
; i < jars.length; i++) {
if
(jars[i].isFile()) {
addURL(jars[i].toURI().toURL());
}
}
二:如何通过反射加载文件
1 2 3 4 5 6 7 8 | |
到这里org.jivesoftware.openfire.starter.ServerStarter文件都阅读完毕,ServerStarter中主要做了如下几件事情:
- 获取classpath路径
- 把.pack文件转换为jar文件
- 获取当前线程的类加载器
- 创建自定义类加载器,并加载jar文件跟zip文件到classpath中
- 利用自定义的类加载器启动org.jivesoftware.openfire.XMPPServer类文件