`
talentkep
  • 浏览: 101947 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

jboss,tomcat,jetty 容器的classloader机制

    博客分类:
阅读更多

背景

     前段时间一直在做应用容器的迁移,将公司的应用容器从jboss,tomcat统一迁移到jetty。在整个迁移过程中遇到最多的潜在问题还是在classloader机制上,这里记录一下希望能对大家有所帮助,避免重复走弯路。

 

啥都不说,先来看下遇到的几个问题,比较纠结的问题。

问题1: (jar sealed问题)

Java代码 复制代码
  1. Caused by: java.lang.SecurityException: sealing violation:  package  com.sun.media.jai.util is sealed   
  2.         at java.net.URLClassLoader.defineClass(URLClassLoader.java: 234 )   
  3.         at java.net.URLClassLoader.access$ 000 (URLClassLoader.java: 58 )   
  4.         at java.net.URLClassLoader$ 1 .run(URLClassLoader.java: 197 )   
  5.         at java.security.AccessController.doPrivileged(Native Method)   
  6.         at java.net.URLClassLoader.findClass(URLClassLoader.java: 190 )   
  7.         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java: 419 )   
  8.         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java: 381 )   
  9.         at java.lang.ClassLoader.defineClass1(Native Method)   
  10.         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代码 复制代码
  1. 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代码 复制代码
  1. java.lang.reflect.InvocationTargetException   
  2.     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)   
  3.     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 39 )   
  4.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 25 )   
  5.     at java.lang.reflect.Method.invoke(Method.java: 597 )   
  6.     at org.eclipse.jetty.start.Main.invokeMain(Main.java: 490 )   
  7.     at org.eclipse.jetty.start.Main.start(Main.java: 634 )   
  8.     at org.eclipse.jetty.start.Main.parseCommandLine(Main.java: 280 )   
  9.     at org.eclipse.jetty.start.Main.main(Main.java: 82 )   
  10. Caused by: javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found   
  11.     at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java: 134 )   
  12.     at org.eclipse.jetty.xml.XmlParser.<init>(XmlParser.java: 68 )   
  13.     at org.eclipse.jetty.xml.XmlConfiguration.initParser(XmlConfiguration.java: 79 )   
  14.     at org.eclipse.jetty.xml.XmlConfiguration.<init>(XmlConfiguration.java: 112 )   
  15.     at org.eclipse.jetty.xml.XmlConfiguration$ 1 .run(XmlConfiguration.java: 1028 )   
  16.     at java.security.AccessController.doPrivileged(Native Method)   
  17.     at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java: 983 )   
  18.     ...  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代码 复制代码
  1. Caused by: java.lang.ClassNotFoundException: javax.mail.event.TransportListener   
  2.         at java.net.URLClassLoader$ 1 .run(URLClassLoader.java: 202 )   
  3.         at java.security.AccessController.doPrivileged(Native Method)   
  4.         at java.net.URLClassLoader.findClass(URLClassLoader.java: 190 )   
  5.         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java: 419 )   
  6.         at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java: 381 )   
  7.         ...  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代码 复制代码
  1. < server >     
  2.       
  3.     <!-- Tomcat 5 Service-->   
  4.     < mbean   code = "org.jboss.web.tomcat.tc5.Tomcat5"   
  5.        name = "jboss.web:service=WebServer"   xmbean-dd = "META-INF/webserver-xmbean.xml" >   
  6. ......   
  7.   
  8. <!-- Get the flag indicating if the normal Java2 parent first class   
  9.            loading model should be used over the servlet 2.3 web container first   
  10.            model.   
  11.       -- >   
  12.        < attribute   name = "Java2ClassLoadingCompliance" > false </ attribute >   
  13.       <!-- A flag indicating if the JBoss Loader should be used. This loader   
  14.            uses a unified class loader as the class loader rather than the tomcat   
  15.            specific class loader.   
  16.            The default is false to ensure that wars have isolated class loading   
  17.            for duplicate jars and jsp files.   
  18.       -- >   
  19.        < attribute   name = "UseJBossWebLoader" > true </ attribute >   
  20.       <!-- The list of package prefixes that should not be loaded without   
  21.          delegating to the parent class loader before trying the web app   
  22.          class loader. The packages listed here are those tha are used by   
  23.          the web container implementation and cannot be overriden. The format   
  24.          is a comma separated list of the package names. There cannot be any   
  25.          whitespace between the package prefixes.   
  26.          This setting only applies when  UseJBossWebLoader = false .   
  27.       -- >   
  28.        < attribute   name = "FilteredPackages" > javax.servlet,org.apache.commons.logging </ attribute >   
  29.   
  30. .....   
  31.   
  32. </ 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的相关实现:

  1. 代码入口: org.jboss.web.tomcat.tc5.Tomcat5, 这里的三个配置都对应于Tomcat5类的属性,默认值就是当前配置的值。
  2. Tomcat5会创建一个Deploy进行war包的装载,TomcatDeployer(继承于AbstractWebDeployer)
    Java代码 复制代码
    1. if  (ctxPath.equals( "/" ) || ctxPath.equals( "/ROOT" ) || ctxPath.equals( "" ))   
    2.       {   
    3.          log.debug( "deploy root context="  + ctxPath);   
    4.          ctxPath =  "/" ;   
    5.          metaData.setContextRoot(ctxPath);   
    6.       }   
    7.   
    8.       URL url =  new  URL(warUrl);   
    9.   
    10.       ClassLoader loader = Thread.currentThread().getContextClassLoader();   
    11.        /* If we are using the jboss class loader we need to augment its path  
    12.       to include the WEB-INF/{lib,classes} dirs or else scoped class loading  
    13.       does not see the war level overrides. The call to setWarURL adds these  
    14.       paths to the deployment UCL.  
    15.       */   
    16.       Loader webLoader =  null ;   
    17.        if  (config.isUseJBossWebLoader())   // 这里对useJbossWebLoader进行判断,进行不同的classloader处理   
    18.       {   
    19.          WebCtxLoader jbossLoader =  new  WebCtxLoader(loader);   
    20.          jbossLoader.setWarURL(url);   
    21.          webLoader = jbossLoader;   
    22.       }   
    23.        else   
    24.       {   
    25.          String[] pkgs = config.getFilteredPackages();   
    26.          WebAppLoader jbossLoader =  new  WebAppLoader(loader, pkgs);   
    27.          jbossLoader.setDelegate(getJava2ClassLoadingCompliance());   
    28.          webLoader = jbossLoader;   
    29.       }  
    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;
          }
     
  3. 最后通过MBean调用,将classloader设置给对应的tomcat上下文对象: "org.apache.catalina.core.StandardContext";
    Java代码 复制代码
    1. if  (webLoader !=  null )   
    2.       {   
    3.          server.setAttribute(objectName,  new  Attribute( "loader" , webLoader));   
    4.       }   
    5.        else   
    6.       {   
    7.          server.setAttribute(objectName,  new  Attribute( "parentClassLoader" , loader));   
    8.       }   
    9.   
    10.       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代码 复制代码
    1. WebCtxLoader(ClassLoader encLoader)   
    2.     {   
    3.            this .encLoader = encLoader;   
    4.            this .ctxLoader =  new  ENCLoader(encLoader);   
    5.           ClassLoader parent = encLoader;   
    6.            while  ((parent  instanceof  RepositoryClassLoader) ==  false  && parent !=  null )   
    7.          parent = parent.getParent();   
    8.            this .delegate = (RepositoryClassLoader) parent;  //delegate对象设置   
    9.     }   
    10.   
    11.   
    12. public   void  setWarURL(URL warURL)  throws  MalformedURLException   
    13.     {   
    14.            this .warURL = warURL;   
    15.           String path = warURL.getFile();   
    16.           File classesDir =  new  File(path,  "WEB-INF/classes" );   
    17.            if  (classesDir.exists())   
    18.           {   
    19.          delegate.addURL(classesDir.toURL());  //无非都是委托给delegate loader   
    20.          ctxLoader.addURLInternal(classesDir.toURL());   
    21.           }   
    22.           File libDir =  new  File(path,  "WEB-INF/lib" );   
    23.            if  (libDir.exists())   
    24.           {   
    25.          File[] jars = libDir.listFiles();   
    26.           int  length = jars !=  null  ? jars.length :  0 ;   
    27.           for  ( int  j =  0 ; j < length; j++)   
    28.          {   
    29.             delegate.addURL(jars[j].toURL());  //无非都是委托给delegate loader   
    30.             ctxLoader.addURLInternal(jars[j].toURL());   
    31.          }   
    32.           }   
    33.        }  
    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代码 复制代码
    1. public   class  WebAppLoader  extends  org.apache.catalina.loader.WebappLoader   
    2. {   
    3.     private  String[] filteredPackages = {   
    4.        "org.apache.commons.logging"   
    5.    };   
    6.   
    7.     public  WebAppLoader()   
    8.    {   
    9.        super ();   
    10.       setLoaderClass(WebAppClassLoader. class .getName());   
    11.    }   
    12. .....   
    13. }  
    public class WebAppLoader extends org.apache.catalina.loader.WebappLoader
    {
       private String[] filteredPackages = {
          "org.apache.commons.logging"
       };
    
       public WebAppLoader()
       {
          super();
          setLoaderClass(WebAppClassLoader.class.getName());
       }
    .....
    }
     

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

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

不过需要注意两点:

  1. 目前在jboss4.05上不支持在在war包下WEB-INF/jboss-web.xml的配置,在jboss4.20以后才支持,可见https://jira.jboss.org/browse/JBAS-3047?subTaskView=all
  2. 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类,找不到才会去装载公用类。

 

下面就来看一下代码的一些实现: 

 

  1. tomcat启动入口,通过分析StandardContext.start()
    Java代码 复制代码
    1. public   synchronized   void  start() {   
    2. .....   
    3.   
    4. if  (getLoader() ==  null ) {   
    5.             WebappLoader webappLoader =  new  WebappLoader(getParentClassLoader());   
    6.             webappLoader.setDelegate(getDelegate());   
    7.             setLoader(webappLoader);   
    8.         }   
    9. .....   
    10. }  
    public synchronized void start() {
    .....
    
    if (getLoader() == null) {
                WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
                webappLoader.setDelegate(getDelegate());
                setLoader(webappLoader);
            }
    .....
    }
  2. 如果getLoader为空,则创建一个新的WebappLoader,注意这也是jboss设置tomcat loader的入口,从而替换默认的tomcat classloader。
  3. WebappLoader通过在startInternal启动了一个新的WebappClassLoader
    Java代码 复制代码
    1. // Construct a class loader based on our current repositories list   
    2.          try  {   
    3.   
    4.             classLoader = createClassLoader();   
    5.             classLoader.setResources(container.getResources());   
    6.             classLoader.setDelegate( this .delegate);   
    7.             classLoader.setSearchExternalFirst(searchExternalFirst);   
    8.             .....   
    9.              for  ( int  i =  0 ; i < repositories.length; i++) {   
    10.                 classLoader.addRepository(repositories[i]);   
    11.             }   
    12. }<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[i]);
                }
    } 
    
  4. 最后就是WebappClassLoader的loadClass方法了,具体的类装载策略。
下面来看一下,loadClass的相关逻辑: 
  1. (0.1) 先检查class是否已经被装载过,findLoadedClass
  2. (0.2)通过system classloader装载系统class,所以这里会优先装载jdk的相关代码,这个很重要,也很特别
  3. 如果是delegate=true并且不是filterPackage列表,则采用parent first,否则采用child first。
相关代码:
Java代码 复制代码
  1. // (0.1) Check our previously loaded class cache   
  2.     clazz = findLoadedClass(name);   
  3.   
  4. // (0.2) Try loading the class with the system class loader, to prevent the webapp from overriding J2SE classes   
  5.     clazz = system.loadClass(name);   
  6.   
  7.   boolean  delegateLoad = delegate || filter(name);   
  8. // (1) Delegate to our parent if requested   
  9. if  (delegateLoad) {   
  10.     ClassLoader loader = parent;   
  11.              if  (loader ==  null )   
  12.                 loader = system;   
  13.              try  {   
  14.                 clazz = loader.loadClass(name);   
  15.         ....   
  16.         }   
  17.      .....   
  18. }   
  19.   
  20. // (2) Search local repositories   
  21. clazz = findClass(name);   
  22.   
  23. // (3) Delegate to parent unconditionally   
  24. if  (!delegateLoad) {   
  25.     ClassLoader loader = parent;   
  26.              if  (loader ==  null )   
  27.                 loader = system;   
  28.              try  {   
  29.                 clazz = loader.loadClass(name);   
  30.         ....   
  31.         }   
  32.      .....   
  33. }  
// (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);
	    ....
 	    }
     .....
}
 

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

  1. delegate(child first/parent first),默认值为false,即为child first
  2. packageTriggers,执行child first时,排除的package列表,如果匹配了package,即时为delegate=false,也会优先执行parent first策略。
  3. tomcat的classloader机制在处理class装载时,会优先尝试使用system进行装载,这样可以优先使用jdk的相关类,这也是问题2的原因。

 

jetty7.1.20 classloader机制

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

  1. jetty启动入口org.eclipse.jetty.server.Server,通过DeploymentManager进行webapps目录的加载
  2. 具体war包加载是通过WebAppProvider,通过createContextHandler创建应用上下文WebAppContext。
  3. WebAppContext在初始化时,创建WebAppClassLoader
    Java代码 复制代码
    1. public   void  preConfigure()  throws  Exception   
    2.     {   
    3.          // Setup configurations   
    4.         loadConfigurations();   
    5.   
    6.          // Setup system classes   
    7.         loadSystemClasses();   
    8.            
    9.          // Setup server classes   
    10.         loadServerClasses();   
    11.   
    12.          // Configure classloader   
    13.         _ownClassLoader= false ;   
    14.          if  (getClassLoader()== null )   
    15.         {   
    16.             WebAppClassLoader classLoader =  new  WebAppClassLoader( this );   
    17.             setClassLoader(classLoader);   
    18.             _ownClassLoader= true ;   
    19.         }   
    20. .....   
    21. }  
    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;
            }
    .....
    }
  4. 最后就是WebAppClassLoader的loadclass方法了。
理解了这些基本概念后,jetty的loadclass方法,逻辑比较好理解:
  1. 如果属于system_class或者_parentLoaderPriority=true,并且不是server_class,优先采用parent first进行装载
    Java代码 复制代码
    1. (_context.isParentLoaderPriority() || system_class) && !server_class)  
    (_context.isParentLoaderPriority() || system_class) && !server_class)
  2. 否则采用child first,先由WebAppClassLoader进行装载,没找到class再交由父CL装载

里面的有几个概念:

  •  _parentLoaderPriority: 对应child first/parent first。
  • _serverClasses : jetty系统实现类,jetty认为服务实现类,必须在WEB-INF/lib , WEB-INF/classes优先加载。
    默认值代码 复制代码
    1. public final static String[] __dftServerClasses =    
    2.     {   
    3.          "-org.eclipse.jetty.continuation." , // don't hide continuation classes   
    4.          "-org.eclipse.jetty.jndi." ,         // don't hide naming classes   
    5.          "-org.eclipse.jetty.plus.jaas." ,    // don't hide jaas classes   
    6.          "-org.eclipse.jetty.websocket." ,    // don't hide websocket extension   
    7.          "-org.eclipse.jetty.servlet.DefaultServlet" , // don't hide default servlet   
    8.          "org.eclipse.jetty."                 // hide other jetty classes   
    9.     } ; <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进行装载。
    默认值代码 复制代码
    1. public final static String[] __dftSystemClasses =    
    2.     {   
    3.          "java." ,                            // Java SE classes (per servlet spec v2. 5  / SRV. 9.7 . 2 )    
    4.          "javax." ,                           // Java SE classes (per servlet spec v2. 5  / SRV. 9.7 . 2 )   
    5.          "org.xml." ,                         // needed by javax.xml   
    6.          "org.w3c." ,                         // needed by javax.xml   
    7.          "org.apache.commons.logging." ,      // TODO: review if special case still needed    
    8.          "org.eclipse.jetty.continuation." ,  // webapp cannot change continuation classes   
    9.          "org.eclipse.jetty.jndi." ,          // webapp cannot change naming classes   
    10.          "org.eclipse.jetty.plus.jaas." ,     // webapp cannot change jaas classes   
    11.          "org.eclipse.jetty.websocket." ,     // WebSocket is a jetty extension   
    12.          "org.eclipse.jetty.servlet.DefaultServlet"   // webapp cannot change default servlets   
    13.     } ;  
    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代码 复制代码
    1. < Call   name = "setAttribute" >   
    2.        < Arg > org.eclipse.jetty.webapp.systemClasses </ Arg >   
    3.        < Arg >   
    4.            < Array   type = "java.lang.String" >   
    5.                < Item > java. </ Item >   
    6.           ......   
    7.        </ Array   
    8.        </ Arg >   
    9. </ 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代码 复制代码
  1. 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.    
  2. A package might be sealed to ensure version consistency among the classes in your software or as a security measure.   
  3. To seal a package, a Name header needs to be added for the package, followed by a Sealed header, similar to this:   
  4. Name: myCompany/myPackage/   
  5. Sealed: true   
  6.   
  7. The Name header's value is the package's relative pathname. Note that it ends with a '/' to distinguish it from a filename.   
    分享到:
    评论

相关推荐

    应用服务架构及性能调优详解

    内容概要:该文档介绍了常见的三种Java应用服务器(JBoss, Tomcat, Jetty)的整体架构及其启动流程,并深入探讨了它们各自的特性与配置要点。此外,文中还详细阐述了应用服务器的关键组件如类加载器(ClassLoader)的...

    Java进阶路线

    - **Nginx, Apache, Tomcat, JBoss, Jetty**:常用的Web服务器。 - **HTML/CSS/JS**:前端技术的基础。 - **JS框架**:React, Angular, Vue.js 等。 - **单点登录, Session同步, SpringSecurity**:安全相关的技术和...

    java程序员的标准

    - **Web服务器**:Tomcat、Jetty、Resin等,支持部署和运行Web应用程序。 #### 七、分布式对象技术 - **RMI (Remote Method Invocation)**:允许在不同虚拟机之间调用方法。 - **RMI-IIOP (Internet Inter-ORB ...

    毕设单片机实战项目基于esp8266的高考倒计时.zip

    【项目资源】: 单片机项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    毕设工坊:专注于计算机毕业设计项目的交流与资源共享平台,涵盖各类技术文档、代码示例及实战经验分享,助力学子顺利完成学业挑战

    毕设工坊:专注于计算机毕业设计项目的交流与资源共享平台,涵盖各类技术文档、代码示例及实战经验分享,助力学子顺利完成学业挑战。

    【window 可视化nvm管理node版本 nvm-desktop】

    【window 可视化nvm管理node版本 nvm-desktop】

    《基于YOLOv8的玉器识别系统》(包含源码、完整数据集、可视化界面、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

    (源码)基于microbit编程语言的mymicrobit扩展插件项目.zip

    # 基于microbit编程语言的mymicrobit扩展插件项目 ## 项目简介 这是一个基于microbit编程语言的mymicrobit扩展插件项目。该项目旨在提供额外的功能和特性,以扩展microbit编程环境。通过此插件,用户可以轻松地在MakeCode环境中进行编程,实现对micro:bit设备的更多控制和功能实现。 ## 项目的主要特性和功能 1. 扩展性提供了丰富的积木块和代码库,允许用户轻松实现复杂的编程逻辑和功能扩展。 2. 图形化编程支持通过积木块形式的图形化编程,降低编程门槛,方便初学者快速上手。 3. 实时预览提供了积木块的实时预览功能,方便用户直观地了解代码块的逻辑和功能。 4. 与MakeCode无缝集成可以直接在MakeCode环境中导入和使用,无需额外的配置和安装。 ## 安装使用步骤

    毕设单片机实战项目基于ESP8266的局域网图片刷新显示系统.zip

    【项目资源】: 单片机项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    《基于YOLOv8的印章分析系统》(包含源码、完整数据集、可视化界面、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

    p111基于django的企业员工管理系统.zip

    项目资源包含:可运行源码+sql文件 适用人群:学习不同技术领域的小白或进阶学习者;可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 项目具有较高的学习借鉴价值,也可拿来修改、二次开发。 有任何使用上的问题,欢迎随时与博主沟通,博主看到后会第一时间及时解答。 开发语言:Python 框架:django Python版本:python3.8 数据库:mysql 5.7 数据库工具:Navicat 开发软件:PyCharm 浏览器:谷歌浏览器

    第三章-局域网-思维导图

    第三章-局域网-思维导图

    机械工程PT300机械故障仿真测试台:高校教学与科研用精密振动分析及故障诊断实验系统了您提供的规范

    内容概要:PT300机械故障综合模拟实验台由瓦仑尼安教学设备有限公司生产,旨在帮助用户深入了解振动特征知识及复杂转子振动频谱分析,实现精密振动分析和精准故障诊断。该实验台能模拟轴承故障、不平衡、不对中、设备松动、转子摩擦等多种机械故障现象,可进行不同转速下的轴承故障频率识别、转子静动平衡模拟试验、设备启停机测试等实验。设备采用高效节能ABB三相交流电动机,配备高精度转速控制和测量模块,确保运行稳定。此外,实验台还设有透明防震安全罩和互锁开关,保障实验安全。; 适合人群:高校师生、科研人员等需要学习或研究机械故障诊断相关理论知识和实践技能的人群。; 使用场景及目标:①用于高校等教育机构的教学,辅助学生理解机械故障诊断的理论知识和实践技能;②满足科研人员进行机械故障诊断算法验证、故障特征分析等科研需求。; 其他说明:PT300机械故障综合模拟实验台的每个部件均经过高精度加工,确保在不同振动状态下稳定运行。用户可根据期望分析特定部件的故障特征。设备尺寸为735mm(长)×310mm(宽)×350mm(高),保修一年,且提供免费操作指导服务。

    Android毕设实战项目基于Android+Django+sqlit3开发.zip

    【项目资源】: 适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    【光子晶体模拟】基于COMSOL弱形式PDE的三维光子晶体能带结构计算与优化:电磁场切向连续性处理及带隙分析系统设计使用COMSOL

    内容概要:本文详细介绍了使用COMSOL Multiphysics的弱形式接口对三维光子晶体进行数值模拟的方法和技巧。文章通过具体的代码示例,解释了如何构建光子晶体的介电常数分布、设置弱形式PDE、处理电磁场切向连续性、应用Floquet周期边界条件以及特征值求解等关键步骤。特别强调了弱形式接口相比传统物理场接口的优势,如灵活性和对复杂边界的处理能力。文中还分享了一些实用的经验和注意事项,如布洛赫边界条件的实现、特征值求解器参数的优化配置以及网格划分的技巧。 适合人群:具备一定电磁学和数值模拟基础的研究人员或工程师,尤其是对光子晶体仿真感兴趣的读者。 使用场景及目标:①理解并掌握COMSOL弱形式接口在光子晶体仿真中的应用;②学习如何通过弱形式设置处理复杂的电磁场问题;③提高对光子晶体能带结构和带隙特性的认识;④掌握特征值求解和网格划分的最佳实践。 阅读建议:由于本文涉及较多的具体代码和物理概念,建议读者在阅读过程中结合COMSOL软件进行实际操作,同时查阅相关电磁理论书籍以加深理解。此外,对于文中提到的一些具体参数设置和技巧,可以通过尝试不同的配置来巩固所学知识。

    (源码)基于Arduino平台的INSPTComputacion2项目.zip

    # 基于Arduino平台的INSPTComputacion2项目 ## 项目简介 INSPTComputacion2是一个基于Arduino平台的开发项目。该项目旨在通过Arduino的硬件和软件能力,实现一系列计算和交互功能。通过此项目,用户可以体验到Arduino在嵌入式系统、物联网和微控制器等领域的强大功能。 ## 项目的主要特性和功能 该项目的主要特性和功能包括但不限于以下几点 1. 嵌入式系统开发利用Arduino的硬件资源,开发嵌入式系统应用。 2. 物联网应用实现Arduino与物联网技术的结合,进行数据采集、传输和控制。 3. 交互设计通过Arduino实现人机交互,如按钮控制、LED显示等。 4. 数据处理利用Arduino进行数据处理和分析,如温度、湿度等环境数据的采集和处理。 ## 安装使用步骤 以下是在已下载本项目源码文件后的安装使用步骤 1. 确保已安装Arduino IDE软件。

    毕业设计物联网实战项目基于云且连接 Internet 的新式应用程序。 可用于建立Web应用、 IoT物联网、移动后端等。.zip

    【项目资源】: 物联网项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    毕业设计物联网实战项目基于touchgfx,调度基于freertos.zip

    【项目资源】: 物联网项目适用于从基础到高级的各种项目,特别是在性能要求较高的场景中,比如操作系统开发、嵌入式编程和底层系统编程。如果您是初学者,可以从简单的控制台程序开始练习;如果是进阶开发者,可以尝试涉及硬件或网络的项目。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。

    Python数据结构-学习笔记

    Python数据结构-学习笔记

    电影在线购票系统(springboot+ssm+vue+mysql)含万字系统详细说明文档

    该系统展示了一个电影在线购票系统的结构图,系统分为前台和后台两部分。前台包括首页、影院信息、电影信息、电影资讯和个人中心等模块,主要面向普通用户,提供电影浏览、选座购票、个人账户管理等功能。后台部分由管理员通过后台模块进行操作,包括系统首页、用户管理、场次管理、时间段管理、影院信息管理、电影分类管理、电影信息管理、订单管理和个人中心等模块,用于系统的维护和管理,如用户信息维护、电影和影院信息更新、订单处理等。整个系统旨在为用户提供便捷的在线购票体验,同时确保后台管理的高效和有序。

Global site tag (gtag.js) - Google Analytics