设为首页 收藏本站
查看: 418|回复: 0

[经验分享] jboss,tomcat,jetty 容器的classloader机制

[复制链接]

尚未签到

发表于 2017-1-29 13:32:43 | 显示全部楼层 |阅读模式
背景
  前段时间一直在做应用容器的迁移,将公司的应用容器从jboss,tomcat统一迁移到jetty。在整个迁移过程中遇到最多的潜在问题还是在classloader机制上,这里记录一下希望能对大家有所帮助,避免重复走弯路。
  啥都不说,先来看下遇到的几个问题,比较纠结的问题。

问题1: (jar sealed问题)

Java代码 DSC0000.gif






  • Caused by: java.lang.SecurityException: sealing violation: 
    package
     com.sun.media.jai.util is sealed   


  •         at java.net.URLClassLoader.defineClass(URLClassLoader.java:
    234

    )   

  •         at java.net.URLClassLoader.access$
    000

    (URLClassLoader.java:
    58

    )   

  •         at java.net.URLClassLoader$
    1

    .run(URLClassLoader.java:
    197

    )   

  •         at java.security.AccessController.doPrivileged(Native Method)   

  •         at java.net.URLClassLoader.findClass(URLClassLoader.java:
    190

    )   

  •         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:
    419

    )   

  •         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:
    381

    )   

  •         at java.lang.ClassLoader.defineClass1(Native Method)   

  •         at java.lang.ClassLoader.defineClassCond(ClassLoader.java:
    632

    )  



Caused by: java.lang.SecurityException: sealing violation: package com.sun.media.jai.util is sealed
at java.net.URLClassLoader.defineClass(URLClassLoader.java:234)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:419)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:381)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)


说明: jboss容器运行正常 , jetty容器运行出错



问题2:  (xml解析) 

Java代码






  • Caused by: java.lang.NoSuchMethodError: javax.xml.parsers.SAXParserFactory.newInstance(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljavax/xml/parsers/SAXParserFactory;  




Caused by: java.lang.NoSuchMethodError: javax.xml.parsers.SAXParserFactory.newInstance(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljavax/xml/parsers/SAXParserFactory;


说明: jetty容器运行正常 , tomcat容器运行正常,jboss容器运行异常



问题3:  (xml解析) 

Java代码






  • java.lang.reflect.InvocationTargetException   


  •     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)   

  •     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
    39

    )   

  •     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
    25

    )   

  •     at java.lang.reflect.Method.invoke(Method.java:
    597

    )   

  •     at org.eclipse.jetty.start.Main.invokeMain(Main.java:
    490

    )   

  •     at org.eclipse.jetty.start.Main.start(Main.java:
    634

    )   

  •     at org.eclipse.jetty.start.Main.parseCommandLine(Main.java:
    280

    )   

  •     at org.eclipse.jetty.start.Main.main(Main.java:
    82

    )   

  • Caused by: javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found   

  •     at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:
    134

    )   

  •     at org.eclipse.jetty.xml.XmlParser.<init>(XmlParser.java:
    68

    )   

  •     at org.eclipse.jetty.xml.XmlConfiguration.initParser(XmlConfiguration.java:
    79

    )   

  •     at org.eclipse.jetty.xml.XmlConfiguration.<init>(XmlConfiguration.java:
    112

    )   

  •     at org.eclipse.jetty.xml.XmlConfiguration$
    1

    .run(XmlConfiguration.java:
    1028

    )   

  •     at java.security.AccessController.doPrivileged(Native Method)   

  •     at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:
    983

    )   

  •     ... 
    8

     more  



java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.eclipse.jetty.start.Main.invokeMain(Main.java:490)
at org.eclipse.jetty.start.Main.start(Main.java:634)
at org.eclipse.jetty.start.Main.parseCommandLine(Main.java:280)
at org.eclipse.jetty.start.Main.main(Main.java:82)
Caused by: javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found
at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:134)
at org.eclipse.jetty.xml.XmlParser.<init>(XmlParser.java:68)
at org.eclipse.jetty.xml.XmlConfiguration.initParser(XmlConfiguration.java:79)
at org.eclipse.jetty.xml.XmlConfiguration.<init>(XmlConfiguration.java:112)
at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1028)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:983)
... 8 more

