Jetty是个servlet容器,要理解servlet容器的class loader,要先看 JSR315 servlet 3 中对web application class loader的要求,比较重要的地方加了中文解释。
10.7.2 Web Application Class Loader
The class loader that a container uses to load a servlet in a WAR must allow the
developer to load any resources contained in library JARs within the WAR following
normal Java SE semantics using getResource. As described in the Java EE license
agreement, servlet containers that are not part of a Java EE product should not allow
the application to override Java SE platform classes, such as those in the java.* and
javax.* namespaces,
不允许应用去覆盖JAVA SE的系统类
that Java SE does not allow to be modified. The container
should not allow applications to override or access the container’s implementation
classes.
不允许应用覆盖或存取容器的实现类
It is recommended also that the application class loader be implemented so
that classes and resources packaged within the WAR are loaded in preference to
classes and resources residing in container-wide library JARs. An implementation
MUST also guarantee that for every web application deployed in a container, a call
to Thread.currentThread.getContextClassLoader() MUST return a
ClassLoader instance that implements the contract specified in this section.
每个应用调用getContextClassLoader()返回的都应该是实现了本规范中定义的class loader。
Furthermore, the ClassLoader instance MUST be a separate instance for each
deployed web application.
每个应用的class loader必须要是独立的实例。
The container is required to set the thread context
ClassLoader as described above before making any callbacks (including listener
callbacks) into the web application, and set it back to the original ClassLoader,
once the callback returns.
WebAppClassLoader会对应一个WebAppContext,
Jetty 的WebAppClassLoader的具体实现,有些地方加了注释:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class c= findLoadedClass(name); //查找已经载入的class ClassNotFoundException ex= null; boolean tried_parent= false; if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) ) //判断是否是parent优先模式或系统路径(WebAppContext中定义的_systemClasses, 如果是system class,则由父classloader完成载入) { tried_parent= true; try { c= _parent.loadClass(name); ... } ... } if (c == null) { try { c= this.findClass(name); //使用自定义的本地方法载入类 } ... } if (c == null && _parent!=null && !tried_parent && !isServerPath(name) ) //类未载入,父classloader不是空,没有用父classloader载入过,不是系统类 //则用父classloader载入尝试 c= _parent.loadClass(name); if (c == null) throw ex; if (resolve) resolveClass(c); return c; }
上面类加载器中系统类的定义根据包名划分,根据servlet规范,Jetty的web app classloader不会加载这些类:
private String[] _systemClasses = { "java.", "javax.", "org.mortbay.", "org.xml.", "org.w3c.", "org.apache.commons.logging.", "org.apache.log4j." };
判断是否是parent优先模式由WebAppContext中的一段来解决:
private boolean _parentLoaderPriority= Boolean.getBoolean("org.mortbay.jetty.webapp.parentLoaderPriority");