Tomcat ClassLoader研究
2009-08-30 16:23
239 查看
http://tomcat.apache.org/tomcat-4.1-doc/class-loader-howto.html
Tomcat的ClassLoader层次结构:
源代码如下:
org.apache.catalina.startup.Bootstrap类(tomcat主类)
其中:
commonLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed2, null);
创建common classloader,以AppClassLoader为父ClassLoader
catalinaLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
创建catalina classloader,以common classloader为父classloader
sharedLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
创建share classloader,以common classloader为父classloader
Thread.currentThread().setContextClassLoader(catalinaLoader);
设置ContextClassLoader为catalina classloader
org.apache.catalina.startup.ClassLoaderFactory类
ClassLoaderFactory创建的是StandardClassLoader(org.apache.catalina.loader包中)
由Bootstrap类调用Catalina类的process()方法,再调用execute()方法,再调用start()来启动Server实例。
当Tomcat开启每个Context时,是调用的StandardContext的start()方法,其中:
设置Loader为WebappLoader
然后调用:bindThread(),设置当前线程的ClassLoader为WebappLoader的ClassLoader,即为WebappClassLoader
WebappLoader的ClassLoader的赋值如下:
在WebappClassLoader中,其findClass的搜索顺序与一般的ClassLoader的搜索顺序不同。
一般的ClassLoader的搜索顺序为:
将其委托给父ClassLoader,如果父ClassLoader不能载入相应类,则才交给自己处理
但是WebappClassLoader中,其是先由自己来处理,如果不行再委托给父ClassLoader
相关源代码如下:
以下引自tomcat的说明文档,说明了加载类的顺序
Therefore, from the perspective of a web application, class or resource
loading looks in the following repositories, in this order:
/WEB-INF/classes
of your web application
/WEB-INF/lib/*.jar
of your web application
Bootstrap classes of your JVM
System class loader classses (described above)
$CATALINA_HOME/common/classes
$CATALINA_HOME/common/endorsed/*.jar
$CATALINA_HOME/common/lib/*.jar
$CATALINA_BASE/shared/classes
$CATALINA_BASE/shared/lib/*.jar
Tomcat的ClassLoader层次结构:
Bootstrap | System | Common / / Catalina Shared / / Webapp1 Webapp2 ... | ||
org.apache.catalina.startup.Bootstrap类(tomcat主类)
public final class Bootstrap { // ------------------------------------------------------- Static Variables /** * Debugging detail level for processing the startup. */ private static int debug = 0; // ----------------------------------------------------------- Main Program /** * The main program for the bootstrap. * * @param args Command line arguments to be processed */ public static void main(String args[]) { // Set the debug flag appropriately for (int i = 0; i < args.length; i++) { if ("-debug".equals(args[i])) debug = 1; } // Configure catalina.base from catalina.home if not yet set if (System.getProperty("catalina.base") == null) System.setProperty("catalina.base", getCatalinaHome()); // Construct the class loaders we will need ClassLoader commonLoader = null; ClassLoader catalinaLoader = null; ClassLoader sharedLoader = null; try { File unpacked[] = new File[1]; File packed[] = new File[1]; File packed2[] = new File[2]; ClassLoaderFactory.setDebug(debug); unpacked[0] = new File(getCatalinaHome(), "common" + File.separator + "classes"); packed2[0] = new File(getCatalinaHome(), "common" + File.separator + "endorsed"); packed2[1] = new File(getCatalinaHome(), "common" + File.separator + "lib"); commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null); unpacked[0] = new File(getCatalinaHome(), "server" + File.separator + "classes"); packed[0] = new File(getCatalinaHome(), "server" + File.separator + "lib"); catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator + "classes"); packed[0] = new File(getCatalinaBase(), "shared" + File.separator + "lib"); sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); } catch (Throwable t) { log("Class loader creation threw exception", t); System.exit(1); } Thread.currentThread().setContextClassLoader(catalinaLoader); // Load our startup class and call its process() method try { SecurityClassLoad.securityClassLoad(catalinaLoader); // Instantiate a startup class instance if (debug >= 1) log("Loading startup class"); Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader if (debug >= 1) log("Setting startup class properties"); String methodName = "setParentClassLoader"; Class paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); // Call the process() method if (debug >= 1) log("Calling startup class process() method"); methodName = "process"; paramTypes = new Class[1]; paramTypes[0] = args.getClass(); paramValues = new Object[1]; paramValues[0] = args; method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); } catch (Exception e) { System.out.println("Exception during startup processing"); e.printStackTrace(System.out); System.exit(2); } }
其中:
commonLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed2, null);
创建common classloader,以AppClassLoader为父ClassLoader
catalinaLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
创建catalina classloader,以common classloader为父classloader
sharedLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
创建share classloader,以common classloader为父classloader
Thread.currentThread().setContextClassLoader(catalinaLoader);
设置ContextClassLoader为catalina classloader
org.apache.catalina.startup.ClassLoaderFactory类
public static ClassLoader createClassLoader(File unpacked[], File packed[], ClassLoader parent) throws Exception { if (debug >= 1) log("Creating new class loader"); // Construct the "class path" for this class loader ArrayList list = new ArrayList(); // Add unpacked directories if (unpacked != null) { for (int i = 0; i < unpacked.length; i++) { File file = unpacked[i]; if (!file.isDirectory() || !file.exists() || !file.canRead()) continue; if (debug >= 1) log(" Including directory " + file.getAbsolutePath()); URL url = new URL("file", null, file.getCanonicalPath() + File.separator); list.add(url.toString()); } } // Add packed directory JAR files if (packed != null) { for (int i = 0; i < packed.length; i++) { File directory = packed[i]; if (!directory.isDirectory() || !directory.exists() || !directory.canRead()) continue; String filenames[] = directory.list(); for (int j = 0; j < filenames.length; j++) { String filename = filenames[j].toLowerCase(); if (!filename.endsWith(".jar")) continue; File file = new File(directory, filenames[j]); if (debug >= 1) log(" Including jar file " + file.getAbsolutePath()); URL url = new URL("file", null, file.getCanonicalPath()); list.add(url.toString()); } } } // Construct the class loader itself String array[] = (String[]) list.toArray(new String[list.size()]); StandardClassLoader classLoader = null; if (parent == null) classLoader = new StandardClassLoader(array); else classLoader = new StandardClassLoader(array, parent); classLoader.setDelegate(true); return (classLoader); }
ClassLoaderFactory创建的是StandardClassLoader(org.apache.catalina.loader包中)
由Bootstrap类调用Catalina类的process()方法,再调用execute()方法,再调用start()来启动Server实例。
当Tomcat开启每个Context时,是调用的StandardContext的start()方法,其中:
设置Loader为WebappLoader
if (getLoader() == null) { // (2) Required by Manager if (getPrivileged()) { if (debug >= 1) log("Configuring privileged default Loader"); setLoader(new WebappLoader(this.getClass().getClassLoader())); } else { if (debug >= 1) log("Configuring non-privileged default Loader"); setLoader(new WebappLoader(getParentClassLoader())); } }
然后调用:bindThread(),设置当前线程的ClassLoader为WebappLoader的ClassLoader,即为WebappClassLoader
private ClassLoader bindThread() { ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); if (getResources() == null) return oldContextClassLoader; Thread.currentThread().setContextClassLoader (getLoader().getClassLoader()); DirContextURLStreamHandler.bind(getResources()); if (isUseNaming()) { try { ContextBindings.bindThread(this, this); } catch (NamingException e) { // Silent catch, as this is a normal case during the early // startup stages } } return oldContextClassLoader; }
WebappLoader的ClassLoader的赋值如下:
private String loaderClass = "org.apache.catalina.loader.WebappClassLoader"; ...... public void start() throws LifecycleException { // Validate and update our current component state if (started) throw new LifecycleException (sm.getString("webappLoader.alreadyStarted")); if (debug >= 1) log(sm.getString("webappLoader.starting")); lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; if (container.getResources() == null) return; // Register a stream handler factory for the JNDI protocol URLStreamHandlerFactory streamHandlerFactory = new DirContextURLStreamHandlerFactory(); try { URL.setURLStreamHandlerFactory(streamHandlerFactory); } catch (Throwable t) { // Ignore the error here. } // Construct a class loader based on our current repositories list try { classLoader = createClassLoader(); classLoader.setResources(container.getResources()); classLoader.setDebug(this.debug); classLoader.setDelegate(this.delegate); if (container instanceof StandardContext) classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking()); for (int i = 0; i < repositories.length; i++) { classLoader.addRepository(repositories[i]); } // Configure our repositories setRepositories(); setClassPath(); setPermissions(); if (classLoader instanceof Lifecycle) ((Lifecycle) classLoader).start(); // Binding the Webapp class loader to the directory context DirContextURLStreamHandler.bind ((ClassLoader) classLoader, this.container.getResources()); } catch (Throwable t) { throw new LifecycleException("start: ", t); } // Validate that all required packages are actually available validatePackages(); // Start our background thread if we are reloadable if (reloadable) { log(sm.getString("webappLoader.reloading")); try { threadStart(); } catch (IllegalStateException e) { throw new LifecycleException(e); } } } ...... private WebappClassLoader createClassLoader() throws Exception { Class clazz = Class.forName(loaderClass); WebappClassLoader classLoader = null; if (parentClassLoader == null) { // Will cause a ClassCast is the class does not extend WCL, but // this is on purpose (the exception will be caught and rethrown) classLoader = (WebappClassLoader) clazz.newInstance(); } else { Class[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoader) constr.newInstance(args); } return classLoader; }
在WebappClassLoader中,其findClass的搜索顺序与一般的ClassLoader的搜索顺序不同。
一般的ClassLoader的搜索顺序为:
将其委托给父ClassLoader,如果父ClassLoader不能载入相应类,则才交给自己处理
但是WebappClassLoader中,其是先由自己来处理,如果不行再委托给父ClassLoader
相关源代码如下:
Class clazz = null; try { if (debug >= 4) log(" findClassInternal(" + name + ")"); try { clazz = findClassInternal(name); } catch(ClassNotFoundException cnfe) { if (!hasExternalRepositories) { throw cnfe; } } catch(AccessControlException ace) { ace.printStackTrace(); throw new ClassNotFoundException(name); } catch (RuntimeException e) { if (debug >= 4) log(" -->RuntimeException Rethrown", e); throw e; } if ((clazz == null) && hasExternalRepositories) { try { clazz = super.findClass(name); } catch(AccessControlException ace) { throw new ClassNotFoundException(name); } catch (RuntimeException e) { if (debug >= 4) log(" -->RuntimeException Rethrown", e); throw e; } } if (clazz == null) { if (debug >= 3) log(" --> Returning ClassNotFoundException"); throw new ClassNotFoundException(name); } } catch (ClassNotFoundException e) { if (debug >= 3) log(" --> Passing on ClassNotFoundException", e); throw e; }
以下引自tomcat的说明文档,说明了加载类的顺序
Therefore, from the perspective of a web application, class or resource
loading looks in the following repositories, in this order:
/WEB-INF/classes
of your web application
/WEB-INF/lib/*.jar
of your web application
Bootstrap classes of your JVM
System class loader classses (described above)
$CATALINA_HOME/common/classes
$CATALINA_HOME/common/endorsed/*.jar
$CATALINA_HOME/common/lib/*.jar
$CATALINA_BASE/shared/classes
$CATALINA_BASE/shared/lib/*.jar
相关文章推荐
- classloader机制研究(2) -- 应用场景
- Tomcat研究 -- 整体框架
- tomcat与地址栏图标之研究(多浏览器)
- 浅议tomcat与classloader
- tomcat源码研究之源码导入eclipse
- Tomcat ClassLoader机制
- 研究tomcat和apache的整合成功例子
- Liferay Portal额外研究(一):初步在新Tomcat下部署
- Tomcat8源码编译及导入Eclipse中研究
- 关于我对于tomcat6 研究两天的经验总结
- ClassLoader与Tomcat的ClassLoader
- ClassLoader,Thread.currentThread().setContextClassLoader,tomcat的ClassLoader
- 启动tomcat的时候一个奇怪的类找不到的错误研究!
- tomcat 连接池参数研究
- ClassLoader,Thread.currentThread().setContextClassLoader,tomcat的ClassLoader
- 研究Tomcat结构,解决数据源连接数据库
- classloader机制研究(3) --- 类型安全
- 关于tomcat的研究的几篇文章在记在此处吧
- Tomcat8源码编译及导入Eclipse中研究
- 地图的开发研究--基于openlayers+geoserver+tomcat的离线地图--postgis空间数据库