说明: jboss容器运行正常 , jetty容器运行异常



问题4:(mail问题) 

Java代码






  • Caused by: java.lang.ClassNotFoundException: javax.mail.event.TransportListener   


  •         at java.net.URLClassLoader$
    1

    .run(URLClassLoader.java:
    202

    )   

  •         at java.security.AccessController.doPrivileged(Native Method)   

  •         at java.net.URLClassLoader.findClass(URLClassLoader.java:
    190

    )   

  •         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:
    419

    )   

  •         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:
    381

    )   

  •         ... 
    78

     more  



Caused by: java.lang.ClassNotFoundException: javax.mail.event.TransportListener
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:419)
at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:381)
... 78 more

说明: jboss容器运行正常 , jetty容器运行异常

 

  可以说基本都是对应的class , method等找不到,或者类冲突等问题,一看就是比较典型的classloader引发的问题。






下面就来看看对容器classloader机制的分析和对比,相信大家了解了相关classloader机制微妙的区别后,基本也能解析这一类问题了


jboss4.05 classloader机制
  这里早期对jboss classloader几点配置做了下说明,可以参见: http://agapple.iteye.com/blog/791940
  为了和tomcat,jetty有更明显的对比,这里就主要介绍三个参数代码层面上的实现:

Xml代码






  • <
    server
    >


        


  •       

  •    
    <!-- Tomcat 5 Service-->
      

  •    
    <
    mbean


     
    code

    =
    "org.jboss.web.tomcat.tc5.Tomcat5"

      

  •       
    name

    =
    "jboss.web:service=WebServer"

     
    xmbean-dd

    =
    "META-INF/webserver-xmbean.xml"

    >


      

  • ......   

  •   

  • <!-- Get the flag indicating if the normal Java2 parent first class   

  •            loading model should be used over the servlet 2.3 web container first   

  •            model.   

  •       --
    >


      

  •       
    <
    attribute


     
    name

    =
    "Java2ClassLoadingCompliance"

    >


    false
    </
    attribute
    >


      

  •       <!-- A flag indicating if the JBoss Loader should be used. This loader   

  •            uses a unified class loader as the class loader rather than the tomcat   

  •            specific class loader.   

  •            The default is false to ensure that wars have isolated class loading   

  •            for duplicate jars and jsp files.   

  •       --
    >


      

  •       
    <
    attribute


     
    name

    =
    "UseJBossWebLoader"

    >


    true
    </
    attribute
    >


      

  •       <!-- The list of package prefixes that should not be loaded without   

  •          delegating to the parent class loader before trying the web app   

  •          class loader. The packages listed here are those tha are used by   

  •          the web container implementation and cannot be overriden. The format   

  •          is a comma separated list of the package names. There cannot be any   

  •          whitespace between the package prefixes.   

  •          This setting only applies when 
    UseJBossWebLoader

    =
    false

    .   

  •       --
    >


      

  •       
    <
    attribute


     
    name

    =
    "FilteredPackages"

    >


    javax.servlet,org.apache.commons.logging
    </
    attribute
    >


      

  •   

  • .....   

  •   

  • </
    server
    >


      



