commonLoader、catalinaLoader和sharedLoader在Tomcat容器初始化的一开始,即调用Bootstrap的init方法时创建。catalinaLoader会被设置为Tomcat主线程的线程上下文类加载器,并且使用catalinaLoader加载Tomcat容器自身容器下的class。Bootstrap的init方法的部分代码清单如下:
/** * Initialize daemon. */ public void init() throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // 省略后边的代码
我们接着来看initClassLoaders方法的实现:
private void initClassLoaders() { try { commonLoader = createClassLoader("common", null); if( commonLoader == null ) { // no config file, default to this loader - we might be in a 'single' env. commonLoader=this.getClass().getClassLoader(); } catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { log.error("Class loader creation threw exception", t); System.exit(1); } }
创建类加载器的createClassLoader方法的实现:
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { String value = CatalinaProperties.getProperty(name + ".loader"); if ((value == null) || (value.equals(""))) return parent; ArrayList<String> repositoryLocations = new ArrayList<String>(); ArrayList<Integer> repositoryTypes = new ArrayList<Integer>(); int i; StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { String repository = tokenizer.nextToken(); // Local repository boolean replace = false; String before = repository; while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) { replace=true; if (i>0) { repository = repository.substring(0,i) + getCatalinaHome() + repository.substring(i+CATALINA_HOME_TOKEN.length()); } else { repository = getCatalinaHome() + repository.substring(CATALINA_HOME_TOKEN.length()); } } while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) { replace=true; if (i>0) { repository = repository.substring(0,i) + getCatalinaBase() + repository.substring(i+CATALINA_BASE_TOKEN.length()); } else { repository = getCatalinaBase() + repository.substring(CATALINA_BASE_TOKEN.length()); } } if (replace && log.isDebugEnabled()) log.debug("Expanded " + before + " to " + repository); // Check for a JAR URL repository try { new URL(repository); repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_URL); continue; } catch (MalformedURLException e) { // Ignore } if (repository.endsWith("*.jar")) { repository = repository.substring (0, repository.length() - "*.jar".length()); repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_GLOB); } else if (repository.endsWith(".jar")) { repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_JAR); } else { repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_DIR); } } String[] locations = repositoryLocations.toArray(new String[0]); Integer[] types = repositoryTypes.toArray(new Integer[0]); ClassLoader classLoader = ClassLoaderFactory.createClassLoader (locations, types, parent); // Retrieving MBean server MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = ManagementFactory.getPlatformMBeanServer(); } // Register the server classloader ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader; }
createClassLoader最终使用ClassLoaderFactory.createClassLoader(locations, types, parent)方法创建ClassLoader。我们回头看看SecurityClassLoad.securityClassLoad(catalinaLoader)的实现:
public static void securityClassLoad(ClassLoader loader) throws Exception { if( System.getSecurityManager() == null ){ return; } loadCorePackage(loader); loadLoaderPackage(loader); loadSessionPackage(loader); loadUtilPackage(loader); loadJavaxPackage(loader); loadCoyotePackage(loader); loadTomcatPackage(loader); }
securityClassLoad方法主要加载Tomcat容器所需的class,包括:
我们以加载Tomcat核心class的loadCorePackage方法为例,查看其实现:
private final static void loadCorePackage(ClassLoader loader) throws Exception { String basePackage = "org.apache.catalina."; loader.loadClass (basePackage + "core.ApplicationContextFacade$1"); loader.loadClass (basePackage + "core.ApplicationDispatcher$PrivilegedForward"); loader.loadClass (basePackage + "core.ApplicationDispatcher$PrivilegedInclude"); loader.loadClass (basePackage + "core.AsyncContextImpl"); loader.loadClass (basePackage + "core.AsyncContextImpl$AsyncState"); loader.loadClass (basePackage + "core.AsyncContextImpl$DebugException"); loader.loadClass (basePackage + "core.AsyncContextImpl$1"); loader.loadClass (basePackage + "core.AsyncContextImpl$2"); loader.loadClass (basePackage + "core.AsyncListenerWrapper"); loader.loadClass (basePackage + "core.ContainerBase$PrivilegedAddChild"); loader.loadClass (basePackage + "core.DefaultInstanceManager$1"); loader.loadClass (basePackage + "core.DefaultInstanceManager$2"); loader.loadClass (basePackage + "core.DefaultInstanceManager$3"); loader.loadClass (basePackage + "core.DefaultInstanceManager$4"); loader.loadClass (basePackage + "core.DefaultInstanceManager$5"); loader.loadClass (basePackage + "core.ApplicationHttpRequest$AttributeNamesEnumerator"); }
至此,我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,StandardContext的方法startInternal的部分代码如下:
/** * Start this component and implement the requirements * of {@link LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protected synchronized void startInternal() throws LifecycleException { // 省略前边的代码 if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } // 省略中间的代码 // Start our subordinate components, if any if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); // 省略后边的代码 }
从上面代码看到最后会调用WebappLoader的start方法,start又调用了startInternal方法,startInternal的实现如下:
/** * Start associated {@link ClassLoader} and implement the requirements * of {@link LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protected void startInternal() throws LifecycleException { // Register a stream handler factory for the JNDI protocol URLStreamHandlerFactory streamHandlerFactory = new DirContextURLStreamHandlerFactory(); if (first) { first = false; try { URL.setURLStreamHandlerFactory(streamHandlerFactory); } catch (Exception e) { // Log and continue anyway, this is not critical log.error("Error registering jndi stream handler", e); } catch (Throwable t) { // This is likely a dual registration log.info("Dual registration of jndi stream handler: " + t.getMessage()); } } // Construct a class loader based on our current repositories list try { classLoader = createClassLoader(); classLoader.setResources(container.getResources()); classLoader.setDelegate(this.delegate); classLoader.setSearchExternalFirst(searchExternalFirst); if (container instanceof StandardContext) { classLoader.setAntiJARLocking( ((StandardContext) container).getAntiJARLocking()); classLoader.setClearReferencesStatic( ((StandardContext) container).getClearReferencesStatic()); classLoader.setClearReferencesStopThreads( ((StandardContext) container).getClearReferencesStopThreads()); classLoader.setClearReferencesStopTimerThreads( ((StandardContext) container).getClearReferencesStopTimerThreads()); classLoader.setClearReferencesThreadLocals( ((StandardContext) container).getClearReferencesThreadLocals()); } for (int i = 0; i < repositories.length; i++) { classLoader.addRepository(repositories[i]); }
最后我们看看createClassLoader的实现:
/** * Create associated classLoader. */ private WebappClassLoader createClassLoader() throws Exception { //loaderClass即字符串org.apache.catalina.loader.WebappClassLoader Class<?> clazz = Class.forName(loaderClass); WebappClassLoader classLoader = null; if (parentClassLoader == null) { parentClassLoader = container.getParentClassLoader(); } Class<?>[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor<?> constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoader) constr.newInstance(args); return classLoader; }
至此,整个Tomcat的类加载体系构建完毕。