### Tomcat类加载器层次
Tomcat 作为`Servlet`容器,它负责加载我们的`Servlet`类,此外它还负责加载`Servlet`所依赖的 JAR 包。并且`Tomcat`本身也是也是一个 Java 程序,因此它需要加载自己的类和依赖的 JAR 包。首先让我们思考这一下这几个问题:
1. 假如我们在 Tomcat 中运行了两个 Web 应用程序,两个 Web 应用中有同名的`Servlet`,但是功能不同,Tomcat 需要同时加载和管理这两个同名的`Servlet`类,保证它们不会冲突,因此 Web 应用之间的类需要隔离。
2. 假如两个 Web 应用都依赖同一个第三方的 JAR 包,比如`Spring`,那`Spring`的 JAR 包被加载到内存后,`Tomcat`要保证这两个 Web 应用能够共享,也就是说`Spring`的 JAR 包只被加载一次,否则随着依赖的第三方 JAR 包增多,`JVM`的内存会膨胀。
3. 跟 JVM 一样,我们需要隔离 Tomcat 本身的类和 Web 应用的类
![](https://img.kancloud.cn/e0/9b/e09b5de9b96e4fb1be65fe5fce5c2aa9_569x485.png)
具体介绍下:
* **CommonClassLoader**:以应用类加载器为父类,是tomcat顶层的公用类加载器,其路径由conf/catalina.properties中的common.loader指定,默认指向${catalina.home}/lib下的包。
* **CatalinaClassloader**:以Common类加载器为父类,是用于加载Tomcat应用服务器的类加载器,其路径由server.loader指定,默认为空,此时tomcat使用Common类加载器加载应用服务器。
* **SharedClassLoader**:以Common类加载器为父类,是所有Web应用的父类加载器,其路径由shared.loader指定,默认为空,此时tomcat使用Common类加载器作为Web应用的父加载器。本质需求是两个 Web 应用之间怎么共享库类,并且不能重复加载相同的类。在双亲委托机制里,各个子加载器都可以通过父加载器去加载类,那么把需要共享的类放到父加载器的加载路径下不就行了吗。
因此 Tomcat 的设计者又加了一个类加载器`SharedClassLoader`,作为`WebAppClassLoader`的父加载器,专门来加载 Web 应用之间共享的类。如果`WebAppClassLoader`自己没有加载到某个类,就会委托父加载器`SharedClassLoader`去加载这个类,`SharedClassLoader`会在指定目录下加载共享类,之后返回给`WebAppClassLoader`,例如多个web应用都依赖的spring就可以使用SharedClassLoader进行加载;
* **WebAppClassLoader**:以Shared类加载器为父类,加载/WEB-INF/classes目录下的未压缩的Class和资源文件以及/WEB-INF/lib目录下的jar包,该类加载器只对当前Web应用可见,对其他Web应用均不可见;Tomcat 的解决方案是自定义一个类加载器`WebAppClassLoader`, 并且给每个 Web 应用创建一个类加载器实例。我们知道,Context 容器组件对应一个 Web 应用,因此,每个`Context`容器负责创建和维护一个`WebAppClassLoader`加载器实例。这背后的原理是,**不同的加载器实例加载的类被认为是不同的类**,即使它们的类名相同。这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间,每一个 Web 应用都有自己的类空间,Web 应用之间通过各自的类加载器互相隔离。
#### 如何隔离 Tomcat 本身的类和 Web 应用的类?
要共享可以通过父子关系,要隔离那就需要兄弟关系了。兄弟关系就是指两个类加载器是平行的,它们可能拥有同一个父加载器,基于此 Tomcat 又设计一个类加载器`CatalinaClassloader`,专门来加载 Tomcat 自身的类。
这样设计有个问题,那 Tomcat 和各 Web 应用之间需要共享一些类时该怎么办呢?
老办法,还是再增加一个`CommonClassLoader`,作为`CatalinaClassloader`和`SharedClassLoader`的父加载器。`CommonClassLoader`能加载的类都可以被`CatalinaClassLoader`和`SharedClassLoader`使用
*****
默认情况下,Common、Catalina、Shared类加载器是同一个,但可以配置3个不同的类加载器,使他们各司其职
> Common类加载器复杂加载Tomcat应用服务器内部和Web应用均可见的类,如Servlet规范相关包和一些通用工具包。
> Catalina类加载器负责只有Tomcat应用服务器内部可见的类,这些类对Web应用不可见。比如,想实现自己的会话存储方案,而且该方案依赖了一些第三方包,当然是不希望这些包对Web应用可见,这时可以配置server.load,创建独立的Catalina类加载器。
> Shared类复杂加载Web应用共享类,这些类tomcat服务器不会依赖
*****
Q:如果有10个Web应用程序都是用Spring来进行组织和管理的话,可以把Spring放到Common或Shared目录下让这些程序共享。Spring要对用户程序的类进行管理,自然要能访问到用户程序的类,而用户的程序显然是放在/WebApp/WEB-INF目录中的,那么被CommonClassLoader或SharedClassLoader加载的Spring如何访问并不在其加载范围内的用户程序呢?
如果按主流的双亲委派机制,显然无法做到让父类加载器加载的类去访问子类加载器加载的类,但使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作。spring加载类所用的Classloader是通过Thread.currentThread().getContextClassLoader()来获取的,而当线程创建时会默认setContextClassLoader(AppClassLoader),即线程上下文类加载器被设置为AppClassLoader,spring中始终可以获取到这个AppClassLoader(在Tomcat里就是WebAppClassLoader)子类加载器来加载bean,以后任何一个线程都可以通过getContextClassLoader()获取到WebAppClassLoader来getbean了
- 概述
- CAP理论
- BASE理论
- ACID
- 分布式系统相关技术
- 主流数据库连接池
- 基础
- 系统单点
- 负载均衡
- HTTP重定向负载均衡
- DNS域名解析负载均衡
- 反向代理负载均衡
- IP负载均衡
- 数据链路层负载均衡
- 负载均衡算法
- 轮询法(Round Robin)
- 加权轮询(Weight Round Robin)
- 随机算法(Random)
- 源地址Hash算法
- 加权随机法(Weight Random)
- 最小连接数法(Least Connections)
- 接入层负载均衡
- 软件架构
- 性能
- 性能测试指标
- 响应时间
- 并发数
- 吞吐量
- 性能计数器
- 性能测试方法
- 性能测试报告
- 性能优化
- Web前端性能优化
- 应用服务器性能优化
- 可用性
- 服务降级
- 伸缩性
- 扩展性
- 事件驱动架构
- 安全性
- 信息加密技术
- 分布式系统概述
- 自动化
- 分布式唯一ID
- 幂等设计
- 分布式锁
- 脑裂
- 一致性原理
- Paxos
- Zab
- Raft
- 分布式远程服务调用
- RMI
- Spring RMI
- WebService
- SOA服务架构
- 微服务架构
- 微服务的九大特性
- 服务注册和发现
- 解决方案及组件
- 分布式网关
- 注册中心
- Zookeeper
- ZNode
- Watch接口
- 持久节点-配置中心实现原理
- 临时节点-注册中心
- Zookeeper选举
- Zookeeper角色
- ZooKeeper工作原理
- 选主流程
- 同步流程
- Leader工作流程
- Follower工作流程
- 常见限流算法
- 计数器算法
- 漏桶算法
- 令牌桶算法
- 滑动窗口
- 计数器&滑动窗口
- 断路器
- 大流量高并发高可用
- 高可用
- 高并发/大流量
- 分布式缓存系统
- 基本概念
- 缓存命中率
- 缓存最大元素
- 缓存回收策略
- 回收算法
- 缓存穿透与缓存雪崩
- CDN缓存
- 缓存分类
- memcached
- 客户端路由原理
- 内存管理机制
- Redis
- Redis数据模型
- redisObject/Redis type/Redis encoding
- 命令的类型检查和多态
- skiplist跳跃表
- 为什么使用跳跃表
- redis-内存管理机制
- Redis淘汰策略
- Redis持久化策略
- Redis并发竞争
- redis主从复制
- Redis集群实现方案
- Redis Cluster
- redis事务
- Redis-Sentinel
- Redis适用场景
- Redis客户端
- redis rehash原理
- dict数据结构
- 触发rehash的条件
- 渐进式rehash
- 渐进式rehash过程
- Redis多线程版本
- 缓存实际应用
- 堆缓存-Guava Cache
- 主要参数
- Caffeine
- Spring注解缓存
- 分布式存储
- Database
- AUTOCOMMIT
- 脏读&幻读&不可重复读
- 子查询
- 连接
- 内连接
- 自连接
- 自然连接
- 外连接
- 组合查询
- 隔离级别
- 数据库范式
- 索引实现机制
- 数据库拆分
- 表分区
- 分库
- 分表
- MySQL
- MySQL基础架构
- 锁分类
- 排它锁&独占锁
- 共享锁
- 间隙锁
- 表级锁
- 存储引擎
- 磁盘IO
- 磁盘结构图
- 磁盘数据读写原理
- MySQL索引原理
- B+树索引
- 局部性原理
- 索引数据结构
- 联合索引
- 最左前缀匹配原则
- 建索引的几大原则
- 数据文件和索引文件
- 执行计划explain
- 常见问题
- 数据页
- MYSQL单表存储量计算
- 回表
- 索引覆盖
- 索引下推
- 页分裂和页合并
- InnoDB
- innodb索引
- Innodb引擎的底层实现
- MyISAM
- MyISAM引擎的底层实现
- MVCC
- Next-Key Locks
- MySQL索引类型
- MYSQL复制
- 主从复制
- 读写分离
- MySQL Dual-Master
- 分库分表实现方案
- MySQL事务实现原理
- MYSQL调优
- 性能优化
- HBase
- 不停机分库分表迁移
- RDBMS&NoSQL
- 分布式事务
- 协议或事务模型
- X/Open XA协议
- 分布式事务编程接口规范JTA
- TCC模型
- 解决方案
- 两阶段提交2PC
- 三阶段提交3PC
- Seata
- 分布式事务Seata产品模块
- AT模式
- TCC模式
- Saga模式
- XA模式
- 基于消息中间件的最终一致性事务方案
- 消息队列
- AMQP
- JMS
- ActiveMQ
- RabbitMQ
- RocketMQ
- RocketMQ基本概念
- 主要特性
- 分区顺序消息
- 全局顺序消息
- 消息可靠性
- 定时消息
- 消息重试
- 死信队列
- 分布式事务消息
- RocketMQ架构
- Producer
- Consumer
- NameServer
- Broker
- RocketMQ设计
- 消息存储
- 页缓存与内存映射
- 消息刷盘
- 通信机制
- console控制台
- RocketMQ部署架构
- Kafka
- Pulsar
- MQ消息重复消费与丢失
- 主流消息队列比较
- 分布式调度系统
- 分布式搜索
- 分布式计算
- 架构案例
- 秒杀业务
- 秒杀整体架构
- 常见的监控系统
- 小米手机抢购秒杀方案
- 架构师领导艺术
- 架构师箴言
- 技术leader核心职责
- WEB服务器
- Servlet
- Servlet实现
- Servlet生命周期
- Servlet容器工作模式
- Servlet工作原理
- servlet线程安全
- CGI&FastCGI
- CGI
- FastCGI
- FastCGI与CGI特点
- CGI与Servlet比较
- HTTP Server
- Nginx
- Apache
- Nginx与Apache比较
- Application Server
- Tomcat
- Tomcat总体架构
- Connector
- 连接器核心功能
- ProtocolHandler
- EndPoint
- Processor
- Adapter
- Container
- 请求定位Servlet的过程
- Lifecycle生命周期
- Tomcat模块设计
- Tomcat实例
- Tomcat运行原理
- spring & servlet
- Tomcat启动流程
- Tomcat支持的I/O模型
- Tomcat应用层协议
- Tomcat类加载机制
- Tomcat类加载器
- Tomcat类加载器层次
- Apache+Tomcat
- 序列化
- XML&JSON
- JSON
- JAVA原生序列化
- hessian
- 常见中间件
- Canal
- Databus
- ELK日志套件
- 数据库连接池
- spring状态机
- 常见解决方案
- 二维码扫码登录原理
- 前沿技术
- Saas服务
- 服务网格(Service Mesh)
- 云原生
- 常见面试问题
- Redis持久化的几种方式
- Redis的缓存失效策略
- 附录
- 二将军问题
- 常见问题定位步骤
- 如何快速熟悉新系统
- 制定技术方案套路
- NUMA陷阱