`
zhanshenny
  • 浏览: 264404 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Jetty类加载机制

阅读更多

问题导出

关于主流的Java Web服务器Tomcat、Jetty、WebLogic、WebSphere等,先提出几个问题:

  • 部署在同一个服务器上的两个Web应用程序所使用的Java类库是如何实现相互独立?
  • 部署在同一个服务器上的两个Web应用程序所使用的Java类库是如何实现相互共享?
  • 服务器如何保证自身的安全不受部署的Web应用程序的影响?
  • 支持JSP应用的Web服务器,如何支持HotSwap功能的?

上述问题,可以由Web服务器的类加载系统来实现。例如,Tomcat的ClassLoader体系结构如下所示:

首先回顾一下Jvm标准的类加载体系。

Jvm ClassLoader

1) ClassLoader
Java程序并不是一个原生的可执行文件,而是由许多独立的类文件组成,每一个文件对应一个Java类。这些类文件并非立即全部装入内存的,而是根据程序需要装入内存。ClassLoader专门负责类文件装入到内存。
获取类加载器的方式如下:
       this.getClass().getClassLoader() 得到当前的类加载器
       this.getClass().getClassLoader() .getParent() 得到当前类的父类加载器

 

Java类装载器在Java安全体系结构中起着最关重要的作用,是Java安全沙箱的第一道防线。类装载器体系结构在三个方面对Java的沙箱起作用:

  • 它防止恶意代码去干涉善意的代码
  • 它守护了被信任的类库的边界
  • 它将代码归入某类(称为保护域),该类确定了代码可以进行哪些操作

类装载器体系结构可以防止恶意代码去干涉善意的代码,这是通过为不同的类装载器装入的类提供不同的命名空间来实现的。

 

Btw: 另外一种加载类的方法:Class.forName,Class.forName的一个很常见的用法用来加载数据库驱动。

2) ClassLoader体系结构

  • 启动类加载器(BootStrap ClassLoader): 是最顶层的类加载器,由C++编写而成,并不继承自 java.lang.ClassLoader,并且已经内嵌到JVM中了。主要用来读取Java的核心类库jre/lib/rt.jar
  • 扩展类加载器(Extension ClassLoader): 是用来读取Java的扩展类库,读取jre/lib/ext/*.jar
  • 系统类加载器(App ClassLoader): 它根据Java应用的类路径(classpath)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
  • 自定义类加载器(Custom ClassLoader): 开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * User: niyong@meituan.com
 * Date: 14-8-19
 * Time: 下午5:42
 *
 第一行结果表示:ClassLoaderTest的类加载器是AppClassLoader
 第二行结果表示:AppClassLoader的类加载器是ExtClassLoder
 第三行结果表示:null表示ExtClassLoader的类加载器是Bootstrap ClassLoader
 *
 */
public class ParentClassLoaderTest {
    public static void main(String[] args) {
        ClassLoader loader = ParentClassLoaderTest.class.getClassLoader();
        while (loader != null) {
            System.out.println(loader.getClass().getName());
            loader = loader.getParent();
        }
        System.out.println(loader);
    }
}
 
result:
 
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
3) 双亲委派模型

从1.2版本开始,Java引入了双亲委派模型,从而更好的保证Java平台的安全。

 在此模型下,当一个装载器被请求装载某个类时,它首先委托自己的parent去装载,若parent能装载,则返回这个类所对应的Class对象,若parent不能装载,则由parent的请求者去装载,如果此时自己也不能加载,则产生java.lang.NoClassDefFoundError。具体委托逻辑在java.lang.ClassLoader的loadClass(String name,boolean resolve)方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 先检查,该类是否已经被加载过
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //递归调用,委托给父类类加载器
                    c = parent.loadClass(name, false);
                else {
                    c = findBootstrapClassOrNull(name);
                }
            catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);
                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

      例如,java.lang.String这个类是由Bootstrap ClassLoader加载的,它是最终的父加载器,如果用户自己写一个java.lang.String从自定义的类加载器加载,那么根据双亲委派模型,真正的String由Bootstrap ClassLoader加载,而自定义的java.lang.String永远加载不进来。

      双亲委派模型有一个缺陷,如果父ClassLoader想加载子ClassLoader中的类比较困难,而在有的应用中这种加载方式是需要的,比如JNDI,Servlet.

4) 全盘负责

所谓全盘负责,即当一个ClassLoader加载一个Class的时候,这个Class所依赖和引用的所有Class也由这个ClassLoader负责载入,除非显示的使用另外一个ClassLoader载入。例如,由于java.lang.String是由Bootstrap ClassLoader载入的,那么String中引用的类如CharSequence等默认都是使用Bootstrap ClassLoader载入。 

5) 命名空间

由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间。命名空间由一系列唯一的名称组成,每一个被装载的类有一个名字。JAVA虚拟机为每一个类装载器维护一个名字空间。例如,一旦JAVA虚拟机将一个名为Volcano的类装入一个特定的命名空间,它就不能再装载名为Valcano的其他类到相同的命名空间了。可以把多个Valcano类装入一个JAVA虚拟机中,因为可以通过创建多个类装载器从而在一个JAVA应用程序中创建多个命名空间。

 

Jetty类加载器

Jetty的ClassLoader架构 

Jetty,Tomcat等web容器通常都会对ClassLoader做扩展,因为一个正常的容器至少要保证其内部运行的多个webapp之间:私有的类库不受影响,并且公有的类库可以共享。这正好发挥ClassLoader的层级划分优势。 Jetty中有一个org.mortbay.jetty.webapp.WebAppClassLoader,负责加载一个webapp context中的应用类,WebAppClassLoader以系统类加载器作为parent,用于加载系统类。不过servlet规范使得web容器的ClassLoader比正常的ClassLoader委托模型稍稍复杂,servlet规范要求:

  • WEB-INF/lib和WEB-INF/classes优先于父容器中的类加载,比如WEB-INF/classes下有个XYZ类,classpath下也有个XYZ类,jetty中优先加载的是WEB-INF/classes下的,这与正常的父加载器优先相反(childfirst)。
  •  系统类比如java.lang.String不遵循第一条, WEB-INF/classes或WEB-INF/lib下的类不能替换系统类。不过规范中没有明确规定哪些是系统类,jetty中的实现是按照类的全路径名判断。
  •  Server的实现类不被应用中的类引用,即Server的实现类不能被人和应用类加载器加载。不过,同样的,规范里没有明确规定哪些是Server的实现类,jetty中同样是按照类的全路径名判断。

为了处理上述三个问题,jetty的应用类加载器(org.mortbay.jetty.webapp.WebAppClassLoader)做了些特殊处理。

Jetty的ClassLoader体系结构如下所示:

 

WebAppClassLoader的实现

WebAppClassLoader的构造器

1
2
3
4
5
6
7
8
9
public WebAppClassLoader(ClassLoader parent, WebAppContext context)
    throws IOException
{
    super(new URL[]{},parent!=null?parent
            :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()
                    :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()
                            :ClassLoader.getSystemClassLoader())));
    ......
}

WebAppClassLoader还是按照正常的范式设置parent ClassLoader,如果当前线程上下文中设定了ClassLoader就以当前线程上下文类加载器为父ClassLoader,否则使用WebAppClassLoader的加载器,如果还没有,就采用系统类加载器。

 

下面看一下WebAppClassLoader的loadClass()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException
{
    //检查类是否已经加载
    Class c= findLoadedClass(name);
    ClassNotFoundException ex= null;
    boolean tried_parent= false;
    
    //判断该类是否为系统类或server类
    //如果该类未加载且父加载器不为空且设置了父加载器优先或类类为系统类,则尝试使用父加载器加载该类
    if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) )
    {
        tried_parent= true;
        try
        {
            c= _parent.loadClass(name);
            if (Log.isDebugEnabled())
                Log.debug("loaded " + c);
        }
        catch (ClassNotFoundException e)
        {
            ex= e;
        }
    }
    //如果不是父加载器优先或者父加载器未加载到该类,使用WebAppClassLoader加载该类
    if (c == null)
    {
        try
        {
            c= this.findClass(name);
        }
        catch (ClassNotFoundException e)
        {
            ex= e;
        }
    }
    //如果是不是父加载器优先,并且WebAppClassLoader未加载到该类,且该类不是server类, 尝试使用父加载器加载该类
    if (c == null && _parent!=null && !tried_parent && !isServerPath(name) )
        c= _parent.loadClass(name);
 
    //找到则返回,否则抛出ClassNotFoundException
    if (c == null)
        throw ex;
    if (resolve)
        resolveClass(c);
    if (Log.isDebugEnabled())
        Log.debug("loaded " + c+ " from "+c.getClassLoader());
     
    return c;
}
ClassLoader Priority

上述过程涉及一个加载器优先级的概念,这也是针对前述第一条规范中WEB-INF/lib和WEB-INF/classes类优先的处理。jetty中父加载器优先的配置项可以通过环境变量

   org.eclipse.jetty.server.webapp.parentLoaderPriority=false(默认)/true来设置

也可以通过

   org.eclipse.jetty.webapp.WebAppContext.setParentLoaderPriority(boolean)方法来设置

优于该配置默认是false,因此在load class过程中优先使用WebAppClassLoader加载WEB-INF/lib和WEB-INF/classes中的类。 当将该配置项设为true时需要确认类加载顺序没有问题。

小结:

       如果是parentfirst或者system_class并且不是server_class,则采用parentfirst策略加载;

       如果是childfirst,则加载顺序为:WebAppClassLoader-->bootstraploader-->ExtClassLoader-->AppClassLoader

设置系统类

规范2中约定系统类不能被应用类覆盖,但是没有明确规定哪些时系统类,jetty中以类的package路径名来区分,当类的package路径名位包含于以下路径时,会被认为是系统类。WebAppContext中配置如下:

1
2
3
4
5
6
7
8
9
10
private String[] _systemClasses =
{
    "java.",
    "javax.",
    "org.mortbay.",
    "org.xml.",
    "org.w3c.",
    "org.apache.commons.logging.",
    "org.apache.log4j."
};

因此,我们可以通过 org.eclipse.jetty.webapp.WebAppContext.setSystemClasses(String Array)或者org.eclipse.jetty.webapp.WebAppContext.addSystemClass(String)来设置系统类。 系统类是对多有应用都可见。

设置Server类

规范3中约定Server类不对任何应用可见。Jetty同样是用package路径名来区分哪些是Server类。WebAppContext中配置如下:

1
2
3
4
5
6
7
8
9
10
private String[] _serverClasses =
{
    "-org.mortbay.jetty.plus.annotation.",       // don't hide
    "-org.mortbay.jetty.plus.jaas.",             // don't hide
    "-org.mortbay.jetty.plus.naming.",           // don't hide
    "-org.mortbay.jetty.plus.jaas.",             // don't hide
    "-org.mortbay.jetty.servlet.DefaultServlet"// don't hide
    "org.mortbay.jetty.",
    "org.slf4j."
};

我们可以通过, org.eclipse.jetty.webapp.WebAppContext.setServerClasses(String Array) 或org.eclipse.jetty.webapp.WebAppContext.addServerClass(String)方法设置Server类。 注意,Server类是对所有应用都不可见的,但是WEB-INF/lib下的类可以替换Server类。

自定义WebApp ClassLoader

当默认的WebAppClassLoader不能满足需求时,可以自定义WebApp ClassLoader,不过Jetty建议自定义的ClassLoader要扩展于默认的WebAppClassLoader实现。

 

参考

 http://www.ibm.com/developerworks/cn/java/j-lo-classloader/  深入探讨Java类加载器

分享到:
评论

相关推荐

    Jetty 学习资料汇总

    3. **WebAppContext**:WebAppContext是Jetty中用于部署Web应用程序的核心类,可以配置虚拟主机、类加载器等。 4. **Jetty与WebSocket**:介绍Jetty对WebSocket协议的支持,包括创建WebSocket服务器端点和客户端...

    Jetty权威指南.pdf

    Jetty采用了多级类加载器机制,包括: - **BaseLoader**:加载Jetty自身所需的类。 - **WebAppClassLoader**:为每个Web应用提供独立的类加载器,确保应用之间的隔离。 - **ParentLoader**:继承自BaseLoader,用于...

    jetty-6.1.26.zip

    7. **部署和管理Web应用**:Jetty支持多种方式部署Web应用,包括WAR文件、目录结构、甚至直接从类路径加载。管理工具如jetty-admin和jetty-console可以帮助开发者监控和控制服务器状态。 8. **连接器和适配器**:...

    jetty 9.4.9

    - **可扩展性**:Jetty提供了丰富的API和插件机制,开发者可以自定义拦截器、会话管理策略等,以适应特定需求。 总之,Jetty 9.4.9是一个功能强大且高效的Web服务器和Servlet容器,适合那些寻求轻量级解决方案的...

    i-jetty源码

    5. **配置与启动流程**:跟踪源码中的配置加载和服务器启动过程,这有助于理解i-jetty的运行机制。 通过以上分析,我们可以了解到i-jetty作为一款开源的轻量级Web服务器,其源码不仅展示了Servlet容器的基本工作...

    jetty指南书

    10. **类加载器**:Jetty的类加载机制对于理解和调试应用中的类冲突至关重要。 11. **Connector配置**:涵盖SSL配置以及与其他服务器(如Apache)的集成,如mod_proxy和AJP。 12. **虚拟主机**:如何在Jetty中设置...

    jetty指导书

    在Jetty中,可以自定义类加载器策略,例如通过`web.xml`文件中的`&lt;loader&gt;`元素来指定类加载顺序和隔离级别。 #### 八、Jetty Connector SSL的配置 **8.1 SSL配置** Jetty支持通过HTTPS协议提供安全的Web服务。...

    jetty6 指南书

    7. **类加载器**、**Jetty Connector的SSL配置**、**虚拟主机**、**管理服务器**、**JNDI配置**、**会话与集群**、**性能优化**、**异步Servlet、Ajax和Comet**以及**嵌入Jetty**等内容,都是指南中详细阐述的主题,...

    jetty-6.1.26源码

    `org.eclipse.jetty.plus`和`org.eclipse.jetty.security`等包揭示了这些扩展机制。 通过分析Jetty 6.1.26的源码,开发者可以深入理解Web服务器的工作原理,这对于优化性能、自定义行为或解决特定问题都有极大的...

    jetty嵌入项目实战

    - Jetty支持基本认证、HTTPS等安全机制,可以通过`org.eclipse.jetty.security`包实现。 - 会话管理可以通过`org.eclipse.jetty.server.session`包实现,支持基于内存或数据库的会话存储。 7. **性能优化** - ...

    Jetty 9 Servlet容器

    Jetty的轻量级特性体现在它不需要像其他大型应用服务器那样依赖复杂的类加载器结构和大量的配置。它的设计哲学是简洁、快速和易于理解,这使得Jetty成为嵌入式应用的理想选择。例如,你可以在Java应用中直接嵌入...

    jetty源代码下载

    了解并研究Jetty源代码可以帮助你深入理解Web服务器和Servlet容器的工作机制,提升你的Java Web开发技能。同时,如果你遇到特定问题,查阅源代码也是查找解决方案的有效方法。通过分析Jetty的实现,你可以学习到如何...

    jetty-6.1.14.zip

    这种设计使得Jetty能够灵活配置,只加载实际应用需要的组件,从而降低资源消耗。 2. **Servlet支持** 作为Servlet容器,Jetty能够处理和调度Servlet请求。在Jetty 6.1.14中,它支持Servlet 2.5规范,包括过滤器、...

    jetty相关

    学习Jetty不仅可以提升Java Web开发能力,还能帮助你理解服务器内部的工作机制,为开发更高效、更稳定的Web服务打下坚实基础。无论是初学者还是经验丰富的开发者,都可以通过深入研究Jetty来增强自己的技能集。

    jetty 嵌入式开发源码

    - `Servlet`: Jetty支持Servlet 3.1规范,源码中可以看到`ServletHandler`如何加载和管理Servlet,以及`ServletHolder`如何存储Servlet配置。 4. **配置与启动** 在源码中,你将看到如何通过`Server`实例来配置...

    JettY 部署Web应用程序.doc

    总结来说,Jetty的部署机制灵活且高效,既支持简单快速的静态部署,也支持动态调整的高级部署策略,使其成为开发和测试环境的理想选择。了解这些概念和方法,开发者可以更好地管理和维护在Jetty上的Web应用程序。

    jetty 源码 2018 zip

    在这个"jetty 源码 2018 zip"压缩包中,包含的是Jetty 9.4.10.RC1版本的源代码,这为我们提供了一个深入了解Jetty内部机制的机会。 首先,我们来看看Jetty的核心组件和架构。Jetty主要由以下几个部分组成: 1. **...

    Jetty入门学习资料

    2. **Continuation机制**:通过Continuation支持异步处理,有效处理大量用户请求和长连接,减少资源消耗。 3. **模块化设计**:Jetty的接口设计使得开发者可以方便地替换或扩展其组件,以适应特定的应用需求。 **易...

    容器jetty

    - **模块化设计**:Jetty采用模块化设计,允许用户按需选择并加载所需组件,减小了运行时的内存占用。 - **高性能**:Jetty通过非阻塞I/O模型(NIO)和高效的线程管理,提供高并发处理能力。 - **轻量级**:相比...

    jetty 8.1.15.v20140411

    2. **高性能**:通过高效的事件驱动模型和非阻塞I/O机制,Jetty能够处理大量并发连接,适用于高流量网站和实时Web应用。 3. **支持最新标准**:Jetty 8.x 支持Servlet 3.0规范,包括异步处理、WebSocket等特性,同时...

Global site tag (gtag.js) - Google Analytics