加入收藏 | 设为首页 | 会员中心 | 我要投稿 甘南站长网 (https://www.0941zz.com/)- 科技、行业物联网、开发、云计算、云管理!
当前位置: 首页 > 运营中心 > Tomcat > 正文

Tomcat Catalina为何不new出来原理解析

发布时间:2023-02-20 10:09:27 所属栏目:Tomcat 来源:互联网
导读:一、Catalina为什么不new出来? 掌握了Java的类加载器和双亲委派机制,现在我们就可以回答正题上来了,Tomcat的类加载器是怎么设计的? 1.Web容器的特性 Web容器有其自身的特殊性,所以在类加载器这块是不能完全使用JVM的类加载器的双亲委派机制的。在Web容器
  一、Catalina为什么不new出来?
  掌握了Java的类加载器和双亲委派机制,现在我们就可以回答正题上来了,Tomcat的类加载器是怎么设计的?
 
  1.Web容器的特性
  Web容器有其自身的特殊性,所以在类加载器这块是不能完全使用JVM的类加载器的双亲委派机制的。在Web容器中我们应该要满足如下的特性:
 
  隔离性:
 
  部署在同一个Web容器上的两个Web应用程序所使用的Java类库可以实现相互隔离。设想一下,两个Web应用,一个使用了Spring3.0,另一个使用了新的的5.0,应用服务器使用一个类加载器,Web应用将会因为jar包覆盖而无法启动。
 
 
  灵活性:
 
  Web应用之间的类加载器相互独立,那么就能针对一个Web应用进行重新部署,此时Web应用的类加载器会被重建,而且不会影响其他的Web应用。如果采用一个类加载器,类之间的依赖是杂乱复杂的,无法完全移出某个应用的类。
 
  性能:
 
  性能也是一个比较重要的点。部署在同一个Web容器上的两个Web应用程序所使用的Java类库可以互相共享。这个需求也很常见,例如,用户可能有10个使用Spring框架的应用程序部署在同一台服务器上,如果把10份Spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费——这主要倒不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到Web容器的内存,如果类库不能共享,虚拟机的方法区就会很容易出现过度膨胀的风险。
 
  2.Tomcat类加载器结构
  明白了Web容器的类加载器有多个,再来看tomcat的类加载器结构。
 
  可以看到在原先的java类加载器基础上,tomcat新增了几个类加载器,包括3个基础类加载器和每个Web应用的类加载器,其中3个基础类加载器可在conf/catalina.properties中配置,具体介绍下:

  以Common类加载器为父类,是所有Web应用的父类加载器,其路径由shared.loader指定,默认为空,此时tomcat使用Common类加载器作为Web应用的父加载器。
 
  以Shared类加载器为父类,加载/WEB-INF/classes目录下的未压缩的Class和资源文件以及/WEB-INF/lib目录下的jar包,该类加载器只对当前Web应用可见,对其他Web应用均不可见。
 
  默认情况下,Common、Catalina、Shared类加载器是同一个,但可以配置3个不同的类加载器,使他们各司其职。
 
 
  首先,Common类加载器复杂加载Tomcat应用服务器内部和Web应用均可见的类,如Servlet规范相关包和一些通用工具包。
 
  其次,Catalina类加载器负责只有Tomcat应用服务器内部可见的类,这些类对Web应用不可见。比如,想实现自己的会话存储方案,而且该方案依赖了一些第三方包,当然是不希望这些包对Web应用可见,这时可以配置server.load,创建独立的Catalina类加载器。
 
  再次,Shared类复杂加载Web应用共享类,这些类tomcat服务器不会依赖
 
  3.Tomcat源码分析
  3.1 CatalinClassLoader
  首先来看看Tomcat的类加载器的继承结构
 
 
  可以看到继承的结构和我们上面所写的类加载器的结构不同。
 
  大家需要注意双亲委派机制并不是通过继承来实现的,而是相互之间组合而形成的。
 
  所以AppClassLoader没有继承自 ExtClassLoader,WebappClassLoader也没有继承自AppClassLoader。
 
  至于Common ClassLoader ,Shared ClassLoader,Catalina ClassLoader则是在启动时初始化的三个不同名字的URLClassLoader。
 
  先来看看Bootstrap#init()方法。init方法会调用initClassLoaders,同样也会将Catalina ClassLoader设置到当前线程设置到当前线程,进入initClassLoaders来看看。

  private void initClassLoaders() {
      try {
          // 创建 commonLoader  catalinaLoader sharedLoader
          commonLoader = createClassLoader("common", null);
          if (commonLoader == null) {
              // no config file, default to this loader - we might be in a 'single' env.
              commonLoader = this.getClass().getClassLoader();
          }
          // 默认情况下 server.loader 和 shared.loader 都为空则会返回 commonLoader 类加载器
          catalinaLoader = createClassLoader("server", commonLoader);
          sharedLoader = createClassLoader("shared", commonLoader);
      } catch (Throwable t) {
          handleThrowable(t);
          log.error("Class loader creation threw exception", t);
          System.exit(1);
      }
  }
  我们可以看到在initClassLoaders()方法中完成了CommonClassLoader, CatalinaClassLoader,和SharedClassLoader的创建,而且进入到createClassLoader方法中。
 
 
 
  可以看到这三个基础类加载器所加载的资源刚好对应conf/catalina.properties中的common.loader,server.loader,shared.loader
 
  3.2 层次结构
 
  Common/Catalina/Shared ClassLoader的创建好了之后就会维护相互之间的组合关系
 

  其实也就是设置了父加载器
 
  3.3 具体的加载过程
  源码比较长,直接进入到 WebappClassLoaderBase中的 LoadClass方法
 
  @Override
      public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
          synchronized (getClassLoadingLock(name)) {
              if (log.isDebugEnabled()) {
                  log.debug("loadClass(" + name + ", " + resolve + ")");
              }
              Class<?> clazz = null;
              // Log access to stopped class loader
              checkStateForClassLoading(name);
              // (0) Check our previously loaded local class cache
              // 检查WebappClassLoader中是否加载过此类
              clazz = findLoadedClass0(name);
              if (clazz != null) {
                  if (log.isDebugEnabled()) {
                      log.debug("  Returning class from cache");
                  }
                  if (resolve) {
                      resolveClass(clazz);
                  }
                  return clazz;
              }
              // (0.1) Check our previously loaded class cache
              // 如果第一步没有找到,则继续检查JVM虚拟机中是否加载过该类
              clazz = findLoadedClass(name);

(编辑:甘南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读