zoukankan      html  css  js  c++  java
  • java的classLoader分析与jettty的WebAppClassLoader

    classLoader,从名字就可以知道,用于加载class的东西。

    我们知道在Java中,源文件是会被编译成class文件的,我们的程序的运行也是需要依赖这些编译成字节码的class文件,而这些字节码文件就必须要被classLoader加载到内存之后才能使用。。。如果classLoader无法加载到我们要用的类型的class文件,那么将会抛出classnodfound的异常。。。

    先用一张图来整体描述一下java标准中定义的classLoader的层次吧:


     

    在整个定义中,一共有两种类型的加载器:

    (1)启动类加载器:Bootstrap ClassLoader,这个类加载器是使用c++语言实现的,它本身就是虚拟机的一部分

    (2)其他的类加载器,这些类加载器都是用java语言实现的,独立于虚拟机外部,并且他们都继承自抽象类java.lang.ClassLoader

     

    另外按照功能来分的话可以如下:

    (1)启动类加载器,也就是Bootstrap ClassLoader,这个类加载器负责将存放在<JAVA_HOME>/lib目录中的类库加载到虚拟机的内存中,而且它还要识别名字,如果名字不符合,就算放在lib目录中,也不会被加载,这个加载器无法被 java程序所引用。。。

    (2)扩展类加载器,Extension ClassLoader,这个加载器负责加载<JAVA_HOME>/lib/ext目录中的类库,也就是扩展类库,java程序中可以直接引用这个加载器。。。。

    (3)应用程序加载器,Application ClassLoader,它用与负责加载用户类路径上的类库,java程序中可以直接使用这个加载器,而且如果在应用程序中没有自定义过自己的类加载器,那么一般情况下这个就是程序中的默认加载器。。。。也就是loadClass啥的默认都是它了。。。。

     

    另外从上面的图形也可以看成一种层次关系,除了最顶层的启动类加载器之外,其余的加载器都必须要有自己的父加载器(这个是组合实现的,不是继承)。。

    这个也就是java中定义的所谓的双亲委派模型:

    如果一个类加载器收到了 类加载的请求,那么它首先不会自己去尝试加载这个类,而是将这个请求委派给自己的父加载器去完成,每一层的加载器都是,因此任何一个加载请求都会自动传送到顶层的启动类加载器中,只有当自己的父加载器反馈无法完成这个加载请求的时候,子加载器才会去尝试自己去加载。。。。

     

    这个是一个非常重要的定义,因为它限定了java在api方面的统一,越基础的类那么就由越基础的加载器来加载。。。。

    (补充:同一个class文件,如果被不同的加载器加载,那么他们将会不相等)

     

    这种双亲委派模型是java中默认以及推荐的模型,我们一般自己实现classLoader的时候也需要遵守这种规范,但是在web容器中就需要打破这种双亲委派模型了。。。

    主流的Java web服务器,如tomcat,jetty,weblogic,啥的都有自己定义自己的类加载器。。。

    因为在同一个服务器上我们会部署多个web应用程序,那么就需要这些web应用程序代码之间实现相互的隔离,而且web程序的类库还需要与服务器之间隔离,这样web应用程序的部署才不会影响到web服务器。。。

    那么在这里就需要打破双亲委派的模型了。。。而且这里会用到线程上下文类加载器。。。(Thread context classLoader)

     

    上面说了这么多,都是对java的classLoader的方面知识的一些普及,因为其实自己在之前也对这方面的概念都了解很少。。。

    那么接下来就可开始来分析jetty中定义的WebAppClassLoader了,它在jetty中用于负责加载web应用程序WEB-INF目录下lib以及classes里面的类库。。说白了就是用于加载web应用程序自己的源代码。。。

    先来看看它定义的一些比较重要的属性吧:

    [java] view plaincopy
     
     
    1. private String _name;   //名字  
    2. private WebAppContext _context;   //其所关联的webcontext  
    3. private ClassLoader _parent;   //父classLoader  

     

    最开始的是当前loader名字,不知道为啥要有这个名字,感觉也没啥用....第二个参数就是当前classLoader所属的webcontext,这个属性就属于比较重要的了,,...。。。每一个web应用程序都会有一个属于自己的classlaoder,最后一个参数就是前面提到过的父classLoader,仿佛这里看起来是要用于实现上面说过的双亲委派,其实不然。。。

    在我们自己定义的web应用程序中,一会用到其余的class代码,例如String,hashmap啥的,那么这里有一个parent,就可以保证可以在parent里面获取他们的class。。。。

     

    那么接下来来看看它的构造函数吧:

    [java] view plaincopy
     
     
    1. public WebAppClassLoader(WebAppContext context)  
    2.     throws IOException  {  
    3.     this(null,context);  
    4. }  
    5.   
    6. /* ------------------------------------------------------------ */  
    7. /** Constructor. 
    8.  */  
    9. //这里可以看出,如果在构建的时候没有提供父亲classLoader,那么将会默认将当前的线程classLoader作为当前的父classLoader  
    10. public WebAppClassLoader(ClassLoader parent, WebAppContext context)  
    11.     throws IOException {  
    12.     super(new URL[]{},parent!=null?parent  
    13.             :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()  
    14.                     :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()  
    15.                             :ClassLoader.getSystemClassLoader())));  
    16.     _parent=getParent();  //指向当前的parent  
    17.     _context=context;  //保存当前的context  
    18.     if (_parent==null)  
    19.         throw new IllegalArgumentException("no parent classloader!");  
    20.       
    21.     if (context.getExtraClasspath()!=null) {  
    22.         addClassPath(context.getExtraClasspath());  
    23.     }  
    24. }  

     

    其实这里一般情况下都是会调用第一个构造函数,第二个构造函数主要是要满足父类URLClassLoader的构造,它是java系统类库中的。。。而且到这里就能够知道其实这里默认是将当前的线程上下文classLoader指定为当前的parent,而这个线程上下文classLoader如果没有用户指定的话默认又将是前面提到过的appClassLoader,也就是用于加载用户代码的classLoader。。。。

    接下来来看看它定义的loadClass方法吧:

    [java] view plaincopy
     
     
    1. //加载类,因为每一个webContext 都会有一个自己的classLoader,所以也就是先了web应用程序代码之间的相互隔离  
    2. protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {  
    3.     Class c= findLoadedClass(name);  //首先在自己这里加载,我们自己定义的代码,引用的jar里面的代码都是在这里加载的  
    4.     ClassNotFoundException ex= null;  
    5.     boolean tried_parent= false;  
    6.     //如果父类加载器优先,或者这里加载的类型是系统类型,例如"java.","javax.servlet.","javax.xml.",那么由父类加载器来加载  
    7.     //不过webapp的代码父类加载器肯定是加载不倒的  
    8.     if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) ) {  
    9.         tried_parent= true;  
    10.         try {  
    11.             //这里的parent就是appclassLoader  
    12.             c= _parent.loadClass(name);  //由父类加载,一些系统代码,例如hashMap,set啥的都在这里加载,还有jetty定义的也在这里加载,例如org.mortbay.servlet.NoJspServlet  
    13.             if (Log.isDebugEnabled())  
    14.                 Log.debug("loaded " + c);  
    15.         }  catch (ClassNotFoundException e) {  
    16.             ex= e;  
    17.         }  
    18.     }  
    19.   
    20.     if (c == null) {  
    21.         try  {  
    22.             c= this.findClass(name);  //这里是从当前classLoader的路径下面去加载  
    23.         } catch (ClassNotFoundException e) {  
    24.             ex= e;  
    25.         }  
    26.     }  
    27.   
    28.     if (c == null && _parent!=null && !tried_parent && !isServerPath(name) )  
    29.         c= _parent.loadClass(name);  
    30.   
    31.     if (c == null)  
    32.         throw ex;  
    33.   
    34.     if (resolve)  
    35.         resolveClass(c);  
    36.   
    37.     if (Log.isDebugEnabled())  
    38.         Log.debug("loaded " + c+ " from "+c.getClassLoader());  
    39.       
    40.     return c;  
    41. }  

     

    这段代码其实还算是比较的简单吧。。。这里就可以知道指定parent的重要作用了。。。因为毕竟在我们自己的代码中也会有调用一些类库中的代码。。。而在parent中,却无法加载到我们自己定义的class文件。。。也就实现代码与jetty 服务器本身的隔离。。。。

    又因为每一个web应用程序都有自己的classLoader,也就是先了在同一个服务器下,不同的app之间的代码隔离。。。

    最后再来补充两个方法吧:

    [java] view plaincopy
     
     
    1. //添加class或者jar的路径,如果是文件夹路径的话,那么路径应该以/  结尾  
    2. public void addClassPath(String classPath)  
    3.     throws IOException {  
    4.     if (classPath == null)  
    5.         return;  
    6.           
    7.     StringTokenizer tokenizer= new StringTokenizer(classPath, ",;");  //有可能一次性传入多个classLoader  
    8.     while (tokenizer.hasMoreTokens()) {  
    9.         /* 
    10.          * file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/classes/ 
    11.   file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/aopalliance-1.0.jar 
    12.   file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/commons-collections-3.1.jar 
    13.          */  
    14.         Resource resource= Resource.newResource(tokenizer.nextToken());  //获取当前路径的resource对象  
    15.         if (Log.isDebugEnabled())  
    16.             Log.debug("Path resource=" + resource);  
    17.   
    18.         // Resolve file path if possible  
    19.         File file= resource.getFile();  //获取当前的文件  
    20.         if (file != null) {    
    21.             URL url= resource.getURL();  //获取url  
    22.             addURL(url);   //这个是父类的方法,将会加载这个路径下的class或者jar资源  
    23.         } else {  
    24.             // Add resource or expand jar/  
    25.             if (!resource.isDirectory() && file == null)  
    26.             {  
    27.                 InputStream in= resource.getInputStream();  
    28.                 File tmp_dir=_context.getTempDirectory();  
    29.                 if (tmp_dir==null)  
    30.                 {  
    31.                     tmp_dir = File.createTempFile("jetty.cl.lib",null);  
    32.                     tmp_dir.mkdir();  
    33.                     tmp_dir.deleteOnExit();  
    34.                 }  
    35.                 File lib= new File(tmp_dir, "lib");  
    36.                 if (!lib.exists())  
    37.                 {  
    38.                     lib.mkdir();  
    39.                     lib.deleteOnExit();  
    40.                 }  
    41.                 File jar= File.createTempFile("Jetty-"".jar", lib);  
    42.                   
    43.                 jar.deleteOnExit();  
    44.                 if (Log.isDebugEnabled())  
    45.                     Log.debug("Extract " + resource + " to " + jar);  
    46.                 FileOutputStream out = null;  
    47.                 try  
    48.                 {  
    49.                     out= new FileOutputStream(jar);  
    50.                     IO.copy(in, out);  
    51.                 }  
    52.                 finally  
    53.                 {  
    54.                     IO.close(out);  
    55.                 }  
    56.                   
    57.                 URL url= jar.toURL();  
    58.                 addURL(url);  
    59.             } else {  
    60.                 URL url= resource.getURL();  
    61.                 addURL(url);  //添加class的搜索路径  
    62.             }  
    63.         }  
    64.     }  
    65. }  
    66.   
    67.   
    68.   
    69.   
    70. //添加jar包或压缩文件  
    71. public void addJars(Resource lib)  {  
    72.     if (lib.exists() && lib.isDirectory())  
    73.     {  
    74.         String[] files=lib.list();  //获取当前文件夹下面的所有文件  
    75.         //遍历下面的所有文件  
    76.         for (int f=0;files!=null && f<files.length;f++)  {  
    77.             try {  
    78.                 Resource fn=lib.addPath(files[f]);    //获取这个文件的resource  
    79.                 String fnlc=fn.getName().toLowerCase();  
    80.                 //判断文件的扩展名是否正确  
    81.                 if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip"))  {  
    82.                     String jar=fn.toString();    
    83.                     jar=StringUtil.replace(jar, ",""%2C");  
    84.                     jar=StringUtil.replace(jar, ";""%3B");  
    85.                     addClassPath(jar); //调用addClassPath来将jar包加载  
    86.                 }  
    87.             }  catch (Exception ex) {  
    88.                 Log.warn(Log.EXCEPTION,ex);  
    89.             }  
    90.         }  
    91.     }  
    92. }  

     

    这两个方法的定义应该从名字就能够知道是干嘛的。。用于加载jar包或者class文件的。。最终是通过父类的addUrl的方法来实现这些源文件的加载。。。

     

    好了,到这里jetty中定义的webappclassLoader就算是比较清楚了。。不过比较遗憾没有去分析它的父类URLClassLoader,从而对java的整个classLoader有更深的了解。。以后有机会的话看看吧。。

    这样子就可以开始看jetty中如何创建webContext以及启动这些web应用程序了。。。

    classLoader,从名字就可以知道,用于加载class的东西。

    我们知道在Java中,源文件是会被编译成class文件的,我们的程序的运行也是需要依赖这些编译成字节码的class文件,而这些字节码文件就必须要被classLoader加载到内存之后才能使用。。。如果classLoader无法加载到我们要用的类型的class文件,那么将会抛出classnodfound的异常。。。

    先用一张图来整体描述一下java标准中定义的classLoader的层次吧:


     

    在整个定义中,一共有两种类型的加载器:

    (1)启动类加载器:Bootstrap ClassLoader,这个类加载器是使用c++语言实现的,它本身就是虚拟机的一部分

    (2)其他的类加载器,这些类加载器都是用java语言实现的,独立于虚拟机外部,并且他们都继承自抽象类java.lang.ClassLoader

     

    另外按照功能来分的话可以如下:

    (1)启动类加载器,也就是Bootstrap ClassLoader,这个类加载器负责将存放在<JAVA_HOME>/lib目录中的类库加载到虚拟机的内存中,而且它还要识别名字,如果名字不符合,就算放在lib目录中,也不会被加载,这个加载器无法被 java程序所引用。。。

    (2)扩展类加载器,Extension ClassLoader,这个加载器负责加载<JAVA_HOME>/lib/ext目录中的类库,也就是扩展类库,java程序中可以直接引用这个加载器。。。。

    (3)应用程序加载器,Application ClassLoader,它用与负责加载用户类路径上的类库,java程序中可以直接使用这个加载器,而且如果在应用程序中没有自定义过自己的类加载器,那么一般情况下这个就是程序中的默认加载器。。。。也就是loadClass啥的默认都是它了。。。。

     

    另外从上面的图形也可以看成一种层次关系,除了最顶层的启动类加载器之外,其余的加载器都必须要有自己的父加载器(这个是组合实现的,不是继承)。。

    这个也就是java中定义的所谓的双亲委派模型:

    如果一个类加载器收到了 类加载的请求,那么它首先不会自己去尝试加载这个类,而是将这个请求委派给自己的父加载器去完成,每一层的加载器都是,因此任何一个加载请求都会自动传送到顶层的启动类加载器中,只有当自己的父加载器反馈无法完成这个加载请求的时候,子加载器才会去尝试自己去加载。。。。

     

    这个是一个非常重要的定义,因为它限定了java在api方面的统一,越基础的类那么就由越基础的加载器来加载。。。。

    (补充:同一个class文件,如果被不同的加载器加载,那么他们将会不相等)

     

    这种双亲委派模型是java中默认以及推荐的模型,我们一般自己实现classLoader的时候也需要遵守这种规范,但是在web容器中就需要打破这种双亲委派模型了。。。

    主流的Java web服务器,如tomcat,jetty,weblogic,啥的都有自己定义自己的类加载器。。。

    因为在同一个服务器上我们会部署多个web应用程序,那么就需要这些web应用程序代码之间实现相互的隔离,而且web程序的类库还需要与服务器之间隔离,这样web应用程序的部署才不会影响到web服务器。。。

    那么在这里就需要打破双亲委派的模型了。。。而且这里会用到线程上下文类加载器。。。(Thread context classLoader)

     

    上面说了这么多,都是对java的classLoader的方面知识的一些普及,因为其实自己在之前也对这方面的概念都了解很少。。。

    那么接下来就可开始来分析jetty中定义的WebAppClassLoader了,它在jetty中用于负责加载web应用程序WEB-INF目录下lib以及classes里面的类库。。说白了就是用于加载web应用程序自己的源代码。。。

    先来看看它定义的一些比较重要的属性吧:

    [java] view plaincopy
     
     
    1. private String _name;   //名字  
    2. private WebAppContext _context;   //其所关联的webcontext  
    3. private ClassLoader _parent;   //父classLoader  

     

    最开始的是当前loader名字,不知道为啥要有这个名字,感觉也没啥用....第二个参数就是当前classLoader所属的webcontext,这个属性就属于比较重要的了,,...。。。每一个web应用程序都会有一个属于自己的classlaoder,最后一个参数就是前面提到过的父classLoader,仿佛这里看起来是要用于实现上面说过的双亲委派,其实不然。。。

    在我们自己定义的web应用程序中,一会用到其余的class代码,例如String,hashmap啥的,那么这里有一个parent,就可以保证可以在parent里面获取他们的class。。。。

     

    那么接下来来看看它的构造函数吧:

    [java] view plaincopy
     
     
    1. public WebAppClassLoader(WebAppContext context)  
    2.     throws IOException  {  
    3.     this(null,context);  
    4. }  
    5.   
    6. /* ------------------------------------------------------------ */  
    7. /** Constructor. 
    8.  */  
    9. //这里可以看出,如果在构建的时候没有提供父亲classLoader,那么将会默认将当前的线程classLoader作为当前的父classLoader  
    10. public WebAppClassLoader(ClassLoader parent, WebAppContext context)  
    11.     throws IOException {  
    12.     super(new URL[]{},parent!=null?parent  
    13.             :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()  
    14.                     :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()  
    15.                             :ClassLoader.getSystemClassLoader())));  
    16.     _parent=getParent();  //指向当前的parent  
    17.     _context=context;  //保存当前的context  
    18.     if (_parent==null)  
    19.         throw new IllegalArgumentException("no parent classloader!");  
    20.       
    21.     if (context.getExtraClasspath()!=null) {  
    22.         addClassPath(context.getExtraClasspath());  
    23.     }  
    24. }  

     

    其实这里一般情况下都是会调用第一个构造函数,第二个构造函数主要是要满足父类URLClassLoader的构造,它是java系统类库中的。。。而且到这里就能够知道其实这里默认是将当前的线程上下文classLoader指定为当前的parent,而这个线程上下文classLoader如果没有用户指定的话默认又将是前面提到过的appClassLoader,也就是用于加载用户代码的classLoader。。。。

    接下来来看看它定义的loadClass方法吧:

    [java] view plaincopy
     
     
    1. //加载类,因为每一个webContext 都会有一个自己的classLoader,所以也就是先了web应用程序代码之间的相互隔离  
    2. protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {  
    3.     Class c= findLoadedClass(name);  //首先在自己这里加载,我们自己定义的代码,引用的jar里面的代码都是在这里加载的  
    4.     ClassNotFoundException ex= null;  
    5.     boolean tried_parent= false;  
    6.     //如果父类加载器优先,或者这里加载的类型是系统类型,例如"java.","javax.servlet.","javax.xml.",那么由父类加载器来加载  
    7.     //不过webapp的代码父类加载器肯定是加载不倒的  
    8.     if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) ) {  
    9.         tried_parent= true;  
    10.         try {  
    11.             //这里的parent就是appclassLoader  
    12.             c= _parent.loadClass(name);  //由父类加载,一些系统代码,例如hashMap,set啥的都在这里加载,还有jetty定义的也在这里加载,例如org.mortbay.servlet.NoJspServlet  
    13.             if (Log.isDebugEnabled())  
    14.                 Log.debug("loaded " + c);  
    15.         }  catch (ClassNotFoundException e) {  
    16.             ex= e;  
    17.         }  
    18.     }  
    19.   
    20.     if (c == null) {  
    21.         try  {  
    22.             c= this.findClass(name);  //这里是从当前classLoader的路径下面去加载  
    23.         } catch (ClassNotFoundException e) {  
    24.             ex= e;  
    25.         }  
    26.     }  
    27.   
    28.     if (c == null && _parent!=null && !tried_parent && !isServerPath(name) )  
    29.         c= _parent.loadClass(name);  
    30.   
    31.     if (c == null)  
    32.         throw ex;  
    33.   
    34.     if (resolve)  
    35.         resolveClass(c);  
    36.   
    37.     if (Log.isDebugEnabled())  
    38.         Log.debug("loaded " + c+ " from "+c.getClassLoader());  
    39.       
    40.     return c;  
    41. }  

     

    这段代码其实还算是比较的简单吧。。。这里就可以知道指定parent的重要作用了。。。因为毕竟在我们自己的代码中也会有调用一些类库中的代码。。。而在parent中,却无法加载到我们自己定义的class文件。。。也就实现代码与jetty 服务器本身的隔离。。。。

    又因为每一个web应用程序都有自己的classLoader,也就是先了在同一个服务器下,不同的app之间的代码隔离。。。

    最后再来补充两个方法吧:

    [java] view plaincopy
     
     
    1. //添加class或者jar的路径,如果是文件夹路径的话,那么路径应该以/  结尾  
    2. public void addClassPath(String classPath)  
    3.     throws IOException {  
    4.     if (classPath == null)  
    5.         return;  
    6.           
    7.     StringTokenizer tokenizer= new StringTokenizer(classPath, ",;");  //有可能一次性传入多个classLoader  
    8.     while (tokenizer.hasMoreTokens()) {  
    9.         /* 
    10.          * file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/classes/ 
    11.   file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/aopalliance-1.0.jar 
    12.   file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/commons-collections-3.1.jar 
    13.          */  
    14.         Resource resource= Resource.newResource(tokenizer.nextToken());  //获取当前路径的resource对象  
    15.         if (Log.isDebugEnabled())  
    16.             Log.debug("Path resource=" + resource);  
    17.   
    18.         // Resolve file path if possible  
    19.         File file= resource.getFile();  //获取当前的文件  
    20.         if (file != null) {    
    21.             URL url= resource.getURL();  //获取url  
    22.             addURL(url);   //这个是父类的方法,将会加载这个路径下的class或者jar资源  
    23.         } else {  
    24.             // Add resource or expand jar/  
    25.             if (!resource.isDirectory() && file == null)  
    26.             {  
    27.                 InputStream in= resource.getInputStream();  
    28.                 File tmp_dir=_context.getTempDirectory();  
    29.                 if (tmp_dir==null)  
    30.                 {  
    31.                     tmp_dir = File.createTempFile("jetty.cl.lib",null);  
    32.                     tmp_dir.mkdir();  
    33.                     tmp_dir.deleteOnExit();  
    34.                 }  
    35.                 File lib= new File(tmp_dir, "lib");  
    36.                 if (!lib.exists())  
    37.                 {  
    38.                     lib.mkdir();  
    39.                     lib.deleteOnExit();  
    40.                 }  
    41.                 File jar= File.createTempFile("Jetty-"".jar", lib);  
    42.                   
    43.                 jar.deleteOnExit();  
    44.                 if (Log.isDebugEnabled())  
    45.                     Log.debug("Extract " + resource + " to " + jar);  
    46.                 FileOutputStream out = null;  
    47.                 try  
    48.                 {  
    49.                     out= new FileOutputStream(jar);  
    50.                     IO.copy(in, out);  
    51.                 }  
    52.                 finally  
    53.                 {  
    54.                     IO.close(out);  
    55.                 }  
    56.                   
    57.                 URL url= jar.toURL();  
    58.                 addURL(url);  
    59.             } else {  
    60.                 URL url= resource.getURL();  
    61.                 addURL(url);  //添加class的搜索路径  
    62.             }  
    63.         }  
    64.     }  
    65. }  
    66.   
    67.   
    68.   
    69.   
    70. //添加jar包或压缩文件  
    71. public void addJars(Resource lib)  {  
    72.     if (lib.exists() && lib.isDirectory())  
    73.     {  
    74.         String[] files=lib.list();  //获取当前文件夹下面的所有文件  
    75.         //遍历下面的所有文件  
    76.         for (int f=0;files!=null && f<files.length;f++)  {  
    77.             try {  
    78.                 Resource fn=lib.addPath(files[f]);    //获取这个文件的resource  
    79.                 String fnlc=fn.getName().toLowerCase();  
    80.                 //判断文件的扩展名是否正确  
    81.                 if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip"))  {  
    82.                     String jar=fn.toString();    
    83.                     jar=StringUtil.replace(jar, ",""%2C");  
    84.                     jar=StringUtil.replace(jar, ";""%3B");  
    85.                     addClassPath(jar); //调用addClassPath来将jar包加载  
    86.                 }  
    87.             }  catch (Exception ex) {  
    88.                 Log.warn(Log.EXCEPTION,ex);  
    89.             }  
    90.         }  
    91.     }  
    92. }  

     

    这两个方法的定义应该从名字就能够知道是干嘛的。。用于加载jar包或者class文件的。。最终是通过父类的addUrl的方法来实现这些源文件的加载。。。

     

    好了,到这里jetty中定义的webappclassLoader就算是比较清楚了。。不过比较遗憾没有去分析它的父类URLClassLoader,从而对java的整个classLoader有更深的了解。。以后有机会的话看看吧。。

    这样子就可以开始看jetty中如何创建webContext以及启动这些web应用程序了。。。

  • 相关阅读:
    图片上传
    中间件
    放大镜
    JQ编写楼层效果
    AJAX,PHP,前端简单交互制作输入框效果
    AJAX中使用post,get接收发送数据的区别
    PHP内写css样式
    计算2个日期相差的月份
    react-相关技术栈之-dva/dynamic
    es6相关知识点
  • 原文地址:https://www.cnblogs.com/heyanan/p/6116971.html
Copyright © 2011-2022 走看看