How Tomcat Works学习笔记<八>

来源:互联网 发布:米思米标选型软件2016 编辑:程序博客网 时间:2024/04/27 17:22
 

加载器

         在How Tomcat Works的前七章中的应用中已经在使用简单的加载器来加载servlet类,这里将会讨论标准web加载器实现,Tomcat为什么没有使用Tomcat默认的加载器呢?因为如果所有的加载器都是通过jvm默认的加载器来加载的,那么每个servlet就都能够操纵classpath下的所有加载类和包,这会带来安全隐患。一个servlet仅仅允许加载WEB-INF/classes和WEB-INF/lib下的类,在servlet容器中的每一个应用(context)都有它自己的加载器,所有加载器都必须实现org.apache.catalina.Loader接口。

         同时Tomcat需要支持热加载在应用WEB-INF/classes和WEB-INF/lib下修改的类,这就要求Tomcat加载器有一个线程不断的检测类是否有修改(通过检测每个文件的时间来实现)。所有需要实现热加载功能的加载器都必须实现org.apache.catalina.loader.Reloader接口。

         这里会介绍Repository和Resource,repository是加载器搜寻类的地方,resources是一个加载器中DirContext类,指向的是context的基础路径。

Java加载器

         每次创建Java类的实例的时候,你必须首先把类加载到内存当中,在jvm用类加载器(loader)加载类,从j2se1.2开始,jvm提供三个加载器bootstray class加载器、extension class加载器和system class加载器,这个三个加载器是一种父子关系,bootstray class 加载器在最上层,system class加载器在最下层。Bootstray 类加载器用来引导jvm,当你运行java.exe的时候开始工作,必须通过本地代码来实现,因为jvm它需要调用一些本地函数,同时它也负责加载所有java核心类,像java.lang和java.io包中的类,同时也会去加载核心库像rt.jar、il8n.jar等等,同时随着jvm版本和操作系统不同而有所不同;Extension class加载器负责加载扩张目录下的类,sun公司的jvm中的扩展类在/jdk/jre/lib/ext;System class加载器是默认的类加载器,负责加载在CLASSPATH环境变量中指定目录下的jar和java类。

         在选择使用那个加载器的时候,jvm使用了委派模式,当需要加载一个类的时候,system class加载器并不马上开始工作,它会首先委派它的父加载器extension class加载器,如果父加载器加载失败它才会开始工作,同理extension加载类时也会首先委派给它的父加载器bootray加载器,当bootray加载器加载失败时它才会工作,当所有的加载器都没有找到类时就会跑出java.lang.ClassNotFoundException异常。

         之所以使用委派模式是出于安全考虑,避免有人恶意提供一个java.lang.Object,同时java中你可以实现自己的加载器,只需要继承java.lang.ClassLoader即可。

         基于以下的原因Tomcat需要自己的加载器:

1、  按照确定的规则加载类;

2、  缓存已经加载的类;

3、  重新加载已经修改的类。

加载器接口

         Tomcat的加载器准确的说是web应用加载器,需要保证加载的servlet只能够使用WEB-INF/classes和WEB-INF/lib库中的类。所有的加载器必须实现org.apache.catalina.Loader接口。

         Tomcat加载器通常是与Context一起作用的,所以Loader提供了setContainer和getContainer方法,Tomcat需要重新加载已经修改的类,具体加载的工作交给Context去做,但是加载器需要提供一个setReloadable和getReloadable方法,告诉Context是否需要重新加载,这个可以在server.xml文件中进行配置:

         <Context path=”/myApp” docBase=”myApp” debug=”0” reloadable=”true”/>

         加载器接口及其实现类类图如下:

        

Reloader接口

         为了支持自动重新加载的功能,每个加载器都需要实现org.apache.catalina.loader.Reloader接口,其接口定义如下:

         package org.apache.catalina.loader;

public interface Reloader {

        public void addRepository(String repository);

        public String[] findRepositories();

        public boolean modified();

}

    最关键的是modified方法,它在一个servlet或者servlet的支持类修改以后会返回true,addRepository方法为加载器添加一个储藏室。

WebappLoader类

         根据前面的介绍可知,WebappLoader类实现了Loader接口,代表一个web应用加载应用下的所有类,同时它会创建一个org.apache.catalina.loader.WebappClassLoader实例作为它的类加载器,同时它也实现了org.apahe.catalina.Lifecycle接口,能够通过它所在的容器(往往是Context)来启动或停止它,WebappLoader实现Runable接口,用一个线程不断的调用它的类加载器的modified接口,来检测是否有类做了修改,在WebappLoader启动的时候会做下面几件事情:

1、  创建一个类加载器(class loader);

2、  设置它的储藏室;

3、  设置他的类路径;

4、  设置权限;

5、  启动一个线程进行自动加载。

WebappClassLoader类

         org.apache.catalina.loader.WebappClassLoader类是web应用的类加载器,类加载器被优化和安全的,同时它会缓存预加载的类,当需要用到某类的时候它首先会去从缓存中找,没有找到时才会去加载。同时也会缓存查找失败的类,从安全方面考虑,WebappClassLoader不允许加载一些确定的类,这些类被存储在一个叫做triggers的数组当中:

         private static final String[] triggers = {

        "javax.servlet.Servlet"                     // Servlet API

};

同时还有一些包下的类也不允许加载:

    private static final String[] packageTriggers = {

        "javax",                                     // Java extensions

        "org.xml.sax",                               // SAX 1 & 2

        "org.w3c.dom",                               // DOM 1 & 2

        "org.apache.xerces",                         // Xerces 1 & 2

        "org.apache.xalan"                           // Xalan

};

加载器类工作的过程如下:

1、 检测本地缓存(local cache);

2、  如果本地缓存中没有找到,则在缓存中查找,比如调用java.lang.ClassLoader类的findLoadedClass;

3、  如果在两个缓存中都没有找到,这调用系统的加载器,防止覆盖J2EE中的类;

4、  如果使用SecurityManager,如果不允许,抛出ClassNotFoundException;

5、  如果delegate标志打开或者类所在包在触发器中,则调用父加载器,如果没有父加载器则调用系统默认加载器;

6、  从储存器中加载类;

7、  如果当前储存器中类没有找到,并且delegate标志未打开,则调用父类加载器,如父加载器没有找到,则调用系统默认加载器;

8、  如果仍然没有找到类,则抛出ClassNotFoundException异常。

原创粉丝点击