<server>
<!-- Tomcat 5 Service-->
<mbean code="org.jboss.web.tomcat.tc5.Tomcat5"
name="jboss.web:service=WebServer" xmbean-dd="META-INF/webserver-xmbean.xml">
......
<!-- Get the flag indicating if the normal Java2 parent first class
loading model should be used over the servlet 2.3 web container first
model.
-->
<attribute name="Java2ClassLoadingCompliance">false</attribute>
<!-- A flag indicating if the JBoss Loader should be used. This loader
uses a unified class loader as the class loader rather than the tomcat
specific class loader.
The default is false to ensure that wars have isolated class loading
for duplicate jars and jsp files.
-->
<attribute name="UseJBossWebLoader">true</attribute>
<!-- The list of package prefixes that should not be loaded without
delegating to the parent class loader before trying the web app
class loader. The packages listed here are those tha are used by
the web container implementation and cannot be overriden. The format
is a comma separated list of the package names. There cannot be any
whitespace between the package prefixes.
This setting only applies when UseJBossWebLoader=false.
-->
<attribute name="FilteredPackages">javax.servlet,org.apache.commons.logging</attribute>
.....
</server>
  相信这几个参数大家都应该比较熟知,配置项在 deploy/jbossweb-tomcat55.sar/META-INF/jboss-service.xml中
  下面循着代码来看一下jboss的相关实现:


  • 代码入口: org.jboss.web.tomcat.tc5.Tomcat5, 这里的三个配置都对应于Tomcat5类的属性,默认值就是当前配置的值。
  • Tomcat5会创建一个Deploy进行war包的装载,TomcatDeployer(继承于AbstractWebDeployer)

    Java代码


    • if
       (ctxPath.equals(
      "/"
      ) || ctxPath.equals(
      "/ROOT"
      ) || ctxPath.equals(
      ""
      ))   


    •       {   

    •          log.debug(
      "deploy root context="
       + ctxPath);   

    •          ctxPath = 
      "/"
      ;   

    •          metaData.setContextRoot(ctxPath);   

    •       }   

    •   

    •       URL url = 
      new
       URL(warUrl);   

    •   

    •       ClassLoader loader = Thread.currentThread().getContextClassLoader();   

    •       
      /* If we are using the jboss class loader we need to augment its path
       

    •       to include the WEB-INF/{lib,classes} dirs or else scoped class loading
       

    •       does not see the war level overrides. The call to setWarURL adds these
       

    •       paths to the deployment UCL.
       

    •       */
        


    •       Loader webLoader = 
      null
      ;   

    •       
      if
       (config.isUseJBossWebLoader())  
      // 这里对useJbossWebLoader进行判断,进行不同的classloader处理
        

    •       {   

    •          WebCtxLoader jbossLoader = 
      new
       WebCtxLoader(loader);   

    •          jbossLoader.setWarURL(url);   

    •          webLoader = jbossLoader;   

    •       }   

    •       
      else
        

    •       {   

    •          String[] pkgs = config.getFilteredPackages();   

    •          WebAppLoader jbossLoader = 
      new
       WebAppLoader(loader, pkgs);   

    •          jbossLoader.setDelegate(getJava2ClassLoadingCompliance());   

    •          webLoader = jbossLoader;   

    •       }  



    if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
    {
    log.debug("deploy root context=" + ctxPath);
    ctxPath = "/";
    metaData.setContextRoot(ctxPath);
    }
    URL url = new URL(warUrl);
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    /* If we are using the jboss class loader we need to augment its path
    to include the WEB-INF/{lib,classes} dirs or else scoped class loading
    does not see the war level overrides. The call to setWarURL adds these
    paths to the deployment UCL.
    */
    Loader webLoader = null;
    if (config.isUseJBossWebLoader())  // 这里对useJbossWebLoader进行判断,进行不同的classloader处理
    {
    WebCtxLoader jbossLoader = new WebCtxLoader(loader);
    jbossLoader.setWarURL(url);
    webLoader = jbossLoader;
    }
    else
    {
    String[] pkgs = config.getFilteredPackages();
    WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs);
    jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
    webLoader = jbossLoader;
    }
     
  • 最后通过MBean调用,将classloader设置给对应的tomcat上下文对象: "org.apache.catalina.core.StandardContext";

    Java代码


    • if
       (webLoader != 
      null
      )   


    •       {   

    •          server.setAttribute(objectName, 
      new
       Attribute(
      "loader"
      , webLoader));   

    •       }   

    •       
      else
        

    •       {   

    •          server.setAttribute(objectName, 
      new
       Attribute(
      "parentClassLoader"
      , loader));   

    •       }   

    •   

    •       server.setAttribute(objectName, 
      new
       Attribute(
      "delegate"

      new
       Boolean(getJava2ClassLoadingCompliance()))); 
      // 设置deletegate属性
        



    if (webLoader != null)
    {
    server.setAttribute(objectName, new Attribute("loader", webLoader));
    }
    else
    {
    server.setAttribute(objectName, new Attribute("parentClassLoader", loader));
    }
    server.setAttribute(objectName, new Attribute("delegate", new Boolean(getJava2ClassLoadingCompliance()))); // 设置deletegate属性

  说明:



  • WebCtxLoader
    : 一个对jboss UCL classloader的一个代理而已,setWarUrl也只是将war资源加入到当前jboss的UCL classloader去装载

    Java代码


    • WebCtxLoader(ClassLoader encLoader)   


    •     {   

    •           
      this
      .encLoader = encLoader;   

    •           
      this
      .ctxLoader = 
      new
       ENCLoader(encLoader);   

    •           ClassLoader parent = encLoader;   

    •           
      while
       ((parent 
      instanceof
       RepositoryClassLoader) == 
      false
       && parent != 
      null
      )   

    •          parent = parent.getParent();   

    •           
      this
      .delegate = (RepositoryClassLoader) parent; 
      //delegate对象设置
        

    •     }   

    •   

    •   

    • public
       
      void
       setWarURL(URL warURL) 
      throws
       MalformedURLException   

    •     {   

    •           
      this
      .warURL = warURL;   

    •           String path = warURL.getFile();   

    •           File classesDir = 
      new
       File(path, 
      "WEB-INF/classes"
      );   

    •           
      if
       (classesDir.exists())   

    •           {   

    •          delegate.addURL(classesDir.toURL()); 
      //无非都是委托给delegate loader
        

    •          ctxLoader.addURLInternal(classesDir.toURL());   

    •           }   

    •           File libDir = 
      new
       File(path, 
      "WEB-INF/lib"
      );   

    •           
      if
       (libDir.exists())   

    •           {   

    •          File[] jars = libDir.listFiles();   

    •          
      int
       length = jars != 
      null
       ? jars.length : 
      0

      ;   

    •          
      for
       (
      int
       j = 
      0

      ; j < length; j++)   

    •          {   

    •             delegate.addURL(jars[j].toURL()); 
      //无非都是委托给delegate loader
        

    •             ctxLoader.addURLInternal(jars[j].toURL());   

    •          }   

    •           }   

    •        }  



    WebCtxLoader(ClassLoader encLoader)
    {
    this.encLoader = encLoader;
    this.ctxLoader = new ENCLoader(encLoader);
    ClassLoader parent = encLoader;
    while ((parent instanceof RepositoryClassLoader) == false && parent != null)
    parent = parent.getParent();
    this.delegate = (RepositoryClassLoader) parent; //delegate对象设置
    }

    public void setWarURL(URL warURL) throws MalformedURLException
    {
    this.warURL = warURL;
    String path = warURL.getFile();
    File classesDir = new File(path, "WEB-INF/classes");
    if (classesDir.exists())
    {
    delegate.addURL(classesDir.toURL()); //无非都是委托给delegate loader
    ctxLoader.addURLInternal(classesDir.toURL());
    }
    File libDir = new File(path, "WEB-INF/lib");
    if (libDir.exists())
    {
    File[] jars = libDir.listFiles();
    int length = jars != null ? jars.length : 0;
    for (int j = 0; j < length; j++)
    {
    delegate.addURL(jars[j].toURL()); //无非都是委托给delegate loader
    ctxLoader.addURLInternal(jars[j].toURL());
    }
    }
    }


  • WebAppLoader
    : 对tomcat WebappLoader的一个封装, 同时设置filteredPackages给tomcat WebappLoader进行class控制。

    Java代码


    • public
       
      class
       WebAppLoader 
      extends
       org.apache.catalina.loader.WebappLoader   


    • {   

    •    
      private
       String[] filteredPackages = {   

    •       
      "org.apache.commons.logging"
        

    •    };   

    •   

    •    
      public
       WebAppLoader()   

    •    {   

    •       
      super
      ();   

    •       setLoaderClass(WebAppClassLoader.
      class
      .getName());   

    •    }   

    • .....   

    • }  



    public class WebAppLoader extends org.apache.catalina.loader.WebappLoader
    {
    private String[] filteredPackages = {
    "org.apache.commons.logging"
    };
    public WebAppLoader()
    {
    super();
    setLoaderClass(WebAppClassLoader.class.getName());
    }
    .....
    }
     

