从零开始手写Tomcat的教程8节----加载器
- Java的类加载器
- 解答一: servlet只能访问指定目录下的类,类加载器如何实现这种隔离访问机制的呢?
- 解决二: 类加载器如何实现在项目不重启的情况下,对某个发生变化的类进行热更新呢?
- Loader接口
- WebAppLoader类
- 创建类加载器
- 设置仓库
- 设置类路径
- 设置访问权限
- 开启新线程执行类的重新载入
- WebappClassLoader类
- 类缓存
- 载入类
- 应用程序
- 总结
我首先提出几个问题,大家先思考一下,如果都可以想出来,说明对类加载器的掌握程度还算不错:
- servlet只能访问指定目录下的类,类加载器如何实现这种隔离访问机制的呢?
- 类加载器如何实现在项目不重启的情况下,对某个发生变化的类进行热更新呢?
Java的类加载器
对classpath不了解的可以看一下这篇文章----Java中令人困惑的classpath和jar到底是什么鬼?
看到这里,提问: 为什么要执行这样一个循环呢?
当然,不是直接委托给父类加载器,而是先判断该类型是否已经被当前类加载器所加载,如果没有再委托给父类加载器,即一个类只能被同一个类加载器加载一次,但是可以被多个不同的类加载器加载多次,这也引出一个没说的知识点: 类加载器 class对象才能唯一确定一个类
这里类加载器指的是两种情况: 两个类加载器本身class类型都不同; 同一个类加载器的两个实例对象加载出来的类,也被视为不同的类
更多类加载器知识点,可以看一下我之前写过关于类加载器的文章:
JVM第六卷—类加载机制
JVM第八卷—类加载与执行子系统的案例与实战
类加载器如何实现类隔离
解答一: servlet只能访问指定目录下的类,类加载器如何实现这种隔离访问机制的呢?
这里先给出一个解决思路,然后下面我们在来看一下tomcat是如何进行处理的
这里只是给我一个思路方向,这不等于tomcat选择的解决办法
解决二: 类加载器如何实现在项目不重启的情况下,对某个发生变化的类进行热更新呢?
这里类重载技术没那么简单,本节先不讲,感兴趣的,可以等几天,等到12节左右,才会进行类自动重载技术的讲解
Loader接口
WebAppLoader类
创建类加载器
设置仓库
tomcat自定义类加载器会去这些仓库下面寻找需要的servelt类
设置类路径
设置访问权限
这里说的安全管理器指的是java中的SecurityManager,感兴趣的小伙伴可以去了解一下,主要是处理权限管理的,比如对某个文件,资源是否有读写权限等
开启新线程执行类的重新载入
因为检查每个资源是否改动,以及重新加载的过程耗时可能会比较长,并且该过程与当前线程能否继续运行也没有之间关联,因此单开一个线程处理这种事情,是明智的选择
因为这样做对性能会有损耗,因此默认自动重载功能是关闭的。
WebappClassLoader类
类缓存
这样说明了在JVM中提到的方法区进行垃圾回收时执行类卸载动作时,一定要求当前类的class和实例对象不被引用,并且当前类的加载器也不被引用的前提下,该类型才会被卸载。
载入类
delegate的中文是委托的意思,放在这里很合适,表示是否将加载当前类的职责委托给父类加载器完成
应用程序
对StandardContext的分析放在12节进行。
这里我们只需要了解StandardContext类是如何与监听他触发事件的监听器协同工作的,监听器必须实现LifecycleListener接口,并调用StandardContext类的setConfigured()方法。
对于本章应用程序来说,监听器是SimpleContextConfig实例。
代码语言:javascript复制public class SimpleContextConfig implements LifecycleListener{
public void lifecycleEvent(LifecycleEvent event)
{
if(Lifecycle.START_EVENT.equals(event.getType()))
{
Context context=(Context)event.getLifecycle();
context.setConfigured(true);
}
}
}
这里还需要做的就是实例化StandardContext和SimpleContextConfig类,然后调用Lifecycle的addLifecycleListener()方法,通过StandardContext注册SimpleContextConfig.
这里使用PrimitiveServlet类和ModernServlet类进行测试,但是使用了StandardContext类的实例作为Servlet容器,所以servlet类只能放在应用程序目录的WEB-INF/classes目录下。
应用程序目录名称为myApp,其次还需要通知StandardContext实例到哪里查找应用程序目录,需要设置一个"catalina.base"的系统属性,其值为"user.dir"属性的值,如下所示:
代码语言:javascript复制 System.setProperty("catalina.base",System.getProperty("user.dir"));
事实上,上面代码就是BootStrap类的main方法的第一行代码,然后实例化默认连接器:
代码语言:javascript复制Connector connector=new Connector();
然后,与之前章节中应用程序一样,它为两个servlet类创建两个Wrapper实例。
代码语言:javascript复制Wrapper wrapper1=new Wrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2=new Wrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
上面docBase指定的是相对路径,因此会根据应用程序的目录而发生改变,如果指定绝对路径,则不会改变,但是一般都是相对路径
总结
本小节到此基本就结束了,现在让我们再来回顾一下开头提出的问题,这里只回答第一个问题,第二个问题会在12小节进行回答:
servlet只能访问指定目录下的类,类加载器如何实现这种隔离访问机制的呢?