zoukankan      html  css  js  c++  java
  • Tomcat从零开始(十)Loader

    第十课:

    不知不觉就10blog了,说实话,我是第一次更这么长时间的Blog
    嗯,今天说说Loader,在以前的课程中,也就是内个能使用最初级的servlet的那一节,我们使用了URLClassLoader加载Servlet,但是这是不科学的,因为如果用系统自带的Loader加载,servlet能访问类库太多就太不安全了。所以我们需要实现自己的一个加载器,那我们首先看看JavaLoader
    JVM
    在运行的时候,会产生3classLoader,分别是Boostrap,ClassLoaderExtension ClassloaderAppClassLoader
    首先第一个Bootstrap,根据他的意思就知道是启动用的,它是用C++编写的,而且实际上不是classLoader的子类,而是JVM自身实现的。在JVM源码中,我们可以看到用static const char classpathFormat []  数组来存储 所需加载的jar之后就是extension classLoader这个类了,他是负责加载/lib/ext这个文件夹下的类的,或者java.ext.dirs这个系统属性指定(别说不知道是什么,我们在以前的课程中,经常使用的user.dir也是系统属性),因为默认的Ext.dirs对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的JAR类包对所有的JVMsystem classloader都是可见的。剩下的一个就是AppClassLoader了,我们用的url内个也是它的儿子,用来加载classpathClassLoader加载类用的是靠上级的方式(这个名字有点忘了 AppClassLoader->ExtClassLoader->好像是全局委托吧),就是加载类的时候,上级先找,找不到下级来。所以就是 bootstrap。我们需要思考一下为什么要采用这种模式来处理,因为如果我们编写一个具有危险性的类,注意这个类要和系统存在的类重名,比如java.lang.math,那么当加载的时候这个有危险的类就不会加载到用户,因为bootstrap就不会加载这个类,因为那时候已经有原有的java.lang.Math了。如果不采用,那就直接在用户机器上运行,破坏安全。
    这里举一个例子,比如你有两个类,一个放到了ext.dirs 这个类引用了另外一个类,另外一个放到classpath中,那么 就会报错了。因为第二个类默认用Extloader来搜索,他不会让appLoader去查找。

    妥了,那现在说说Tomcat为什么要使用自己的加载器,一个原因在上面已经说了,另外就是需要缓存以前的类,和预先加载一些类做预备。当然,说这么多J2SEloader是为了让大家不与tomcatloader混淆, j2se是用来加载类的,而tomcat实际上是一个webAppLoaderLoader组件必须要实现org.apache.catalina.Loader接口。
    Tomcat
    Loader其实也是像J2SE中那样的

    首先是Commonclass Loader,主要是加载tomcat 的common下的所有jar和类。他的上级是appclassloader

    之后就是server和shared Class Loader,两只个的区别是,前者是加载tomcat的核心类,主要是tomcat的server目录。而后者是加载 web app的类(公共类)。他们的上级是common class loader

    再之后就是 webappclass loader了,这个是用来加载每个app的WEB-INF/ classes和lib的。他的上级是shared class loader。但是它的加载 和 之前的不一样,刚才说了Tomcat的loader是有缓存的,如果之前的类没加载,那么就给它 上级来加载(这里就是j2se的加载了),之后,如果加载失败了,那就查找WEB-INF/classes 和 lib下,这个是怎么实现的,我下面就会说。如果再没找到,就给shared class loader。 如果以前加载过,直接从缓存取就行了。

    其实这里没什么说的,其实主要理解一下Java的loader 的委托模式。


    我们先来看看Loader这个接口,其中我们注意与Repository有关的方法,Repository这个单词的意思是仓库。那么思考一下,这里的repository就是代表类库的意思,add就是添加,find就是查找。那么,我们来看看

    Loader的实现类,是如何实现的。这个类在org.apache.catalina.loader.WebappLoader中。



        public void addRepository(String repository) {
    
            if (debug >= 1)
                log(sm.getString("webappLoader.addRepository", repository));
    
            for (int i = 0; i < repositories.length; i++) {
                if (repository.equals(repositories[i]))
                    return;
            }
            String results[] = new String[repositories.length + 1];
            for (int i = 0; i < repositories.length; i++)
                results[i] = repositories[i];
            results[repositories.length] = repository;
            repositories = results;
    
            if (started && (classLoader != null)) {
                classLoader.addRepository(repository);
                setClassPath();
            }
    	//我们可以看到,这里就是一个数组的操作
        }



    之后我们看看他的Start()方法,这个方法在Lifecycle中说过了。那么,在运行的初期,系统就会默认调用一个方法setRepositories(),这个在start()方法可以自己找到,那我贴一段setRepositories方法。

            // 加载Context的工作目录
            File workDir =
                (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
            if (workDir == null)
                return;
            log(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));
    
            DirContext resources = container.getResources();
    
            //这里,就是将这个  classes目录设置到repository中,所以不用看了,我们的apploader
            //一定默认加载了  /WEB-INF/classes目录的实现。
            String classesPath = "/WEB-INF/classes";
            DirContext classes = null;


    看到了么,这里就是默认加载classes的地方,其实/lib目录也是开始的时候就被加到repositories中了,这个希望可以自己找到。毕竟读代码也是需要多多练习的~。
    我们都知道,这个东西是要跟一个容器关联的,Engine,host,Context,Wrapper这四个容器中,他关联的是context。也不是说必须关联,只是context必须要有这个加载器。另外,我们在用tomcat的时候,会发现另外一个功能,就是当我们更改完servlet之后一般都不需要重启服务器,因为tomcat存在一种reloader机制。这个reloader也是一个接口,每个加载器都必须实现这个,其实就是实现以下reloader接口的 modified方法,来看  是否被更改,更改了就重新加载。
    今天先说这么多,明天我会具体的分析一下这个appclassloader这个类。其实我觉得讲了这么多了,这个类其实大家也都能看懂了吧。另外说一下,用下面这段代码,可以看bootstrap在启动的时候到底加载了什么jar。注意!   这里不能用Eclipse去用这段代码。因为eclipse的Access Rule的问题。具体的设置,我会在下面贴图。

    public class Test {
    	public static void main(String args[]){
    		URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs(); 
    		for (int i = 0; i < urls.length; i++) { 
    			System.out.println(urls[i].toString()); 
    		} 
    
    	}
    }



    首先看一下运行截图。

     
     
     
     
  • 相关阅读:
    IOS
    WAN
    在不同网段使用 VLAN 通信
    数据链路层
    GRE 协议
    路由协议 (1)
    隔离广播域的 VLAN 来了
    数据包的通信过程
    Webpack 原理浅析
    蒲公英 · JELLY技术周刊 Vol.16 谷歌首个线上 Web 开发者大会
  • 原文地址:https://www.cnblogs.com/pangblog/p/3324952.html
Copyright © 2011-2022 走看看