看到这里大家相信应该差不多了解了,总结一下: 


  • java2ClassLoadingCompliance是针对useJbossWebLoader=false时而言,是通过设置tomcat WebappClassloader的是否delegate进行控制classloader,实现child first/parent first。
  • java2ClassLoadingCompliance在useJBossWebLoader=true时,不会生效,会被强制设置为false,具体可看WebCtxLoaders,实现了Loader接口,getClassLoader()返回的是一个ctxLoader对jboss WebLoader的一个包装。
  • filteredPackages目前是通过tomcat的classloader机制上进行控制,所以只能是在useJbossWebLoader=false时有效,因为依赖了tomcat的实现。




不过需要注意两点:



  • 目前在jboss4.05上不支持在在war包下WEB-INF/jboss-web.xml的配置,在jboss4.20以后才支持,可见https://jira.jboss.org/browse/JBAS-3047?subTaskView=all


  • jboss 5.0以后,就没有UseJbossWebLoader这一配置项了,实现方式也不是delegate到tomcat,而是独立的一个classloader,统一处理。

jboss classloader机制大家也不必太刻意的去学习,只要适当的了解基本使用。只能说一切都是浮云浮云,随时都可能被改变。


tomcat6.0.30 classloader机制 
  tomcat相比于jboss4.05概念上简介了很多,不过tomcat 6版本相比于tomcat 5有一些变化,少了一些shared lib库的概念。

      Bootstrap
