开发者可以通过主动调整字节码生成和类加载器功能对代码编译进行深层次的自定义。
Tomcat:正统的类加载器架构
Tomcat源码参考:
概述
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。
Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。
除tomcat外,web应用服务器还有jetty,weblogic,websphere,undertow等,都实现了自己的类加载器。
Tomcat的目录结构(以8.5.42为例)
bin目录
bin目录主要要来存放tomcat命令的目录,里面主要有两种类型的文件,一种是以.bat为扩展名的文件,一类是以.sh为扩展名的文件。其中.bat的文件是在windows平台的命令,.sh的文件是在linux平台的文件。
该目录中主要的文件有一下几个:
setclasspath:用于设置Java环境变量,如JAVA_HOME,JRE_HOME等
catalina:该文件也是用来配置环境的,如CATALINA_HOME,及tomcat的安装路径,我们可以在此目录设置Java的启动参数,如最大最小堆空间大小
startup:用来给启动tomcat,当双击startup后,tomcat启动,读取catalina脚本配置启动环境,catalina又会调用setclasspath脚本,用来设置jdk需要的环境变量,如果系统没有定义JAVA_HOME环境变量,则在会报错退出。
shutdomn:用于停止tomcat服务
conf目录
该目录主要用来存放tomcat的配置文件, 重要的配置文件如下:
server.xml:用于配置端口号,虚拟主机,默认加载项目,请求编码等等,是tomcat中最常使用的配置文件
web.xml:为所有的部署在该tomcat下的web应用通用的配置,与每个web应用的web.xml配合使用。例如:DefaultServlet,就算系统中没有配置任何的servlet,tomcat依然可以处理html,js,css等静态资源的请求,就是这个servelt起的作用;JspServlet,当请求jsp页面时,则会用到这个servlet。session过期时间配置session-timeout参数,也是在这个web.xml中配置的。
catalina.policy:这个是tomcat安全相关的配置文件,主要使用安全策略文件可以在tomcat启动是附上 -security
catalina.properties:安全配置,类加载设置,不需扫描的类,字符缓存等配置
context.xml:所有在tomcat中发布的应用都会使用的公用配置,例如:发布的web应用的描述文件的位置及名称,及session持久化配置等。session持久化示例请参考第三部分的示例
tomcat-users.xml:tomcat的角色(授权用户)配置文件,用于访问tomcat管理应用程序时的安全性设置,用server.xml中引用的默认的用户数据库域(UserDatabase Realm)使用它,所有的凭据都是默认被注释的
tomcat-users.xsd:定义了 tomcat-users.xml 所使用到的标签,即tomcat-user.xml 的结构定义文件
lib目录
所有的部署在tomcat中的web应用公用的jar包
logs目录
用于保存tomcat运行时的日志数据
catalina.{yyyy-MM-dd}.log:tomcat自己的运行日志
localhost.{yyyy-MM-dd}.log:应用初始化日志,包含tomcat的启动和暂停时的运行日志,没有catalina.{yyyy-MM-dd}.log记录的全,只包含一部分
localhost_access_log.{yyyy-MM-dd}.txt:tomcat访问日志,包括servlet接口请求时间,资源,返回的状态码
manager.{yyyy-MM-dd}.log:是tomcat管理控制台应用的专有日志。在Linux系统中,tomcat将日志记入catalina.out文件,可以使用
tail -f catalina.out
进行跟踪。
temp目录
存放tomcat在运行期间产生的临时文件,可以清空该目录的文件,但不能删除该目录,可能会引发不可预知的错误。
webapps目录
tomcat默认的项目部署目录,在默认情况下,如果要发布应用可以直接将应用的war包放入该目录即可完成发布,也可以直接发布未压缩的目录,但要求该目录需要符合web应用规范。
doc:tomcat文档
examples:tomcat自带的web应用示例
host-manager:tomcat主机管理应用程序
manager:tomcat的管理应用程序
ROOT:如果项目部署为ROOT,则在地址栏直接输入:http://ip:port/ 就可以访问,即不用输入应用名
work目录
用来部署应用中由jsp文件生成的java文件和编译之后的class文件,可以删除,删除后可以迫使tomcat重新生成jsp对用的java和class文件。
其他文件
LICENSE:tomcat开源许可文件
NOTICE:tomcat说明文件
RELEASE-NOTES:版本说明文件
README.md:即README.txt,是tomcat帮助文件
Web服务器需要解决的问题和类加载器目录
tomcat类加载器架构
部署在同一个服务器上的两个web应用程序所使用的java类库要互相隔离,因为他们可能依赖不同的三方件版本
部署在同一个服务器上的两个web应用程序所使用的java类库要能互相共享,例如他们都使用相同的spring组件,避免多次加载导致虚拟机方法区过度膨胀
服务器自身安全不受部署的web应用程序影响,因此服务器使用的类库与应用程序之间要独立
部分应用例如JSP、ASP、PHP等需要热替换,句柄无须重启就能修改运行类的能力
针对上述问题,tomcat提供多种不同含义的ClassPath:
/common目录:类库可被Tomcat和所有web应用程序共同使用
/server目录:类库可被Tomcat使用,对web应用程序不可见
/shared目录:类库可被所有Web应用程序共同使用,对tomcat自己不可见
/WebApp/WEB-INF目录:类库仅该web应用使用,对Tomcat和其他Web应用程序均不可见
基于这套类加载器目录结构,tomcat的类加载器结构如下:
前面3个类加载和默认的一致
CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载/common/*
、/server/*
、/shared/*
(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*
中的Java类库
其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
而它们的委派关系是:
CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。
WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。
tomcat类加载器对双亲委派机制的挑战
Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托
为什么要这样?
因为tomcat 为了实现隔离性,实现前面说的:“/WebApp/WEB-INF目录类库仅该web应用使用,对Tomcat和其他Web应用程序均不可见”
那么如果tomcat 的 Common ClassLoader 想加载 WebApp ClassLoader 中的类,该怎么办?
可以使用线程上下文类加载器实现,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作
tomcat类加载顺序
当tomcat启动时,会创建几种类加载器:
Bootstrap 引导类加载器
加载JVM启动所需的类,以及标准扩展类(位于jre/lib/ext
下)
System 系统类加载器
加载tomcat启动的类,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin
下。
Common 通用类加载器
加载tomcat使用以及应用通用的一些类,位于CATALINA_HOME/lib
下,比如servlet-api.jar
webapp 应用类加载器
每个应用在部署后,都会创建一个唯一的类加载器。该类加载器会加载位于WEB-INF/lib
下的jar文件中的class 和WEB-INF/classes
下的class文件。
总结下来,当应用需要到某个类时,则会按照下面的顺序进行类加载:
使用bootstrap引导类加载器加载
使用system系统类加载器加载
使用应用类加载器在
WEB-INF/classes
中加载使用应用类加载器在
WEB-INF/lib
中加载使用common类加载器在
CATALINA_HOME/lib
中加载
评论区