How Tomcat Works(十一)

系统 1639 0

本文接下来分析tomcat的类载入器,tomcat需要实现一个自定义的载入器,而不能使用系统类载入器

(1)限制serlvet访问当前运行的java虚拟机中环境变量CLASSPATH指明的路径下的所有类和库,而只允许载入WEB-INF/class目录及其子目录下的类,和从部署的库到WEB-INF/lib目录载入类

(2)提供自动重载的功能,即当WEB-INF/class目录或WEB-INF/lib目录下的类发生变化时,Web应用程序会重新载入这些类

我们先来回顾一下java的类载入器,当我们创建java类的实例时,都必须先将类载入到内存中,java虚拟机使用类载入器来载入需要的类

JVM使用三种类型的类载入器来载入所需要的类,分别为引导类载入器(bootstrap class loader)、扩展类载入器(extensions class loader)和系统类载入器(system class loader)

  • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

JVM采用一种代理模型的类载入机制,可以解决类载入过程中的安全性问题;每当系统需要载入一个类的时候,会首先调用系统类载入器,而系统类载入器将载入类的任务委派给其父载入器,即扩展类载入器,同样扩展类载入器将载入类的任务委派给其父载入器,即引导类载入器;因此引导类载入器会首先执行载入某个类的任务,如果引导类载入器找不到需要载入的类,那么扩展类载入器尝试载入该类,如果扩展类载入器也找不到该类,就轮到系统类载入器继续执行载入任务。如果系统类载入器还是找不到该类,则会抛出java.lang.ClassNotFoundException异常

Tomcat中的载入器是指Web应用程序载入器,而不仅仅指类载入器,载入器必须实现org.apache.catalina.Loader接口

      
        public
      
      
        interface
      
      
         Loader {

      
      
        public
      
      
         ClassLoader getClassLoader();

      
      
        public
      
      
         Container getContainer();

      
      
        public
      
      
        void
      
      
         setContainer(Container container);

      
      
        public
      
      
         DefaultContext getDefaultContext();

      
      
        public
      
      
        void
      
      
         setDefaultContext(DefaultContext defaultContext);    

      
      
        public
      
      
        boolean
      
      
         getDelegate();

      
      
        public
      
      
        void
      
       setDelegate(
      
        boolean
      
      
         delegate);

      
      
        public
      
      
         String getInfo();

      
      
        public
      
      
        boolean
      
      
         getReloadable();

      
      
        public
      
      
        void
      
       setReloadable(
      
        boolean
      
      
         reloadable);

      
      
        public
      
      
        void
      
      
         addPropertyChangeListener(PropertyChangeListener listener);

      
      
        public
      
      
        void
      
      
         addRepository(String repository);

      
      
        public
      
      
         String[] findRepositories();

      
      
        public
      
      
        boolean
      
      
         modified();

      
      
        public
      
      
        void
      
      
         removePropertyChangeListener(PropertyChangeListener listener);
}
      
    

下面我们来具体来分析tomcat容器中 Web应用程序载入器的具体实现,即org.apache.catalina.loader.WebappLoader类实现了上面的Loader接口,负责载入Web应用程序中所使用到的类。

WebappLoader类会创建org.apache.catalina.loader.WebappClassLoader类的实例作为其类载入器;

同时WebappLoader类也实现了org.apache.catalina.Lifecycle接口,可以由其相关联的容器来启动或关闭;

此外,WebappLoader类还实现了java.lang.Runnable接口,通过一个线程不断地调用其类载入器的modified()方法。如果modified()方法返回true, WebappLoader的实例会通知其关联的servlet容器(在这里是Context类的实例),然后由Context实例来完成类的重新载入。

当调用WebappLoader类的start()方法时,会完成以下几项重要工作:

(1)创建一个类载入器

(2)设置仓库

(3)设置类路径

(4)设置访问权限

(5)启动一个新线程来支持自动重载