|
System
|
Common
/     \
Webapp1   Webapp2 ...

  一个树状结构,相信大家差不多都知道tomcat默认是以child first装载class,优先载入web中的class类,找不到才会去装载公用类。
  下面就来看一下代码的一些实现: 


  • tomcat启动入口,通过分析StandardContext.start()

    Java代码


    • public
       
      synchronized
       
      void
       start() {   


    • .....   

    •   

    • if
       (getLoader() == 
      null
      ) {   

    •             WebappLoader webappLoader = 
      new
       WebappLoader(getParentClassLoader());   

    •             webappLoader.setDelegate(getDelegate());   

    •             setLoader(webappLoader);   

    •         }   

    • .....   

    • }  



    public synchronized void start() {
    .....
    if (getLoader() == null) {
    WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
    webappLoader.setDelegate(getDelegate());
    setLoader(webappLoader);
    }
    .....
    }


  • 如果getLoader为空,则创建一个新的WebappLoader,注意这也是jboss设置tomcat loader的入口,从而替换默认的tomcat classloader。
  • WebappLoader通过在startInternal启动了一个新的WebappClassLoader

    Java代码


    • // Construct a class loader based on our current repositories list
        


    •         
      try
       {   

    •   

    •             classLoader = createClassLoader();   

    •             classLoader.setResources(container.getResources());   

    •             classLoader.setDelegate(
      this
      .delegate);   

    •             classLoader.setSearchExternalFirst(searchExternalFirst);   

    •             .....   

    •             
      for
       (
      int
       i = 
      0

      ; i < repositories.length; i++) {   

    •                 classLoader.addRepository(repositories);   

    •             }   

    • }<SPAN style=
      "WHITE-SPACE: normal"
      > </SPAN>  



    // Construct a class loader based on our current repositories list
    try {
    classLoader = createClassLoader();
    classLoader.setResources(container.getResources());
    classLoader.setDelegate(this.delegate);
    classLoader.setSearchExternalFirst(searchExternalFirst);
    .....
    for (int i = 0; i < repositories.length; i++) {
    classLoader.addRepository(repositories);
    }



  • 最后就是WebappClassLoader的loadClass方法了,具体的类装载策略。

下面来看一下,loadClass的相关逻辑: 



  • (0.1)
    先检查class是否已经被装载过,findLoadedClass
  • (0.2)通过system classloader装载系统class,所以这里会优先装载jdk的相关代码,这个很重要,也很特别

  • 如果是delegate=true并且不是filterPackage列表,则采用parent first,否则采用child first。

相关代码:

Java代码






  • // (0.1) Check our previously loaded class cache
      


  •     clazz = findLoadedClass(name);   

  •   

  • // (0.2) Try loading the class with the system class loader, to prevent the webapp from overriding J2SE classes
      

  •     clazz = system.loadClass(name);   

  •   

  •  
    boolean
     delegateLoad = delegate || filter(name);   

  • // (1) Delegate to our parent if requested
      

  • if
     (delegateLoad) {   

  •     ClassLoader loader = parent;   

  •             
    if
     (loader == 
    null
    )   

  •                 loader = system;   

  •             
    try
     {   

  •                 clazz = loader.loadClass(name);   

  •         ....   

  •         }   

  •      .....   

  • }   

  •   

  • // (2) Search local repositories
      

  • clazz = findClass(name);   

  •   

  • // (3) Delegate to parent unconditionally
      

  • if
     (!delegateLoad) {   

  •     ClassLoader loader = parent;   

  •             
    if
     (loader == 
    null
    )   

  •                 loader = system;   

  •             
    try
     {   

  •                 clazz = loader.loadClass(name);   

  •         ....   

  •         }   

  •      .....   

  • }  



// (0.1) Check our previously loaded class cache
clazz = findLoadedClass(name);
// (0.2) Try loading the class with the system class loader, to prevent the webapp from overriding J2SE classes
clazz = system.loadClass(name);
boolean delegateLoad = delegate || filter(name);
// (1) Delegate to our parent if requested
if (delegateLoad) {
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
....
}
.....
}
// (2) Search local repositories
clazz = findClass(name);
// (3) Delegate to parent unconditionally
if (!delegateLoad) {
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
....
}
.....
}


 

