Tomcat ClassLoader研究
Tomcat的ClassLoader层次结构:
Bootstrap |
||
源代码如下:
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