Tomcat7.0源码分析——类加载体系

阅读: 评论:0

Tomcat7.0源码分析——类加载体系

Tomcat7.0源码分析——类加载体系

前言

Tomcat遵循J2EE规范,实现了Web容器。很多有关web的书籍和文章都离不开对Tomcat的分析,初学者可以从Tomcat的实现对J2EE有更深入的了解。此外,Tomcat还根据Java虚拟机规范实现了经典的双亲委派模式的类加载体系。本文基于Tomcat7.0的Java源码,对其类加载体系进行分析。

概述

首先简单介绍下Java虚拟机规范中提到的主要类加载器;
  • Bootstrap Loader:加载lib目录下或者Property(“sun.boot.class.path”)、或者-XBootclasspath所指定的路径或jar。
  • Extended Loader:加载libext目录下或者Property(“dirs”) 所指定的 路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -dirs=d:projectstestprojclasses HelloWorld。
  • AppClass Loader:加载Property("java.class.path")所指定的 路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld。
根据java虚拟机的双亲委派模式的原则,类加载器在加载一个类时,首先交给父类加载器加载,层层往上直到Bootstrap Loader。也就是一个类最先由Bootstrap Loader加载,如果没有加载到,则交给下一层的类加载器加载,如果没有加载到,则依次层层往下,直到最下层的类加载器。这也就是说,凡是能通过父一级类加载器加载到的类,对于子类也是可见的。因此可以利用双亲委派模式的特性,使用类加载器对不同路径下的jar包或者类进行环境隔离。

然后用一张图片来展示Tomcat的类加载体系:

这里结合之前对双亲委派模式的类加载过程的描述,对上图所示类加载体系进行介绍:
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方法的部分代码见代码清单1。