最后总结一下, 两个参数配置:


  • delegate(child first/parent first),默认值为false,即为child first
  • packageTriggers,执行child first时,排除的package列表,如果匹配了package,即时为delegate=false,也会优先执行parent first策略。

  • tomcat的classloader机制在处理class装载时,会优先尝试使用system进行装载,这样可以优先使用jdk的相关类,这也是问题2的原因。





jetty7.1.20 classloader机制
  jetty与tomcat相比,没有太大上的区别,也是有parent first/child first的概念,filter package等。但还是有些小区别,那先看一下代码:


  • jetty启动入口org.eclipse.jetty.server.Server,通过DeploymentManager进行webapps目录的加载
  • 具体war包加载是通过WebAppProvider,通过createContextHandler创建应用上下文WebAppContext。
  • WebAppContext在初始化时,创建WebAppClassLoader

    Java代码


    • public
       
      void
       preConfigure() 
      throws
       Exception   


    •     {   

    •         
      // Setup configurations
        

    •         loadConfigurations();   

    •   

    •         
      // Setup system classes
        

    •         loadSystemClasses();   

    •            

    •         
      // Setup server classes
        

    •         loadServerClasses();   

    •   

    •         
      // Configure classloader
        

    •         _ownClassLoader=
      false
      ;   

    •         
      if
       (getClassLoader()==
      null
      )   

    •         {   

    •             WebAppClassLoader classLoader = 
      new
       WebAppClassLoader(
      this
      );   

    •             setClassLoader(classLoader);   

    •             _ownClassLoader=
      true
      ;   

    •         }   

    • .....   

    • }  



    public void preConfigure() throws Exception
    {
    // Setup configurations
    loadConfigurations();
    // Setup system classes
    loadSystemClasses();
    // Setup server classes
    loadServerClasses();
    // Configure classloader
    _ownClassLoader=false;
    if (getClassLoader()==null)
    {
    WebAppClassLoader classLoader = new WebAppClassLoader(this);
    setClassLoader(classLoader);
    _ownClassLoader=true;
    }
    .....
    }

  • 最后就是WebAppClassLoader的loadclass方法了。

