Tomcat源码解析(7)

载入器

  载入需要的servlet。之前我们都使用系统类加载器。所以servlet可以访问所有的类(包括jvm的JDK)。这样是不安全的。

servlet应该只允许载入WEB-INF/classes目录以及子目录下的类。这就需要我们自定义的载入器,定义某种规则来载入类。

  Catalina中,载入器是org.apache.catalina.Loader接口的实例。

  Tomcat需要实现自定义载入器。1.为了提供自动重载的功能,即当WEB-INF/classes目录或者WEB-INF/lib目录下的类发生变化的时候,web会重新载入这些类。(在实现中,类载入器使用一个额外的线程来不断检查servlet类和其他文件的时间戳) 如果支持自动重载功能,则载入器必须实现org.apache.catalina.loader.Reloader接口。

  

JAVA类载入器

 我们每次创建Java类的实例时,必须将类载入到内存中。JVM使用类载入器来载入需要的类。

 一般情况下,类载入器会在一些Java核心类库,以及环境变量CLASSPATH指明的目录搜索相关类。

 java类加载器,从上到下,引导类加载器、扩展类加载器、系统类加载器。

 引导类加载器,用于引导启动Java虚拟机。当调用javax.exe程序时,就会启动引导类加载器,它是使用本地代码实现的。因为用来载入允许JVM需要的类,和java核心类(java.lang、java.id)。启动类加载器会在rt.jar和i18n.jar等java包搜索要载入的类。

 扩展类加载器,负责载入标准扩展目录中的类。我们只需要将jar文件复制到扩展目录,就可以被类加载器搜索到。/jdk/jre/lib/ext

 系统类加载器,是默认的类加载器,他会搜索环境变量CLASSPATH中指明的路径和jar文件。


 具体我们使用哪个类加载器,根据其代理模型,可以解决类载入过程的安全问题。

 就是说,我们在载入一个类的时候,先调用系统类加载器。但不会立即载入这个类。相反,他会将载入类的任务交给父类加载器,扩展类加载器。而扩展类加载器会交给引导类加载器。 所以,引导类加载器,一直是 首先执行类加载任务。如果找到则放返回,如果引导类找不到这个类,则往下继续找。如果到最后系统类加载器都没有找到。则抛出ClassNotFoundException异常。

 为什么要执行这一循环呢?

 代理模型,最重要的用途就是解决安全问题。(防止我们破坏JAVA核心代码,自己写一个String类。)

 比如:我们使用安全管理器来限制某类的访问路径。你编写一个叫做java.lang.Object的类,该类可以访问硬盘任何目录。由于JVM是信任java.lang.Object类的,所以它不会监视这个类的活动。引入代理模型后。

 当某个地方调用java.lang.Object类时,先有引导类来加载,这样我们自定义的类,就不会被加载。

 

 Tomcat的类加载器,通过继承java.lang.ClassLoader类,自定义的。

  1. 在类载入器定一些规则。  2.为了缓存已经载入的类   3.实现类的预载入,方便使用


Loader接口(org.apache.catalina.Loader)

  servlet只能引用部署在WEB-INF/classes目录及其子目录下的类。

  servlet只能访问WEB-INF/lib目录下的库。

这里的载入器,是指we应用程序载入器。不仅指类。  载入器的实现中,会使用一个自定义类载入器。它是org.apache.catalina.loader.WebappClassClassLoader的一个实例。

 还定义了一些对仓库集合的操作。而上面2个限制,作为仓库添加到载入器中。addRepository()

 Tomcat的载入器,通常与一个Context级别的servlet容器相关联。Loader接口的get、setContainter()。如果Context容器中的一个或者多个类改变了,载入器也支持对类的自动重载。这样就不需要重启tomcat了。 Loader使用modified()方法支持类的自动重载,如果重载了,返回true。

  但是载入器本身不能自动重载,它会调用context接口的reload()方法来实现。通过get、setReloadable()指明是否支持载入器的自动重载。

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

 通过get、setDelegate()指明,是否要委托给一个父类载入器。

 每次加载的时候,先调用getLoader()方法,来获取类载入器实例

 

Reloader接口

  为了支持自动重载。重要方法,modified()方法。如果web程序某个servlet被修改,该方法返回true。


WebappLoader类

  实现loader接口,其就是web应用程序中的载入器。负责载入web应用程序中所使用的类。

  继承了Lifecycle接口,和Runable接口。

  这样就可以指定一个线程,不断调用类载入器的modified(),如果返回true,则通知相关的Context容器。然后由容器来完成类载入。

 start方法

  1.创建一个类载入器

          在内部使用一个类载入器。loader接口只有getClassLoader()没有set,那么我们只能使用默认载入器?。

          该实例,提供了get、setClassLoader(),来改变私有变量的值。

          我们可以继承WebappLoader类,来自定义类载入器。这个类通过私有方法createClassLoader(),使用的是默认类载入器

  2.设置仓库

         调用setRepositories()将仓库添加到类载入器。WEB-INF/class目录被传入到类载入器的addRepositories(),WEB-INF/lib目录被传入类载入器          的setJarPath()。

  3.设置类路径

         调用setClassPatch()方法。会在servlet上下文中为Jasper JSP编译器设置一个字符串形式的属性来表明类路径信息。

  4.设置访问权限

         运行Tomcat时,使用了安全管理器。则setPermissions()会为类载入器设置访问相关目录的权限。

  5.启动一个线程,支持自动加载

         如果modifited()返回为true,则continue,否则调用notifyContext()。通知与WebappLoader关联的Context容器重新加载相关联。

         

private void notifyContext(){
  WebContextNotifier notifier = new WebContextNotifier ();
   (new Thread(notifier)).start();
}
         WebContextNotifier是一个内部类

protected class WebContextNotifier implements Runable{
   public void run(){
       ((Context) container).reload();
   }

}


WebappClassLoader类

   负责,web应用程序,载入类的 类载入器。该类,继承java.net.URLClassLoader类。

   该类考虑到优化和安全两个方面。

   优化: 它会缓存之前已经载入的类来加载。会缓存加载失败的类的名字,当再次请求的时候直接抛异常。它会在仓库和指定的jar文件搜索类。

   安全:不允许载入指定的某些类。这些类的名字存储在一个字符串数组变量triggers中。特殊包下的特殊类也不会被载入,也不会将载入类的任务委托给系统类加载器。

   

private static final String[] packageTriggers = {
"javax","org.xml.sax"
}

   类缓存
      基于上面说的优化,java.lang.ClassLoader类,会维护一个Vector对象,保存已经载入的类。防止这些类因为不使用,而被当作垃圾回收。

      org.apache.catalina.loader.ResourceEntry类,实例会保存 资源的class文件的字节流、最后修改时间、如果来自jar会有,mainfest信息。

      已经载入的类会存在,resourceEntries的HashMap中,key就是资源名称。如果失败,存放在notFoundResources的HashMap中。

   载入类

      1.先检查本地缓存

      2.如果没有,则检查上一级缓存 即 调用java.lang.ClassLoader的findLoadedClass()方法

      3.如果2个都没有,则使用系统的类加载器进行加载,防止web程序的类覆盖JDK的类。

      4.如果启用了SecurityManager,则检查是否载入该类。如果是禁止载入的,则classNotFoundException

      5.如果打开标志位delegate,或者待载入的类是属于包触发器,的包名,则调用父类载入器 来载入。如果父类载入器返回null,则使用系统的类载入器。

      6.从当前仓库载入类

      7.如果当前仓库没有需要的类,且标志位delegate关闭,则使用父类载入器。如果父类载入器返回null,则使用系统的类载入器。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值