WebappLoader类会调用其私有方法createClassLoader()方法来创建默认的类载入器

      
        /**
      
      
        
     * Create associated classLoader.
     
      
      
        */
      
      
        private
      
      
         WebappClassLoader createClassLoader()
        
      
      
        throws
      
      
         Exception {

        Class clazz 
      
      =
      
         Class.forName(loaderClass);
        WebappClassLoader classLoader 
      
      = 
      
        null
      
      
        ;

        
      
      
        if
      
       (parentClassLoader == 
      
        null
      
      
        ) {
            
      
      
        //
      
      
         Will cause a ClassCast is the class does not extend WCL, but
            
      
      
        //
      
      
         this is on purpose (the exception will be caught and rethrown)
      
      
            classLoader =
      
         (WebappClassLoader) clazz.newInstance();
        } 
      
      
        else
      
      
         {
            Class[] argTypes 
      
      = { ClassLoader.
      
        class
      
      
         };
            Object[] args 
      
      =
      
         { parentClassLoader };
            Constructor constr 
      
      =
      
         clazz.getConstructor(argTypes);
            classLoader 
      
      =
      
         (WebappClassLoader) constr.newInstance(args);
        }

        
      
      
        return
      
      
         classLoader;

    }
      
    

String loaderClass 的默认值为org.apache.catalina.loader.WebappClassLoader

启动方法后面的设置仓库、设置类路径、设置访问权限等都与类载入器的初始化相关

WebappLoader类支持自动重载功能,如果WEB-INF/class目录或WEB-INF/lib目录下的某些类被重新编译了,那么这些类会自动重新载入,而无需重启tomcat。WebappLoader类使用一个线程周期性地检查每个资源的时间戳,我们可以调用setCheckInterval()方法设置间隔时间

      
        /**
      
      
        
     * The background thread that checks for session timeouts and shutdown.
     
      
      
        */
      
      
        public
      
      
        void
      
      
         run() {

        
      
      
        if
      
       (debug >= 1
      
        )
            log(
      
      "BACKGROUND THREAD Starting"
      
        );

        
      
      
        //
      
      
         Loop until the termination semaphore is set
      
      
        while
      
       (!
      
        threadDone) {

            
      
      
        //
      
      
         Wait for our check interval
      
      
                    threadSleep();

            
      
      
        if
      
       (!
      
        started)
                
      
      
        break
      
      
        ;

            
      
      
        try
      
      
         {
                
      
      
        //
      
      
         Perform our modification check
      
      
        if
      
       (!
      
        classLoader.modified())
                    
      
      
        continue
      
      
        ;
            } 
      
      
        catch
      
      
         (Exception e) {
                log(sm.getString(
      
      "webappLoader.failModifiedCheck"
      
        ), e);
                
      
      
        continue
      
      
        ;
            }

            
      
      
        //
      
      
         Handle a need for reloading
      
      
                    notifyContext();
            
      
      
        break
      
      
        ;

        }

        
      
      
        if
      
       (debug >= 1
      
        )
            log(
      
      "BACKGROUND THREAD Stopping"
      
        );

    }
      
    

在上面run()方法里面的循环中,首先使线程休眠一段时间,然后调用 WebappLoader实例的类载入器的 modified()方法检查已经载入的类是否被修改,肉没有修改则重新执行循环;否则调用私有方法notifyContext(),通知与WebappLoader实例相关联的Context容器重新载入相关类

      
        /**
      
      
        
     * Notify our Context that a reload is appropriate.
     
      
      
        */
      
      
        private
      
      
        void
      
      
         notifyContext() {

        WebappContextNotifier notifier 
      
      = 
      
        new
      
      
         WebappContextNotifier();
        (
      
      
        new
      
      
         Thread(notifier)).start();

    }
      
    

上面方法中的WebappContextNotifier为内部类,实现了Runnable接口

      
        /**
      
      
        
     * Private thread class to notify our associated Context that we have
     * recognized the need for a reload.
     
      
      
        */
      
      
        protected
      
      
        class
      
       WebappContextNotifier 
      
        implements
      
      
         Runnable {

        
      
      
        /**
      
      
        
         * Perform the requested notification.
         
      
      
        */
      
      
        public
      
      
        void
      
      
         run() {
            ((Context) container).reload();
        }
    }
      
    