理解了这些基本概念后,jetty的loadclass方法,逻辑比较好理解:


  • 如果属于system_class或者_parentLoaderPriority=true,并且不是server_class,优先采用parent first进行装载

    Java代码


    • (_context.isParentLoaderPriority() || system_class) && !server_class)  




    (_context.isParentLoaderPriority() || system_class) && !server_class)

  • 否则采用child first,先由WebAppClassLoader进行装载,没找到class再交由父CL装载







里面的有几个概念:


  •  _parentLoaderPriority: 对应child first/parent first。
  • _serverClasses : jetty系统实现类,jetty认为服务实现类,必须在WEB-INF/lib , WEB-INF/classes优先加载。

    默认值代码


    • public final static String[] __dftServerClasses =    


    •     {   

    •         
      "-org.eclipse.jetty.continuation."
      , // don't hide continuation classes   

    •         
      "-org.eclipse.jetty.jndi."
      ,         // don't hide naming classes   

    •         
      "-org.eclipse.jetty.plus.jaas."
      ,    // don't hide jaas classes   

    •         
      "-org.eclipse.jetty.websocket."
      ,    // don't hide websocket extension   

    •         
      "-org.eclipse.jetty.servlet.DefaultServlet"
      , // don't hide default servlet   

    •         
      "org.eclipse.jetty."
                      // hide other jetty classes   

    •     } ; <SPAN style=
      "WHITE-SPACE: normal"
      > </SPAN>  



    public final static String[] __dftServerClasses =
    {
    "-org.eclipse.jetty.continuation.", // don't hide continuation classes
    "-org.eclipse.jetty.jndi.",         // don't hide naming classes
    "-org.eclipse.jetty.plus.jaas.",    // don't hide jaas classes
    "-org.eclipse.jetty.websocket.",    // don't hide websocket extension
    "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
    "org.eclipse.jetty."                // hide other jetty classes
    } ;  


  • __dftSystemClasses :  系统类,类似于tomcat的filterPackage,优先采取parent first进行装载。

    默认值代码


    • public final static String[] __dftSystemClasses =    


    •     {   

    •         
      "java."
      ,                            // Java SE classes (per servlet spec v2.
      5

       / SRV.
      9.7

      .
      2

      )    

    •         
      "javax."
      ,                           // Java SE classes (per servlet spec v2.
      5

       / SRV.
      9.7

      .
      2

      )   

    •         
      "org.xml."
      ,                         // needed by javax.xml   

    •         
      "org.w3c."
      ,                         // needed by javax.xml   

    •         
      "org.apache.commons.logging."
      ,      // TODO: review if special case still needed    

    •         
      "org.eclipse.jetty.continuation."
      ,  // webapp cannot change continuation classes   

    •         
      "org.eclipse.jetty.jndi."
      ,          // webapp cannot change naming classes   

    •         
      "org.eclipse.jetty.plus.jaas."
      ,     // webapp cannot change jaas classes   

    •         
      "org.eclipse.jetty.websocket."
      ,     // WebSocket is a jetty extension   

    •         
      "org.eclipse.jetty.servlet.DefaultServlet"
        // webapp cannot change default servlets   

    •     } ;  



    public final static String[] __dftSystemClasses =
    {
    "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
    "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
    "org.xml.",                         // needed by javax.xml
    "org.w3c.",                         // needed by javax.xml
    "org.apache.commons.logging.",      // TODO: review if special case still needed
    "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
    "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
    "org.eclipse.jetty.plus.jaas.",     // webapp cannot change jaas classes
    "org.eclipse.jetty.websocket.",     // WebSocket is a jetty extension
    "org.eclipse.jetty.servlet.DefaultServlet"  // webapp cannot change default servlets
    } ;





