版权声明:本文为博主原创文章,未经博主允许不得转载。
前言
Tomcat遵循J2EE规范,实现了Web容器。很多有关web的书籍和文章都离不开对Tomcat的分析,初学者可以从Tomcat的实现对J2EE有更深入的了解。此外,Tomcat还根据Java虚拟机规范实现了经典的双亲委派模式的类加载体系。本文基于Tomcat7.0的Java源码,对其类加载体系进行分析。概述
首先简单介绍下Java虚拟机规范中提到的主要类加载器;- Bootstrap Loader:加载lib目录下或者System.getProperty(“sun.boot.class.path”)、或者-XBootclasspath所指定的路径或jar。
- Extended Loader:加载lib\ext目录下或者System.getProperty(“java.ext.dirs”) 所指定的 路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld。
- AppClass Loader:加载System.getProperty("java.class.path")所指定的 路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld。
这里对上图所示类加载体系进行介绍:
ClassLoader:Java提供的类加载器抽象类,用户自定义的类加载器需要继承实现
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
源码分析
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,即org.apache.catalina.core路径下的class;
- org.apache.catalina.loader.WebappClassLoader$PrivilegedFindResourceByName;
- Tomcat有关session的class,即org.apache.catalina.session路径下的class;
- Tomcat工具类的class,即org.apache.catalina.util路径下的class;
- javax.servlet.http.Cookie;
- Tomcat处理请求的class,即org.apache.catalina.connector路径下的class;
- Tomcat其它工具类的class,也是org.apache.catalina.util路径下的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;
- }