下面我们来分析web应用程序中负责载入类的类载入器org.apache.catalina.loader.WebappClassLoader,该类继承自java.net.URLClassLoader,同时实现了org.apache.catalina.loader.Reloader接口

      
        public
      
      
        interface
      
      
         Reloader {
   
      
      
        public
      
      
        void
      
      
         addRepository(String repository);

      
      
        public
      
      
         String[] findRepositories();
   
      
      
        public
      
      
        boolean
      
      
         modified();

}
      
    

该接口用来与仓库操作相关,同时检查web应用程序中的某个servlet或相关的类是否被修改

每个由WebappClassLoader载入的类,都视为资源,由org.apache.catalina.loader.ResourceEntry类的实例表示,存储了类的相关信息

      
        public
      
      
        class
      
      
         ResourceEntry {
  
      
      
        public
      
      
        long
      
       lastModified = -1
      
        ;
   
      
      
        public
      
      
        byte
      
      [] binaryContent = 
      
        null
      
      
        ;
   
      
      
        public
      
       Class loadedClass = 
      
        null
      
      
        ;

      
      
        public
      
       URL source = 
      
        null
      
      
        ;

      
      
        public
      
       URL codeBase = 
      
        null
      
      
        ;

      
      
        public
      
       Manifest manifest = 
      
        null
      
      
        ;

      
      
        public
      
       Certificate[] certificates = 
      
        null
      
      
        ;

}
      
    

在WebappClassLoader类中,所有已经缓存的类存储在名为resourceEntries的HashMap类型的变量中,而载入失败的类被存储在另一个名为notFoundResources的HashMap类型的变量中