相关配置: 


  • _parentLoaderPriority的变量,可以通过环境变量org.eclipse.jetty.server.webapp.parentLoaderPriority=false/true
  • server_class和system_class,可以通过设置Server attributes

    Xml代码


    • <
      Call


       
      name

      =
      "setAttribute"

      >


        


    •       
      <
      Arg
      >


      org.eclipse.jetty.webapp.systemClasses
      </
      Arg
      >


        

    •       
      <
      Arg
      >


        

    •           
      <
      Array


       
      type

      =
      "java.lang.String"

      >


        

    •               
      <
      Item
      >


      java.
      </
      Item
      >


        

    •           ......   

    •       
      </
      Array


        

    •       
      </
      Arg
      >


        

    • </
      Call
      >


        



    <Call name="setAttribute">
    <Arg>org.eclipse.jetty.webapp.systemClasses</Arg>
    <Arg>
    <Array type="java.lang.String">
    <Item>java.</Item>
    ......
    </Array
    </Arg>
    </Call>











 

总结和对比一下(jboss,tomcat,jetty)容器的classloader机制

容器jboss(4.05)tomcat(6.0.30)jetty(7.1.20)
支持child/parent first设置(默认值)
Java2ClassLoadingCompliance=false
delegate=false_parentLoaderPriority=false
过滤package配置
FilteredPackages

默认值: javax.servlet,org.apache.commons.logging


packageTriggers

默认配置:org.apache.commons.logging

systemClasses

默认配置:java. 

javax.

org.xml.

org.w3c.

org.apache.commons.logging.

org.eclipse.jetty.continuation.

org.eclipse.jetty.jndi.

org.eclipse.jetty.plus.jaas.

org.eclipse.jetty.websocket.

org.eclipse.jetty.servlet.DefaultServlet.

特殊性  1. UseJBossWebLoader=false时,过滤packages才能生效

  2. UseJBossWebLoader=true时,不支持过滤packages
  3. jboss 5.0以后UseJBossWebLoader参数将不支持

1. 在执行child/parent判断之前,会委托system classloader装载系统class,比如jdk的lib库  1. 多了一个serverclass配置,如果是serverclass优先采用child first
  2. systemclass默认的配置,多了javax,org.xml,org.w3c配置。

相关文档svn url : http://anonsvn.jboss.org/repos/jbossas/tags/JBoss_4_0_5_GA_CP18


jboss社区classloader文档: http://community.jboss.org/wiki/ClassLoadingConfiguration
  svn url : http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk

  官方classloader机制: http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html


  svn url : http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/tags/jetty-7.2.0.v20101020/

  classloader 官方文档: http://docs.codehaus.org/display/JETTY/Classloading





 

 


 
最后(相关问题分析)

问题1:
  是一个jar sealed问题, 官方说明: http://download.oracle.com/javase/tutorial/deployment/jar/sealman.html


Html代码






Package Sealing: A package within a JAR file can be optionally sealed, which means that all classes defined in that package must be archived in the same JAR file.    



A package might be sealed to ensure version consistency among the classes in your software or as a security measure.   


To seal a package, a Name header needs to be added for the package, followed by a Sealed header, similar to this:   


Name: myCompany/myPackage/   


Sealed: true   


  


The Name header's value is the package's relative pathname. Note that it ends with a '/' to distinguish it from a filename.   

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-334948-1-1.html 上篇帖子: 如何修改tomcat内存使用情况 下篇帖子: ssh整合tomcat启动报错排除
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表