Tomcat源码学习(五)-- Tomcat_7.0.70 类加载体系分析
2016-07-08 00:00
537 查看
摘要: Tomcat源码学习(五)-- Tomcat_7.0.70 类加载体系分析
启动类加载器(bootstrap classloader)
它用来加载 Java 的核心库,是用原生代码(本地代码,与平台有关)来实现的,并不继承自java.lang.ClassLoader。这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识加的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
扩展类加载器(extensions classloader)
扩展类加载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量java.ext.dir 指定位置中的类库加载到内存中
应用程序类加载器(application classloader)
系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,由于这个类加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序默认的类加载器。
用户自定义的类装载器
用户自定义的类装载器是普通的Java对象,它的类必须派生自java.lang.ClassLoader类。ClassLoader中定义的方法为程序为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,Java虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和所有其它对象一样,用户自定义的类装载器以有Class类的实例都放在内存中的堆区,而装载的类型信息则都放在方法区。
然后在来一张图简要说明Tomcat的类加载体系(图画的不好):
ClassLoader:Java提供的类加载器抽象类,用户自定义的类加载器需要继承实现
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见
initClassLoaders方法:
创建类加载器的createClassLoader方法的实现:
createClassLoader最终使用ClassLoaderFactory.createClassLoader(locations, types, parent)方法创建ClassLoader。
我们在看SecurityClassLoad.securityClassLoad(catalinaLoader);
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方法为例,查看其实现:
至此为止,我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,StandardContext的方法startInternal的部分代码如下:
从上面代码看到最后会调用WebappLoader的start方法:
start又调用了startInternal方法,startInternal的实现如下:
最后我们看看createClassLoader的实现:
至此Tomcat类加载完毕。
1、前言
Tomcat遵循J2EE规范,实现了Web容器。Java虚拟机有自己的一套类加载体系,同样Tomcat也有自己的一套类加载体系。2、概述
首先简单介绍下Java虚拟机的主要的类加载器:启动类加载器(bootstrap classloader)
它用来加载 Java 的核心库,是用原生代码(本地代码,与平台有关)来实现的,并不继承自java.lang.ClassLoader。这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识加的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
扩展类加载器(extensions classloader)
扩展类加载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量java.ext.dir 指定位置中的类库加载到内存中
应用程序类加载器(application classloader)
系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,由于这个类加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序默认的类加载器。
用户自定义的类装载器
用户自定义的类装载器是普通的Java对象,它的类必须派生自java.lang.ClassLoader类。ClassLoader中定义的方法为程序为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,Java虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和所有其它对象一样,用户自定义的类装载器以有Class类的实例都放在内存中的堆区,而装载的类型信息则都放在方法区。
然后在来一张图简要说明Tomcat的类加载体系(图画的不好):
ClassLoader:Java提供的类加载器抽象类,用户自定义的类加载器需要继承实现
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见
3、分析
commonLoader、catalinaLoader和sharedLoader在Tomcat容器初始化的一开始,即调用Bootstrap的init方法时创建。catalinaLoader会被设置为Tomcat主线程的线程上下文类加载器,并且使用catalinaLoader加载Tomcat容器自身容器下的class。Bootstrap的init方法的部分代码如下:/** * Initialize daemon. */ public void init() throws Exception { 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) { handleThrowable(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; value = replace(value); List<Repository> repositories = new ArrayList<Repository>(); StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { String repository = tokenizer.nextToken().trim(); if (repository.length() == 0) { continue; } // Check for a JAR URL repository try { @SuppressWarnings("unused") URL url = new URL(repository); repositories.add( new Repository(repository, RepositoryType.URL)); continue; } catch (MalformedURLException e) { // Ignore } // Local repository if (repository.endsWith("*.jar")) { repository = repository.substring (0, repository.length() - "*.jar".length()); repositories.add( new Repository(repository, RepositoryType.GLOB)); } else if (repository.endsWith(".jar")) { repositories.add( new Repository(repository, RepositoryType.JAR)); } else { repositories.add( new Repository(repository, RepositoryType.DIR)); } } return ClassLoaderFactory.createClassLoader(repositories, parent); }
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); loadCoyotePackage(loader); loadLoaderPackage(loader); loadRealmPackage(loader); loadServletsPackage(loader); loadSessionPackage(loader); loadUtilPackage(loader); loadValvesPackage(loader); loadJavaxPackage(loader); loadConnectorPackage(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 static final void loadCorePackage(ClassLoader loader) throws Exception { final String basePackage = "org.apache.catalina.core."; loader.loadClass (basePackage + "AccessLogAdapter"); loader.loadClass (basePackage + "ApplicationContextFacade$1"); loader.loadClass (basePackage + "ApplicationDispatcher$PrivilegedForward"); loader.loadClass (basePackage + "ApplicationDispatcher$PrivilegedInclude"); loader.loadClass (basePackage + "AsyncContextImpl"); loader.loadClass (basePackage + "AsyncContextImpl$DebugException"); loader.loadClass (basePackage + "AsyncContextImpl$1"); loader.loadClass (basePackage + "AsyncListenerWrapper"); loader.loadClass (basePackage + "ContainerBase$PrivilegedAddChild"); loader.loadClass (basePackage + "DefaultInstanceManager$1"); loader.loadClass (basePackage + "DefaultInstanceManager$2"); loader.loadClass (basePackage + "DefaultInstanceManager$3"); loader.loadClass (basePackage + "DefaultInstanceManager$AnnotationCacheEntry"); loader.loadClass (basePackage + "DefaultInstanceManager$AnnotationCacheEntryType"); loader.loadClass (basePackage + "ApplicationHttpRequest$AttributeNamesEnumerator"); }
至此为止,我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,StandardContext的方法startInternal的部分代码如下:
protected synchronized void startInternal() throws LifecycleException { ...... if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } ...... if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); // 省略后边的代码 }
从上面代码看到最后会调用WebappLoader的start方法:
public final synchronized void start() throws LifecycleException { if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) || LifecycleState.STARTED.equals(state)) { if (log.isDebugEnabled()) { Exception e = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e); } else if (log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStarted", toString())); } return; } if (state.equals(LifecycleState.NEW)) { init(); } else if (state.equals(LifecycleState.FAILED)) { stop(); } else if (!state.equals(LifecycleState.INITIALIZED) && !state.equals(LifecycleState.STOPPED)) { invalidTransition(Lifecycle.BEFORE_START_EVENT); } setStateInternal(LifecycleState.STARTING_PREP, null, false); try { startInternal();//start再次调用了startInternal方法(WebappLoader中的方法) } catch (Throwable t) { // This is an 'uncontrolled' failure so put the component into the // FAILED state and throw an exception. ExceptionUtils.handleThrowable(t); setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t); } if (state.equals(LifecycleState.FAILED)) { // This is a 'controlled' failure. The component put itself into the // FAILED state so call stop() to complete the clean-up. stop(); } else if (!state.equals(LifecycleState.STARTING)) { // Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to. invalidTransition(Lifecycle.AFTER_START_EVENT); } else { setStateInternal(LifecycleState.STARTED, null, false); } }
start又调用了startInternal方法,startInternal的实现如下:
protected void startInternal() throws LifecycleException { if (log.isDebugEnabled()) log.debug(sm.getString("webappLoader.starting")); if (container.getResources() == null) { log.info("No resources for " + container); setState(LifecycleState.STARTING); return; } // Register a stream handler factory for the JNDI protocol URLStreamHandlerFactory streamHandlerFactory = DirContextURLStreamHandlerFactory.getInstance(); 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) { ExceptionUtils.handleThrowable(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.setClearReferencesRmiTargets( ((StandardContext) container).getClearReferencesRmiTargets()); classLoader.setClearReferencesStatic( ((StandardContext) container).getClearReferencesStatic()); classLoader.setClearReferencesStopThreads( ((StandardContext) container).getClearReferencesStopThreads()); classLoader.setClearReferencesStopTimerThreads( ((StandardContext) container).getClearReferencesStopTimerThreads()); classLoader.setClearReferencesHttpClientKeepAliveThread( ((StandardContext) container).getClearReferencesHttpClientKeepAliveThread()); } for (int i = 0; i < repositories.length; i++) { classLoader.addRepository(repositories[i]); } // Configure our repositories setRepositories(); setClassPath(); setPermissions(); ((Lifecycle) classLoader).start(); // Binding the Webapp class loader to the directory context DirContextURLStreamHandler.bind(classLoader, this.container.getResources()); StandardContext ctx=(StandardContext)container; String contextName = ctx.getName(); if (!contextName.startsWith("/")) { contextName = "/" + contextName; } ObjectName cloname = new ObjectName (MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,context=" + contextName + ",host=" + ctx.getParent().getName()); Registry.getRegistry(null, null) .registerComponent(classLoader, cloname, null); } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); log.error( "LifecycleException ", t ); throw new LifecycleException("start: ", t); } setState(LifecycleState.STARTING); }
最后我们看看createClassLoader的实现:
private WebappClassLoaderBase createClassLoader() throws Exception { Class<?> clazz = Class.forName(loaderClass); WebappClassLoaderBase classLoader = null; if (parentClassLoader == null) { parentClassLoader = container.getParentClassLoader(); } Class<?>[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor<?> constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoaderBase) constr.newInstance(args); return classLoader; }
至此Tomcat类加载完毕。
相关文章推荐
- mac下tomcat的配置和jdk的设置 jsp的初级知识
- Tomcat源码阅读之Init过程
- LoadRunner监控Tomcat的几种方法
- Tomcat配置文件常用操作
- a configuration error occurred during startup. place verify the preference field whth the prompt:TomcatJDK name:
- Myeclipse tomcat部署项目至根路径INVALID的问题
- Tomcat配置优化
- 使用JDK自带jvisualvm监控tomcat和java程序
- Jenkins自动部署到(远程)tomcat服务器
- tomcat方法区内存溢出
- 设置TOMCAT启用GZIP压缩
- Linux - tomcat
- eclipse启动tomcat奇慢无比
- Tomcat中JVM内存溢出及合理配置
- Tomcat学习总结(6)——Tomca常用配置详解
- Tomcat学习总结(6)——Tomca常用配置详解
- eclipse tomcat 集成
- 解决eclipse配置Tomcat时找不到server选项
- tomcat启动报[ERROR]Unable to set localhost. This prevents creation of a GUID
- 常见Tomcat无法启动故障