代码清单1 Bootstrap的init方法的部分实现

    /*** Initialize daemon.*/public void init()throws Exception{// Set Catalina pathsetCatalinaHome();setCatalinaBase();initClassLoaders();Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);// 省略后边的代码

 代码清单1中,我们首先关注initClassLoaders方法的实现,见代码清单2.initClassLoaders方法用来初始化commonLoader、catalinaLoader、sharedLoader。

代码清单2 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' envmonLoader&#Class().getClassLoader();}catalinaLoader = createClassLoader("server", commonLoader);sharedLoader = createClassLoader("shared", commonLoader);} catch (Throwable t) {("Class loader creation threw exception", t);it(1);}}

 从代码清单2中看到创建类加载器是通过调用createClassLoader方法实现的,createClassLoader的实现见代码清单3.

代码清单3 createClassLoader方法的实现

    private ClassLoader createClassLoader(String name, ClassLoader parent)throws Exception {String value = Property(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 = Token();// Local repositoryboolean 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 repositorytry {new URL(repository);repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_URL);continue;} catch (MalformedURLException e) {// Ignore}if (dsWith("*.jar")) {repository = repository.substring(0, repository.length() - "*.jar".length());repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_GLOB);} else if (dsWith(".jar")) {repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_JAR);} else {repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_DIR);}}String[] locations = Array(new String[0]);Integer[] types = Array(new Integer[0]);ClassLoader classLoader = ateClassLoader(locations, types, parent);// Retrieving MBean serverMBeanServer mBeanServer = null;if (MBeanServerFactory.findMBeanServer(null).size() > 0) {mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);} else {mBeanServer = PlatformMBeanServer();}// Register the server classloaderObjectName objectName =new ObjectName("Catalina:type=ServerClassLoader,name=" + name);isterMBean(classLoader, objectName);return classLoader;}

createClassLoader方法的执行步骤如下:

  1. 获取各个类加载器相应的资源配置文件(分别为common.loader、server.loader、shared.loader),从中获取类资源路径的配置信息;
  2. 解析类资源路径下的各个资源位置和类型,也包括对jar资源的检查;
  3. 调用ateClassLoader(locations, types, parent)方法创建ClassLoader;
  4. 将ClassLoader注册到JMX服务中,有个JMX的内容可以参照《Tomcat7.0源码分析——生命周期管理 》一文中的相关介绍。

我们回头看看代码清单1中的SecurityClassLoad.securityClassLoad(catalinaLoader)的实现,见代码清单4.这说明加载Tomcat容器本身的类资源的确是使用catalinaLoader来完成的。

代码清单4 securityClassLoad的实现

    public static void securityClassLoad(ClassLoader loader)throws Exception {if( SecurityManager() == null ){return;}loadCorePackage(loader);loadLoaderPackage(loader);loadSessionPackage(loader);loadUtilPackage(loader);loadJavaxPackage(loader);loadCoyotePackage(loader);        loadTomcatPackage(loader);}

securityClassLoad方法主要加载Tomcat容器所需的class,包括:

  • Tomcat核心class,即org.路径下的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.tor路径下的class;
  • Tomcat其它工具类的class,也是org.apache.catalina.util路径下的class;

我们以加载Tomcat核心class的loadCorePackage方法为例,其实现见代码清单5所示。

代码清单5 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");}

 至此,有关commonLoader、catalinaLoader和sharedLoader三个类加载器的初始化以及使用catalinaLoader加载Tomcat容器自身类资源的内容已经介绍完了,但是我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,根据《Tomcat7.0源码分析——生命周期管理 》一文的内容,我们知道启动StandardContext时会最终调用其startInternal方法,其实现见代码清单6.

代码清单6 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*/@Overrideprotected synchronized void startInternal() throws LifecycleException {// 省略前边的代码 if (getLoader() == null) {WebappLoader webappLoader = new WebappLoader(getParentClassLoader());webappLoader.setDelegate(getDelegate());setLoader(webappLoader);}// 省略中间的代码 // Start our subordinate components, if anyif ((loader != null) && (loader instanceof Lifecycle))((Lifecycle) loader).start(); // 省略后边的代码 }

 从代码清单6看到首先创建WebappLoader实例,然后调用WebappLoader的start方法,start又调用了startInternal方法,WebappLoader的startInternal的实现见代码清单7.

代码清单7 WebappLoader的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*/@Overrideprotected void startInternal() throws LifecycleException {// Register a stream handler factory for the JNDI protocolURLStreamHandlerFactory streamHandlerFactory =new DirContextURLStreamHandlerFactory();if (first) {first = false;try {URL.setURLStreamHandlerFactory(streamHandlerFactory);} catch (Exception e) {// Log and continue anyway, this is ("Error registering jndi stream handler", e);} catch (Throwable t) {// This is likely a dual registrationlog.info("Dual registration of jndi stream handler: " + t.getMessage());}}// Construct a class loader based on our current repositories listtry {classLoader = createClassLoader();classLoader.Resources());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]);}

 我们看到代码清单7中通过调用createClassLoader来创建类加载器,并且设置其资源路径为当前Webapp下某个context的类资源。最后我们看看createClassLoader的实现,见代码清单8.

代码清单8 createClassLoader的实现

    /*** Create associated classLoader.*/private WebappClassLoader createClassLoader()throws Exception {//loaderClass即字符串org.apache.catalina.loader.WebappClassLoaderClass<?> clazz = Class.forName(loaderClass);WebappClassLoader classLoader = null;if (parentClassLoader == null) {parentClassLoader = ParentClassLoader();}Class<?>[] argTypes = { ClassLoader.class };Object[] args = { parentClassLoader };Constructor<?> constr = Constructor(argTypes);classLoader = (WebappClassLoader) wInstance(args);return classLoader;}

 这里loaderClass的值是字符串org.apache.catalina.loader.WebappClassLoader,通过反射来实例化WebappClassLoader。由于每个Webapp下的类资源由不同的WebappClassLoader负责加载,因此Webapp下各个Context的类资源是独立的。至此,整个Tomcat的类加载体系构建完毕。

此外每个jsp为了实现热替换,会有专门的类加载器负责加载。


后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。


京东:.html 

当当:.html 


本文发布于:2024-02-01 02:40:58,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170672646033286.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:源码   加载   体系
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23