下面是WebappClassLoader的loadClass()方法的具体实现

      
        public
      
       Class loadClass(String name, 
      
        boolean
      
      
         resolve)
        
      
      
        throws
      
      
         ClassNotFoundException {
        
      
      
        if
      
       (debug >= 2
      
        )
            log(
      
      "loadClass(" + name + ", " + resolve + ")"
      
        );
        Class clazz 
      
      = 
      
        null
      
      
        ;

        
      
      
        //
      
      
         Don't load classes if class loader is stopped
      
      
        if
      
       (!
      
        started) {
            log(
      
      "Lifecycle error : CL stopped"
      
        );
            
      
      
        throw
      
      
        new
      
      
         ClassNotFoundException(name);
        }

        
      
      
        //
      
      
         (0) Check our previously loaded local class cache
      
      
        clazz =
      
         findLoadedClass0(name);
        
      
      
        if
      
       (clazz != 
      
        null
      
      
        ) {
            
      
      
        if
      
       (debug >= 3
      
        )
                log(
      
      "  Returning class from cache"
      
        );
            
      
      
        if
      
      
         (resolve)
                resolveClass(clazz);
            
      
      
        return
      
      
         (clazz);
        }

        
      
      
        //
      
      
         (0.1) Check our previously loaded class cache
      
      
        clazz =
      
         findLoadedClass(name);
        
      
      
        if
      
       (clazz != 
      
        null
      
      
        ) {
            
      
      
        if
      
       (debug >= 3
      
        )
                log(
      
      "  Returning class from cache"
      
        );
            
      
      
        if
      
      
         (resolve)
                resolveClass(clazz);
            
      
      
        return
      
      
         (clazz);
        }

        
      
      
        //
      
      
         (0.2) Try loading the class with the system class loader, to prevent
        
      
      
        //
      
      
               the webapp from overriding J2SE classes
      
      
        try
      
      
         {
            clazz 
      
      =
      
         system.loadClass(name);
            
      
      
        if
      
       (clazz != 
      
        null
      
      
        ) {
                
      
      
        if
      
      
         (resolve)
                    resolveClass(clazz);
                
      
      
        return
      
      
         (clazz);
            }
        } 
      
      
        catch
      
      
         (ClassNotFoundException e) {
            
      
      
        //
      
      
         Ignore
      
      
                }

        
      
      
        //
      
      
         (0.5) Permission to access this class when using a SecurityManager
      
      
        if
      
       (securityManager != 
      
        null
      
      
        ) {
            
      
      
        int
      
       i = name.lastIndexOf('.'
      
        );
            
      
      
        if
      
       (i >= 0
      
        ) {
                
      
      
        try
      
      
         {
                    securityManager.checkPackageAccess(name.substring(
      
      0
      
        ,i));
                } 
      
      
        catch
      
      
         (SecurityException se) {
                    String error 
      
      = "Security Violation, attempt to use " +
                        "Restricted Class: " +
      
         name;
                    System.out.println(error);
                    se.printStackTrace();
                    log(error);
                    
      
      
        throw
      
      
        new
      
      
         ClassNotFoundException(error);
                }
            }
        }

        
      
      
        boolean
      
       delegateLoad = delegate ||
      
         filter(name);

        
      
      
        //
      
      
         (1) Delegate to our parent if requested
      
      
        if
      
      
         (delegateLoad) {
            
      
      
        if
      
       (debug >= 3
      
        )
                log(
      
      "  Delegating to parent classloader"
      
        );
            ClassLoader loader 
      
      =
      
         parent;
            
      
      
        if
      
       (loader == 
      
        null
      
      
        )
                loader 
      
      =
      
         system;
            
      
      
        try
      
      
         {
                clazz 
      
      =
      
         loader.loadClass(name);
                
      
      
        if
      
       (clazz != 
      
        null
      
      
        ) {
                    
      
      
        if
      
       (debug >= 3
      
        )
                        log(
      
      "  Loading class from parent"
      
        );
                    
      
      
        if
      
      
         (resolve)
                        resolveClass(clazz);
                    
      
      
        return
      
      
         (clazz);
                }
            } 
      
      
        catch
      
      
         (ClassNotFoundException e) {
                ;
            }
        }

        
      
      
        //
      
      
         (2) Search local repositories
      
      
        if
      
       (debug >= 3
      
        )
            log(
      
      "  Searching local repositories"
      
        );
        
      
      
        try
      
      
         {
            clazz 
      
      =
      
         findClass(name);
            
      
      
        if
      
       (clazz != 
      
        null
      
      
        ) {
                
      
      
        if
      
       (debug >= 3
      
        )
                    log(
      
      "  Loading class from local repository"
      
        );
                
      
      
        if
      
      
         (resolve)
                    resolveClass(clazz);
                
      
      
        return
      
      
         (clazz);
            }
        } 
      
      
        catch
      
      
         (ClassNotFoundException e) {
            ;
        }

        
      
      
        //
      
      
         (3) Delegate to parent unconditionally
      
      
        if
      
       (!
      
        delegateLoad) {
            
      
      
        if
      
       (debug >= 3
      
        )
                log(
      
      "  Delegating to parent classloader"
      
        );
            ClassLoader loader 
      
      =
      
         parent;
            
      
      
        if
      
       (loader == 
      
        null
      
      
        )
                loader 
      
      =
      
         system;
            
      
      
        try
      
      
         {
                clazz 
      
      =
      
         loader.loadClass(name);
                
      
      
        if
      
       (clazz != 
      
        null
      
      
        ) {
                    
      
      
        if
      
       (debug >= 3
      
        )
                        log(
      
      "  Loading class from parent"
      
        );
                    
      
      
        if
      
      
         (resolve)
                        resolveClass(clazz);
                    
      
      
        return
      
      
         (clazz);
                }
            } 
      
      
        catch
      
      
         (ClassNotFoundException e) {
                ;
            }
        }

        
      
      
        //
      
      
         This class was not found
      
      
        throw
      
      
        new
      
      
         ClassNotFoundException(name);

    }
      
    

在WebappClassLoader实例载入类时,首先是检查缓存,然后再载入指定类(如果设置了安全管理。在代理载入器载入前还要检测类型的安全)

--------------------------------------------------------------------------- 

本系列How Tomcat Works系本人原创 

转载请注明出处 博客园 刺猬的温驯 

本人邮箱:   chenying998179 # 163.com ( #改为@

本文链接 http://www.cnblogs.com/chenying99/p/3237389.html

How Tomcat Works(十一)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论