`
zhanshenny
  • 浏览: 259753 次
  • 性别: 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 手动 加载spring mvc demo

    NULL 博文链接:https://nbaertuo.iteye.com/blog/366847

    jetty相关的全部jar包

    jetty-security-9.4.8.v20171121.jar,jetty-io-9.4.8.v20171121.jar,jetty-continuation-9.4.8.v20171121.jar,jetty-client-9.4.8.v20171121.jar,jetty-jmx-9.4.8.v20171121.jar,jetty-plus-9.4.8.v20171121....

    jetty-9.4.6

    jetty-9.4.6

    eclipse jetty插件run-jetty-run-1.3.3

    eclipse jetty插件,从...下载run-jetty-run.zip文件,解压后再编写个links文件丢到eclipse的dropins目录下即可,省去了使用eclipse update方式安装的麻烦。 link文件样例如: path=d:\\eclipse_plugins\\run-jetty-run

    Jetty多版本软件包

    Jetty软件包内容: jetty-distribution-9.4.51.v20230217.tar.gz jetty-distribution-9.4.51.v20230217.zip jetty-home-10.0.15.tar.gz jetty-home-10.0.15.zip jetty-home-11.0.15.tar.gz jetty-home-11.0.15.zip ...

    jetty6 指南书

    jetty是什么 jetty配置 jetty使用 jetty嵌入 jetty启动 jetty部署 jetty教程 jetty嵌入式 jetty

    Jetty cometd(Continuation)学习笔记

    Jetty是一个纯Java实现的开源Servlet容器,Jetty也可以作为HTTP服务器和HTTP客户端,Jetty仪器轻巧...众所周知因为安全的原因,多数浏览器都限制了Ajax跨域请求和javascript加载的时候只能是与当前域下的应用进行交互。

    jetty嵌入式服务器必须的jar包

    jetty嵌入式服务器开发所必须的jar包,本人使用jetty版本为6.1.3,jar包总数为9个,来自jetty:commons-el-1.0.jar,jasper-compiler-5.5.15,jasper-compiler-jdt-5.5.15.jar,jasper-runtime-5.5.15.jar,jetty-...

    jetty相关所有jar包

    jetty相关所有jar包,包含jar包: jetty-continuation-8.1.15.v20140411,jetty-http-8.1.15.v20140411,jetty-io-8.1.15.v20140411,jetty-security-8.1.15.v20140411,jetty-server-8.1.15.v20140411,jetty-util-8.1.15...

    PDF的JETTY文档

    .jetty

    Jetty配置支持https

    Jetty配置支持HTTPS以及受信网站证书生成方式

    maven集成jetty所需jar包maven-jetty-plugin,多版本

    maven集成jetty必须jar包maven-jetty-plugin,内含多个版本

    jetty 学习资料合集

    jetty 学习资料合集 jetty 学习资料合集 jetty 学习资料合集 jetty 学习资料合集

    i-jetty libs包

    android i-jetty servlet-api-2.5.jar jetty-servlet-7.6.0.RC4.jar jetty-server-7.6.0.RC4.jar jetty-http-7.6.0.RC4.jar

    jetty 9.2.24

    jetty服务器,9.2版本适合java7+开发环境。 Jetty是一个纯粹的基于Java的网页服务器和Java Servlet容器。 尽管网页服务器通常用来为人们呈现文档,但是Jetty通常在较大的软件框架中用于计算机与计算机之间的通信。 ...

    Jetty嵌入项目代码中示例

    Jetty嵌入项目代码中示例,现我有一示例项目 e:/workspace/web-demo(称为project_home),里面的Web根目录是WebContent。 在project_home建一个jetty目录,子目录如:contexts、etc、lib。 把${jetty_home}/etc...

    Jetty9 配置使用HTTPS证书

    Jetty9 配置使用HTTPS证书,访问你的服务器更安全,更好的配置方法。

    jetty 9.4.9

    jetty 9.4.9, jetty 容器是轻便型容器,启动速度的确可以 ,附带有servlet-api.jar 和jsp-api.jar 两个jar包

    jetty源代码下载

    jetty源代码下载 jetty源代码下载 jetty源代码下载 jetty源代码下载

    jetty在eclipse中配置

    自己写的jetty6在eclipse启动中配置说明

Global site tag (gtag.js) - Google Analytics