精华内容
下载资源
问答
  • Tomcat加载器WebappLoader和WebappClassLoader的工作流程。

    在上一篇文章中,主要介绍了jvm中类加载器的工作原理和小demo,在这篇文章中,介绍下Tomcat封装后的加载器。

    Tomcat封装后的加载器,只能够加载指定库中的文件,这里,还拿第二章的程序中的加载器做个例子。

    URLClassLoader loader = null;
    try
    {
    	URL[] urls = new URL[1];
         URLStreamHandler streamHandler = null;
         File classPath = new File(Constants.WEB_ROOT);
         String repository = (new URL("file", null,
         classPath.getAbsolutePath() + File.separator)).toString();
         urls[0] = new URL(null, repository, streamHandler);
         loader = new URLClassLoader(urls);
    } catch (MalformedURLException e)
    {
    	e.printStackTrace();
    }
    介绍下上面用到的URL的构造器, URL(URL context, String spec, URLStreamHandler handler),第一个参数,context,上下文,官方的注释:the context in which to parse the specification,从构造器中的代码的条件(((context != null) && ((newProtocol == null) || newProtocol.equalsIgnoreCase(context.protocol))))可以知道,当传入的上下文中的协议与传入参数中spec中的url协议一致的时候,构造器会使用我们传入的上下文(context)。下面介绍第二个参数,spec,官方的注释:the String  to parse as a URL,它就表示一个URL,通过上面的代码片段,这个参数使用URL的另一个构造器URL(String protocol, String host, String file),这个构造器我之后再说,这个参数所表示的url就是我们的类加载器所加载的类的路径。最后一个参数streamHandler,官方解释:the stream handler for the URL。如果这个参数不为空的话,Check for permission to specify a handler,在构造器中就会检查处理程序的权限。这个处理中的代表类的实际路径的spec参数是非空的,其他两个参数是可以为空的,这个构造器被Tomcat看中的点,就在于那两个参数,可以添加上下文和权限控制。

    下面介绍先刚刚提到的构造器URL(String protocol, String host, String file),第一个参数protocol,官方解释:the name of the protocol to use.第二个参数:the name of the host.,第三个参数:the file on the host.当我们使用这个构造器的时候,他会调取该对象内的另一构造器:代码片段如下

    public URL(String protocol, String host, String file)
                throws MalformedURLException {
            this(protocol, host, -1, file);
        }
    它又搞出来一个新的构造器URL(String protocol, String host, int port, String file),这个构造器比刚才的构造器多了一个端口的参数,当调用上面的构造器的方法的时候,他会使用传入的主机host在默认端口-1,中查找文件file。但是当我看这个构造器的源码的时候,这里并没有结束,它描述路径的方法:String file = (new File(Constants.WEB_ROOT)).getAbsolutePath() + File.separator ; 描述项目下的web_root文件夹的路径

    public URL(String protocol, String host, int port, String file)
            throws MalformedURLException
        {
            this(protocol, host, port, file, null);
        }

    这里的构造器URL(String protocol, String host, int port, String file,URLStreamHandler handler),这是一个最终的构造器。看了这个构造器,我就好奇了,这个东西已经很全了,这个方法不能给构造器添加上下文,其余的添加权限控制类似的东西,这里都可以搞定。通过最开始的demo可以看出,它使用两个url构造器构造一个url实例,为了后面的内容做铺垫呢,为url添加权限,和上下文。

    在Tomcat中,加载器都需要实现Loader接口,回顾一下tomcat加载器的UML结构图:


    从结构图中,可以看出,在Tomcat的加载器中生活在最底层的是WebappClassLoader,下面,介绍下WebappClassLoader对象与WebappLoader对象,WebappLoader可以控制WebAppClassLoader的生死。从上面的结构图中可以知道,它还继承了URLClassLoader类,从WebappLoader的start()方法的开始,Start this component, initializing our associated class loader.在开始的时候,日志组件和Lifecycle接口中做的事情就不累述了,我在后面的会把前面讲到的几个组件联合起来讲一遍,这里只说loader的事情了,看下面代码块:

    1、为JNDI协议注册一个流管理工厂

      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.
     }
    2、创建classloader加载器,运用java中的反射创建classloader加载器,其中在下面代码块中的变量 loaderClass,是定义好了的常量,Tomcat默认加载器的类名:“org.apache.catalina.loader.WebappClassLoader”

            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);
            }
    
    3、装载有特色的classLoader,下面的各种设置中最主要的就是设置类库列表 addRepository,为classloader指定库,不仅如此,在设置完库之后,他又设置权限相关内容,使classLoader加载各种url中,按照与url匹配的协议加载,这样设置之后,对于首先的classLoader,用起来是安全的。

                classLoader.setResources(container.getResources());
                classLoader.setDebug(this.debug);
                classLoader.setDelegate(this.delegate);
                for (int i = 0; i < repositories.length; i++) {
                    classLoader.addRepository(repositories[i]);
                }
                // Configure our repositories
                setRepositories();
                setClassPath();
                setPermissions();
    

    4、组件一旦启动起来,下面这个方法可能会经常调用,重载。WebappLoader 支持自动重载,如果 WEB-INF/classes 或者 WEB-INF/lib 目录被重新编译过,在不重启 Tomcat 的情况下必须自动重新载入这些类。为了实现这个目的,WebappLoader 有一个单独的线程每个 x 秒会检查源的时间戳。x 的值由checkInterval 变量定义,它的默认值是 15,也就是每隔 15 秒会进行一次检查是否需要自动重载。

    if (reloadable) 
    {
      log(sm.getString("webappLoader.reloading"));
      try {
         threadStart();
      } catch (IllegalStateException e) {
         throw new LifecycleException(e);
      }
    }
    5、重载,下面就是重载线程所做的事情:检查classloader是否发生改变,如果发生改变了,这里会通知上下文,上下文会唤醒一个线程 WebappContextNotifier,这个线程运行,就是让容器重载: ((Context) container).reload();

                try {
                    // Perform our modification check
                    if (!classLoader.modified())
                        continue;
                } catch (Exception e) {
                    log(sm.getString("webappLoader.failModifiedCheck"), e);
                    continue;
                }
                // Handle a need for reloading
                notifyContext();
    
    6、缓存。

    每一个可以被加载的类(放在 WEB-INF/classes 目录下的类文件或者 JAR 文件)都被当做一个源。一个源被 org.apache.catalina.loader.ResourceEntry 类表示。一个 ResourceEntry 实例保存一个 byte 类型的数组表示该类、最后修改的数据或者副本等等。所有缓存的源被存放在一个叫做 resourceEntries 的 HashMap 中(在WabappClassLoader中的元素),键值为源名,所有找不到的源都被放在一个名为 notFoundResources 的 HashMap 中。要注意,Tomcat没加载一个类,都会把它放到缓存中,加载器加载类的时候,也是首先在缓存中查找,找不到才会到库中去查找。


    展开全文
  • 加载器    虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块...

    转自:https://blog.csdn.net/fuzhongmin05/article/details/57404890

    Java类加载机制

    类加载器
      

          虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。

          类加载器可以说是Java语言的一项创新,也是Java语言流行的重要原因之一,在类层次划分、OSGi、热部署、代码加密等领域大放异彩,成为了Java技术体系中一块重要的基石

          类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

    双亲委派模型(作用是系统的稳定性)


          从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。

          从Java开发人员的角度来看, 类加载器还可以划分得更细致一些, 绝大部分Java程序都会使用到以下3种系统提供的类加载器。

    1)启动类加载器(Bootstrap ClassLoader):前面已经介绍过,这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-X bootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。

    2)扩展类加载器(Extension ClassLoader):这个加载器由sun.misc.Launcher.ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

    3)应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher.AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Class Path)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

          我们的应用程序都是由这3种类加载器互相配合进行加载的,如果有必要,还可以加入自己定义的类加载器。这些类加载器之间的关系一般如下图所示

    双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

          使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的Class Path中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。如果读者有兴趣的话,可以尝试去编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但永远无法被加载运行。


    双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中,如以下代码所示,逻辑清晰易懂:

    protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    	//首先, 检查请求的类是否已经被加载过了
    	Class c=findLoadedClass(name);
    	if( c== null ){
    		try{
    			if( parent != null ){
    				c = parent.loadClass(name,false);
    			} else {
    				c = findBootstrapClassOrNull(name);
    			}
    		} catch (ClassNotFoundException e) {
    		//如果父类加载器抛出ClassNotFoundException
    		//说明父类加载器无法完成加载请求
    		}
    		if( c == null ) {
    			//在父类加载器无法加载的时候
    			//再调用本身的findClass方法来进行类加载
    			c = findClass(name);
    		}
    	} 
    	if(resolve){
    		resolveClass(c);
    	}
    	return c;
    }

    先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。双亲委派的具体逻辑就实现在这个loadClass()方法之中,JDK 1.2之后已不提倡用户再去覆盖loadClass()方法,而应当把自己的类加载逻辑写到findClass()方法中,在loadClass()方法的逻辑里如果父类加载失败,则会调用自己的findClass()方法来完成加载,这样就可以保证新写出来的类加载器是符合双亲委派规则的。

    双亲模型的缺点:上级加载不了下级的类

    双亲模型是有层级的,如自定义

    ClassLoader(4) -> ApplicationClassLoader(3) -> ExtensionClassLoader(2) -> BootstrapClassLoader(1)

    举个例子:

    JNDI(Java Naming and Directory Interface)服务,JNDI说白了就是把资源取个名字,再根据名字来找资源。JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK 1.3时放进去的rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的Class Path下的JNDI接口提供者(SPI,Service Provider Interface)的代码,但启动类加载器不可能“认识”这些代码,因为启动类加载器的搜索范围中找不到用户应用程序类,那该怎么办?为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器(Application ClassLoader)。

    也就是说可以通过这个线程上下文类加载器来加载下级的类,它默认是ApplicationClassLoader

     

    Tomcat的类加载器架构

     主流的Java Web服务器(也就是Web容器),如Tomcat、Jetty、WebLogic、WebSphere或其他笔者没有列举的服务器,都实现了自己定义的类加载器(一般都不止一个)。因为一个功能健全的Web容器,要解决如下几个问题:

          1)部署在同一个Web容器上的两个Web应用程序所使用的Java类库可以实现相互隔离。这是最基本的需求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相独立使用。

          2)部署在同一个Web容器上的两个Web应用程序所使用的Java类库可以互相共享。这个需求也很常见,例如,用户可能有10个使用Spring组织的应用程序部署在同一台服务器上,如果把10份Spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费——这主要倒不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到Web容器的内存,如果类库不能共享,虚拟机的方法区就会很容易出现过度膨胀的风险。

          3)Web容器需要尽可能地保证自身的安全不受部署的Web应用程序影响。目前,有许多主流的Java Web容器自身也是使用Java语言来实现的。因此,Web容器本身也有类库依赖的问题,一般来说,基于安全考虑,容器所使用的类库应该与应用程序的类库互相独立

          4)支持JSP应用的Web容器,大多数都需要支持HotSwap(热交换)功能。我们知道,JSP文件最终要编译成Java Class才能由虚拟机执行,但JSP文件由于其纯文本存储的特性,运行时修改的概率远远大于第三方类库或程序自身的Class文件。而且ASP、PHP和JSP这些网页应用也把修改后无须重启作为一个很大的“优势”来看待,因此“主流”的Web容器都会支持JSP生成类的热替换,当然也有“非主流”的,如运行在生产模式(Production Mode)下的WebLogic服务器默认就不会处理JSP文件的变化。

          由于存在上述问题,在部署Web应用时,单独的一个Class Path就无法满足需求了,所以各种Web容都“不约而同”地提供了好几个Class Path路径供用户存放第三方类库,这些路径一般都以“lib”或“classes”命名。被放置到不同路径中的类库,具备不同的访问范围和服务对象,通常,每一个目录都会有一个相应的自定义类加载器去加载放置在里面的Java类库。现在,就以Tomcat容器为例,看一看Tomcat具体是如何规划用户类库结构和类加载器的。

          在Tomcat目录结构中,有3组目录(“/common/*”、“/server/*”和“/shared/*”)可以存放Java类库,另外还可以加上Web应用程序自身的目录“/WEB-INF/*”,一共4组,把Java类库放置在这些目录中的含义分别如下:

          ①放置在/common目录中:类库可被Tomcat和所有的Web应用程序共同使用。

          ②放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见。

          ③放置在/shared目录中:类库可被所有的Web应用程序共同使用,但对Tomcat自己不可见。

          ④放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见。

          为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,其关系如下图所示。

    CommonClassLoader(加载/common/*)、CatalinaClassLoader(加载/server/*)、SharedClassLoader(加载/shared/*)和WebappClassLoader(加载/WebApp/WEB-INF/*)是Tomcat自己定义的类加载器,其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器(它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能

    对于Tomcat的6.x版本,只有指定了tomcat/conf/catalina.properties配置文件的server.loader和share.loader项后才会真正建立Catalina ClassLoader和Shared ClassLoader的实例,否则在用到这两个类加载器的地方都会用Common ClassLoader的实例代替,而默认的配置文件中没有设置这两个loader项,所以Tomcat 6.x顺理成章地把/common、/server和/shared三个目录默认合并到一起变成一个/lib目录,这个目录里的类库相当于以前/common目录中类库的作用。这是Tomcat设计团队为了简化大多数的部署场景所做的一项改进,如果默认设置不能满足需要,用户可以通过修改配置文件指定server.loader和share.loader的方式重新启用Tomcat 5.x的加载器架构。

          Tomcat加载器的实现清晰易懂,并且采用了官方推荐的“正统”的使用类加载器的方式。如果读者阅读完上面的案例后,能完全理解Tomcat设计团队这样布置加载器架构的用意,那说明已经大致掌握了类加载器“主流”的使用方式,那么笔者不妨再提一个问题让读者思考一下:前面曾经提到过一个场景,如果有10个Web应用程序都是用Spring来进行组织和管理的话,可以把Spring放到Common或Shared目录下让这些程序共享。Spring要对用户程序的类进行管理,自然要能访问到用户程序的类,而用户的程序显然是放在/WebApp/WEB-INF目录中的,那么被CommonClassLoader或SharedClassLoader加载的Spring如何访问并不在其加载范围内的用户程序呢?如果研究过虚拟机类加载器机制中的双亲委派模型,相信读者可以很容易地回答这个问题。

          分析:如果按主流的双亲委派机制,显然无法做到让父类加载器加载的类去访问子类加载器加载的类,上面在类加载器一节中提到过通过线程上下文方式传播类加载器。

          答案是使用线程上下文类加载器来实现的,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作。看spring源码发现,spring加载类所用的Classloader是通过Thread.currentThread().getContextClassLoader()来获取的,而当线程创建时会默认setContextClassLoader(AppClassLoader),即线程上下文类加载器被设置为AppClassLoader,spring中始终可以获取到这个AppClassLoader(在Tomcat里就是WebAppClassLoader)子类加载器来加载bean,以后任何一个线程都可以通过getContextClassLoader()获取到WebAppClassLoader来getbean了。

     

    所以Java类加载器跟Tomcat类加载器的关系,我觉得,JAVA类加载器定义了加载的规范,Tomcat使用了它这种规范,然后根据自己的情况进行了扩展,Tomcat启动时,就对项目进行加载。所以这里的Tomcat加载也可以理解为Java类加载了,因为之前疑惑,是不是Tomcat加载了一部分,然后虚拟机加载一部分。这个疑惑是错的,因为开头就说了,这个动作放到 “Java虚拟机外部” 去实现,这里的外部就是指Tomcat,   Tomcat启动时,将类加载进虚拟机

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     


     

     

     

     

     

     

     

     

     

     

    展开全文
  • 13.tomcat加载器

    2012-11-13 13:21:13
    库表示加载器查找的地方,源表示加载器中的DirContext对象,它的文档基(document base)指向了上下文的文档基。 一个servlet容器需要一个定制的容器,而不是简单的使用系统的加载器如果使用系统的加载器来加载...

    库(repository)和源(resources)。库表示加载器查找的地方,源表示加载器中的DirContext对象,它的文档基(document base)指向了上下文的文档基。

    一个servlet容器需要一个定制的容器,而不是简单的使用系统的加载器
    如果使用系统的加载器来加载servlet和其他需要的类,这样servlet就可以进入Java虚拟机CLASSPATH环境下面的任何类和类库,这会带来安全隐患
    在Catalina中,加载器使用org.apache.catalina.Loader接口表示
    Tomcat需要一个自己的加载器的另一个原因是它需要支持在WEB-INF/classes或者是WEB-INF/lib目录被改变的时候会重新加载
    要支持类的自动加载功能,一个加载器类必须实现org.apache.catalina.loader.Reloader接口

    Java的类加载机制
    在每次创建一个Java类的实例时候,必须先将该类加载到内存中
    Java虚拟机(JVM)使用类加载器来加载类。Java加载器在Java核心类库和CLASSPATH环境下面的所有类中查找类。
    如果需要的类找不到,会抛出java.lang.ClassNotFoundException异常。
    JVM使用了三种类加载器:bootstrap类加载器、extension类加载器和systen类加载器。
    这三个加载器是父子关系,其中bootstrap类加载器在顶端,而system加载器在结构的最底层。

    其中bootstrap类加载器用于引导JVM,一旦调用java.exe程序,bootstrap类加载器就开始工作。因此,它必须使用本地代码实现,然后加载JVM需要的类到函数中。
    另外,它还负责加载所有的Java核心类,例如java.lang和java.io包。另外bootstrap类加载器还会查找核心类库如rt.jar、i18n.jar等,这些类库根据JVM和操作系统来查找。

    extension类加载器负责加载标准扩展目录下面的类。这样就可以使得编写程序变得简单,只需把JAR文件拷贝到扩展目录下面即可,类加载器会自动的在下面查找。
    不同的供应商提供的扩展类库是不同的,Sun公司的JVM的标准扩展目录是/jdk/jre/lib/ext。

    system加载器是默认的加载器,它在环境变量CLASSPATH目录下面查找相应的类。

    JVM使用哪个类加载器?答案在于委派模型(delegation model),这是出于安全原因
    每次一类需要加载,system类加载器首先调用。但是,它不会马上加载类。相反,它委派该任务给它的父类-extension类加载器。extension类加载器也把任务委派给它的父类bootstrap类加载器
    因此,bootstrap类加载器总是首先加载类。如果bootstrap类加载器不能找到所需要的类的extension类加载器会尝试加载类。
    如果扩展类加载器也失败,system类加载器将执行任务。如果系统类加载器找不到类,一个java.lang.ClassNotFoundException异常。
    为什么需要这样的往返模式?
    委派模型对于安全性是非常重要的。如你所知,可以使用安全管理器来限制访问某个目录。
    现在,恶意的意图有人能写出一类叫做java.lang.Object,可用于访问任何在硬盘上的目录。
    因为JVM的信任java.lang.Object类,它不会关注这方面的活动。因此,如果自定义java.lang.Object被允许加载的安全管理器将很容易瘫痪。

    工作原理。

    当自定义java.lang.Object类在程序中被调用的时候,system类加载器将该请求委派给extension类加载器,然后委派给bootstrap类加载器。
    这样bootstrap类加载器先搜索的核心库,找到标准java.lang.Object并实例化它。
    这样,自定义java.lang.Object类永远不会被加载


    java.lang.ClassLoader抽象类来扩展自己的类加载器。


    -------------------------------------------

    Tomcat的需求自定义自己的类加载器原因包括以下内容
    · 要制定类加载器的某些特定规则
     · 缓存以前加载的类
    · 事先加载类以预备使用

    Loader接口
    一个Tomcat类加载器表示一个Web应用程序加载器,而不是一个类加载器。
    一个加载器必须实现org.apache.catalina.Loader接口。
    加载器的实现使用定制的类加载器org.apache.catalina.loader.WebappClassLoader。
    可以使用Loader接口的getClassLoader方法获取一个网络加载器ClassLoader。

    Loader接口的addReposity方法用于添加一个库,findRepositories方法用于返回一个所有库的队列。
    一个Tomcat的加载器通常跟一个上下文相关联,Loader接口的和getContainer及setContainer方法是建立此关联。
    一个加载器还可以支持重新加载,如果在上下文中的一个或多个类已被修改。这样,一个servlet程序员可以重新编译servlet或辅助类,新类将被重新加载而不需要不重新启动Tomcat加载。
    为了达到重新加载的目的,Loader接口有修改方法。在加载器的实现中,如果在其库中一个或多个类别已被修改,modeify方法必须返回true,因此需要重新加载。
    setReloadable和getReloadable,用于确定加载器中是否可以使用重加载。
    默认情况下,在标准的上下文实现中(org.apache.catalina.core.StandardContext类)重载机制并未启用。
    因此,要使得上下文启动重载机制,需要在server.xml文件添加一些元素如下: <Context path="/myApp" docBase="myApp" debug="0" reloadable="true"/>
    另外,一个加载器的实现可以确定是否委派给父加载器类。为了实现这一点,Loader接口提供了getDelegate和setDelegate方法。

    public interface Loader { 
     public ClassLoader getClassLoader(); 
     public Container getContainer(); 
     public void setContainer(Container container); 
     public DefaultContext getDefaultContext(); 
     public void setDefaultContext(DefaultContext defaultContext); 
     public boolean getDelegate(); public void setDelegate(boolean delegate); 
     public String getInfo(); public boolean getReloadable(); 
     public void setReloadable(boolean reloadable); 
     public void addPropertyChangeListener(PropertyChangeListenerlistener); 
     public void addRepository(String repository); 
     public String[] findRepositories(); 
     public boolean modified(); 
     public void removePropertyChangeListener(PropertyChangeListenerlistener); 
    }

     
    Catalina提供了org.apache.catalina.loader.WebappLoader作为Load接口的实现。
    WebappLoader对象包含一个org.apache.catalina.loader.WebappClassLoader类的实例,该类扩展了Java.netURLClassLoader类。
    Loader接口和它的实现类的结构图



     
    Reloader接口
    要支持自动重新加载,一个加载器的实现必须实现org.apache.catalina.loader.Reloader接口

    public interface Reloader { 
    	public void addRepository(String repository); 
    	public String[] findRepositories ();
    	public boolean modified(); 
    }
    

     

     

    Reloader接口里最重要的方法是modified方法,如果在web应用程序中的servlet任何支持类被修改的时候该方法返回true。
    addRepository方法用于添加一个库而findRepositories方法用于返回实现了Reloader接口的加载器的所有的库。


    WebappLoader类
    org.apache.catalina.loader.WebappLoader类是Loader接口的实现,它表示一个web应用程序的加载器,负责给web应用程序加载类。
    WebappLoader创建一个org.apache.catalina.loader.WebappClassLoader类的实例作为它的类加载器。
    像其他的Catalina组件一样,WebappLoader实现了org.apache.catalina.Lifecycle接口,可有由关联容器启动和停止。
    WebappLoader类还实现了java.lang.Runnable接口,所以可以通过一个线程来重复的调用modified方法,如果modified方法返回true,WebappLoader实例同志它的关联容器。

    WebappLoader类的start方法被调用的时候,将会完成下面几项重要任务:
    创建一个类加载器
    设置库
    设置类路径
    设置访问权限
    开启一个新线程用来进行自动重载


    创建类加载器
    WebappLoader使用一个内部类加载器来加载类。可以回头看Loader接口,该接口提供了getClassLoader方法但是并没有setClassLoader方法。
    因此,不能通过传递一个WebappLoader来初始化它

     

    private WebappClassLoader createClassLoader() throws Exception {
    	Class clazz = Class.forName(loaderClass);
    	WebappClassLoader classLoader = null;
    	if (parentClassLoader == null) {
    		classLoader = (WebappClassLoader) clazz.newInstance();
    	}else {
    		Class[] argTypes = { ClassLoader.class };
    		Object[] args = { parentClassLoader };
    		Constructor constr = clazz.getConstructor(argTypes);
    		classLoader = (WebappClassLoader) constr.newInstance(args);
    	}
    	return classLoader;
    }

     

     

    设置库
    WebappLoader的start方法会调用setRepositories方法来给类加载器添加一个库。
    WEB-INF/classes目录传递给加载器addRepository方法,而WEB-INF/lib传递给加载器的setJarPath方法。
    这样,类加载器能能从WEB-INF/classes 目录下面和WEB-INF/lib目录下面部署的类库里加载类。
    设置类路径
    该任务由start方法调用setClassPath方法完成,setClassPath方法会给servlet上下文分配一个String类型属性保存Jasper JSP编译的类路径,
    设置访问权限
    如果Tomcat使用了安全管理器,setPermissions给类加载器给必要的目录添加访问权限,例如WEB-INF/classes和WEB-INF/lib。如果不使用管理器,该方法马上返回
    开启自动重载线程
    WebappLoader支持自动重载,如果WEB-INF/classes或者WEB-INF/lib目录被重新编译过,在不重启Tomcat的情况下必须自动重新载入这些类。
    为了实现这个目的,WebappLoader有一个单独的线程每个x秒会检查源的时间戳。
    x的值由checkInterval变量定义,它的默认值是15,也就是每隔15秒会进行一次检查是否需要自动重载。
    该类还提供了两个方法getCheckInterval和setCheckInterval方法来访问或者设置checkInterval的值。

    WebappClassLoader类
    WebappClassLoader表示在一个web应用程序中使用的加载器。WebappClassLoader类继承了java.net.URLClassLoader类
    缓存
    缓存由WebappClassLoader类实例自己管理。另外,java.lang.ClassLoader维护了一个Vector,可以避免前面加载过的类被当做垃圾回收掉。
    每一个可以被加载的类(放在 WEB-INF/classes目录下的类文件或者 JAR 文件)都被当做一个源。
    一个源被org.apache.catalina.loader.ResourceEntry类表示。一个ResourceEntry实例保存一个byte类型的数组表示该类、最后修改的数据或者副本等等。

     

    public class ResourceEntry {
    	public long lastModifled = -1;
    	public byte[] binaryContent = null;
    	public Class loadedClass = null;
    	public URL source = null;
    	public URL CodeBase = null;
    	public Manifest manifest = null; 
    	public Certificate[] certificates = null;
    }

     

     

    所有缓存的源被存放在一个叫做resourceEntries的HashMap中,键值为源名,所有找不到的源都被放在一个名为notFoundResources的HashMap中。

    加载类
    当加载一个类的时候,WebappClassLoader类遵循以下规则:
    · 所有加载过的类都要进行缓存,所以首先需要检查本地缓存。
    · 如果无法再本地缓存找到类,使用java.langClassLoader类的findLoaderClass方法在缓存查找类、
    · 如果在两个缓存中都无法找到该类,使用系统的类加载器避免从J2EE类中覆盖来的web应用程序。
    · 如果使用了安全管理器,检查该类是否允许加载,如果该类不允许加载,则抛出ClassNotFoundException异常。
    · 如果要加载的类使用了委派标志或者该类属于trigger包中,使用父加载器来加载类,如果父加载器为null,使用系统加载器加载。
    · 从当前的源中加载类
    · 如果在当前的源中找不到该类并且没有使用委派标志,使用父类加载器。如果父类加载器为null,使用系统加载器
    · 如果该类仍然找不到,抛出ClassNotFoundException异常

     

    例子

     

    public static void main(String[] args) {
    		
    		
    		System.setProperty("catalina.base", System.getProperty("user.dir"));
    		Connector connector = new HttpConnector();
    		Wrapper wrapper1 = new SimpleWrapper();
    		wrapper1.setName("test1");
    		wrapper1.setServletClass("test1Servlet");
    		Wrapper wrapper2 = new SimpleWrapper();
    		wrapper2.setName("test2");
    		wrapper2.setServletClass("test2Servlet");
    		
    		Context context = new StandardContext();
    		context.setPath("/myapp");
    		context.setDocBase("myapp");
    		
    		context.addChild(wrapper1);
    		context.addChild(wrapper2);
    		
    		context.addServletMapping("/test1", "test1");
    		context.addServletMapping("/test2", "test2");
    		
    		LifecycleListener listener = new SimpleContextConfig();
    		((Lifecycle)context).addLifecycleListener(listener);
    		
    		Loader loader = new WebappLoader();
    		context.setLoader(loader);
    		connector.setContainer(context);
    		
    		try{
    			connector.initialize();
    			((Lifecycle)connector).start();
    			((Lifecycle)context).start();
    			
    			WebappClassLoader classLoader= (WebappClassLoader)loader.getClassLoader();
    			String[] repositories = classLoader.findRepositories();
    			for(int i=0;i<repositories.length;i++){
    				System.out.println("--------------------");
    			}
    			
    			System.in.read();
    			((Lifecycle)context).stop();
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    	}
    

     

     

    public class SimpleContextConfig implements LifecycleListener {
    
    	public void lifecycleEvent(LifecycleEvent event) {
    
    		if(Lifecycle.START_EVENT.equals(event.getType())){
    			System.out.println("---------------------");
    		}
    	}
    
    }
    

     

    public class SimplePipeline implements Pipeline, Lifecycle {
    
    	protected Valve basic = null;
    	protected Container container = null;
    	protected Valve[] valves = new Valve[0];
    
    	public void addValve(Valve valve) {
    		Valve results[] = new Valve[valves.length + 1];
    		System.arraycopy(valves, 0, results, 0, valves.length);
    		results[valves.length] = valve;
    		valves = results;
    	}
    	
    	protected class StandardPipelineValveContext implements ValveContext {
    	    protected int stage = 0;
    	    public String getInfo() {
    	      return null;
    	    }
    
    	    public void invokeNext(Request request, Response response)
    	      throws IOException, ServletException {
    	      int subscript = stage;
    	      stage = stage + 1;
    	      // Invoke the requested Valve for the current request thread
    	      if (subscript < valves.length) {
    	        valves[subscript].invoke(request, response, this);
    	      }
    	      else if ((subscript == valves.length) && (basic != null)) {
    	        basic.invoke(request, response, this);
    	      }
    	      else {
    	        throw new ServletException("No valve");
    	      }
    	    }
    	  } // end of inner class
    
    
    	public Valve getBasic() {
    		return null;
    	}
    
    	public Valve getFirst() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	public Valve[] getValves() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	public void removeValve(Valve arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	public void setBasic(Valve arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	public void addLifecycleListener(LifecycleListener arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	public LifecycleListener[] findLifecycleListeners() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	public void removeLifecycleListener(LifecycleListener arg0) {
    		// TODO Auto-generated method stub
    
    	}
    
    	public void start() throws LifecycleException {
    		// TODO Auto-generated method stub
    
    	}
    
    	public void stop() throws LifecycleException {
    		// TODO Auto-generated method stub
    
    	}
    
    }

     

     

    public class SimpleWrapper implements Wrapper, Pipeline, Lifecycle {
    	
    	private Servlet instance = null;
    	private String servletClass;
    	
    	private Loader loader;
    	private String name;
    	protected LifecycleSupport lifecycle = new LifecycleSupport(this);
    	private SimplePipeline pipeline = new SimplePipeline();
    	protected Container parent = null;
    	protected boolean started = false;
    	
    	public SimpleWrapper(){}
    
    
    
    	
    
    }
    

     

    展开全文
  • 接下来就以tomcat6为例看看tomat是如何使用自定制类加载器的。(本介绍是基于tomcat6.0.41,不同版本可能存在差异!) 网上所描述的tomcat加载器  在网上搜一下“tomcat加载器”会发现有大量的文章,在此我偷...

      在上文中,已经介绍了系统类加载器以及类加载器的相关机制,还自定制类加载器的方式。接下来就以tomcat6为例看看tomat是如何使用自定制类加载器的。(本介绍是基于tomcat6.0.41,不同版本可能存在差异!)

    网上所描述的tomcat类加载器

      在网上搜一下“tomcat类加载器”会发现有大量的文章,在此我偷个懒,^_^把网上对tomcat类加载器的描述重说一下吧。

    • CommonClassLoader:加载的类目录通过{tomcat}/conf/catalina.properties中的common.loader指定,以SystemClassLoader为parent(目前默认定义是common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar)
    • CatalinaClassLoader   :加载的类目录通过{tomcat}/conf/catalina.properties中server.loader指定,以CommonClassLoader为parent,如果server.loader配置为空,则ServerClassLoader 与CommonClassLoader是同一个(默认server.loader配置为空)
    • SharedClassLoader:加载的类目录通过{tomcat}/conf/catalina.properties中share.loader指定,以CommonClassLoader为parent,如果server.loader配置为空,则CatalinaClassLoader 与CommonClassLoader是同一个(默认share.loader配置为空)
    • WebappClassLoader:每个Context一个WebappClassLoader实例,负责加载context的/WEB-INF/lib和/WEB-INF/classes目录,context间的隔离就是通过不同的WebappClassLoader来做到的。由于类定义一旦加载就不可改变,因此要实现tomcat的context的reload功能,实际上是通过新建一个新的WebappClassLoader来做的,因此reload的做法实际上代价是很高昂的,需要注意的是,JVM内存的Perm区是只吃不拉的,因此抛弃掉的WebappClassLoader加载的类并不会被JVM释放,因此tomcat的reload功能如果应用定义的类比较多的话,reload几次就OutOfPermSpace异常了。
    • JasperLoader:每个JSP一个JasperLoader实例,与WebappClassLoader做法类似,JSP支持修改生效是通过丢弃旧的JasperLoader,建一个新的JasperLoader来做到的,同样的,存在轻微的PermSpace的内存泄露的情况

    以上对个个classloader的作用做了介绍,但请读者不要搞混淆了,上边说的个个类加载器只是类加载器的名字,不是类加载类的名字。上边的图是看到网上资料的说明绘制的,但是与实际源码中的结构还是差异挺大的。(没有研究是不是因为tomcat的版本所致)。下面就详细介绍下tomcat源码中类加载器的组织结构。

    tomcat源码中类加载器的结构分析

      首先要说明是tomcat默认配置下的情况。那接下来看看tomcat启动时的类初始化情况,这是BootStrap类的类初始化方法:

      

      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);
            }
        }
    

     

     可以看到,在创建commonLoader时传的父类加载器是null。跟踪下去会发现commonLoader的父类加载器确实是null。有朋友可能想,tomcat在启动时肯定也要依赖jdk核心库,parent是null那怎么委托给parent去加载核心库的类了啊。这里大家不要忘了ClassLoader类的loadClass方法:

    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);//没有父类加载器时使用bootstrap类加载器
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    

      通过以上initClassLoaders方法我们也能看到catalinaLoader和sharedLoader的父类加载器都是commonLoader,跟上边图的类加载器结构符合。但是commonLoader、catalinaLoader和sharedLoader的创建都是依赖tomcat安装目录下conf/catalina.properties的配置。默认情况配置是:

    • common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
    • server.loader=
    • shared.loader=

    由于server.loader和shared.loader的配置为空,所以其实commonLoader、catalinaLoader和sharedLoader都是指向同一个类加载器实例,看代码如下:(限于篇幅只贴部分代码)

     private ClassLoader createClassLoader(String name, ClassLoader parent)
            throws Exception {
    
            String value = CatalinaProperties.getProperty(name + ".loader");
            if ((value == null) || (value.equals("")))
                return parent;
    

    而他们指向那个类加载器类的实例呢?跟踪到最后我们发现如下代码:

     StandardClassLoader classLoader = null;
            if (parent == null)
                classLoader = new StandardClassLoader(array);
            else
                classLoader = new StandardClassLoader(array, parent);
            return (classLoader);
    

    就是StandardClassLoader的实例,下文再对StandardClassLoader进行源码讲解。

      接下来再看看webappclassloader的创建过程,webappclassLoader是在WebappLoader类中的createClassLoader方法中通过反射实例化的。下边是源代码:

        private WebappClassLoader createClassLoader()
            throws Exception {
    
            Class clazz = Class.forName(loaderClass);//loaderClass="org.apache.catalina.loader.WebappClassLoader"
            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;
    
        }
    

    可以看到他的parent是通过调用container.getParentlassLoader()获得的(如果对tomcat的结构不熟悉,请看这篇文章)跟踪到最后我们发现它调用了ContainerBase的这个方法:

        public ClassLoader getParentClassLoader() {
            if (parentClassLoader != null)
                return (parentClassLoader);
            if (parent != null) {
                return (parent.getParentClassLoader());
            }
            return (ClassLoader.getSystemClassLoader());
    
        }
    

    通过默认配置下debug可以知道最后是返回的systemclassloader,也就是说WebappClassLoader的父类加载器是systemclassloader也就是上篇文章说的App ClassLoader。

    (由于JasperLoader本人还没有做分析,先不进行讲解了)

    tomcat类加载器的实现方式分析

      上文说到了commonLoader、catalinaLoader和sharedLoader都是指向StandardClassLoader的实例,来先看一看StandardClassLoader的源码实现:

    public class StandardClassLoader
        extends URLClassLoader
        implements StandardClassLoaderMBean {
    
    	public StandardClassLoader(URL repositories[]) {
            super(repositories);
        }
    
        public StandardClassLoader(URL repositories[], ClassLoader parent) {
            super(repositories, parent);
        }
    
    }
    

    有没有感到你的意外啊,对的就是这么简单,这跟我上篇文章说的最简单的实现方式一样。(上篇文章做了解读,这里不再做说明了)

      我们再来看看webappclassLoader,他的实现类就是org.apache.catalina.loader.WebappClassLoader,此类加载器也是继承自URLClassLoader,但是它覆盖了loadClass方法和findClass方法。这个类有三千多行这里就不将代码全部贴出来了。

    public Class loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
    
            if (log.isDebugEnabled())
                log.debug("loadClass(" + name + ", " + resolve + ")");
            Class clazz = null;
    
            // Log access to stopped classloader
            if (!started) {
                try {
                    throw new IllegalStateException();
                } catch (IllegalStateException e) {
                    log.info(sm.getString("webappClassLoader.stopped", name), e);
                }
            }
    
            // (0) 检查WebappClassLoader之前是否已经load过这个资源
    clazz = findLoadedClass0(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
    
            // (0.1) 检查ClassLoader之前是否已经load过
            clazz = findLoadedClass(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
    
            // (0.2) 先检查系统ClassLoader,因此WEB-INF/lib和WEB-INF/classes或{tomcat}/libs下的类定义不能覆盖JVM 底层能够查找到的定义(譬如不能通过定义java.lang.Integer替代底层的实现
            try {
                clazz = system.loadClass(name);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
    
            // (0.5) Permission to access this class when using a SecurityManager
            if (securityManager != null) {
                int i = name.lastIndexOf('.');
                if (i >= 0) {
                    try {
                        securityManager.checkPackageAccess(name.substring(0,i));
                    } catch (SecurityException se) {
                        String error = "Security Violation, attempt to use " +
                            "Restricted Class: " + name;
                        log.info(error, se);
                        throw new ClassNotFoundException(error, se);
                    }
                }
            }
    
            //这是一个很奇怪的定义,JVM的类加载机制建议先由parent去load,load不到自己再去load(见上篇文章),而Servelet规范的建议则恰好相反,Tomcat的实现则做个折中,由用户去决定(context的 delegate定义),默认使用Servlet规范的建议,即delegate=false
            boolean delegateLoad = delegate || filter(name);
    
            // (1) 先由parent去尝试加载,如上说明,除非设置了delegate,否则这里不执行
            if (delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader1 " + parent);
                ClassLoader loader = parent;
                
                if (loader == null)
                    loader = system;
                try {
                    clazz = loader.loadClass(name);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return (clazz);
                    }
                } catch (ClassNotFoundException e) {
                    ;
                }
            }
    
            // (2) 到WEB-INF/lib和WEB-INF/classes目录去搜索,细节部分可以再看一下findClass,会发现默认是先搜索WEB-INF/classes后搜索WEB-INF/lib
            if (log.isDebugEnabled())
                log.debug("  Searching local repositories");
            try {
                clazz = findClass(name);
                if (clazz != null) {
                    if (log.isDebugEnabled())
                        log.debug("  Loading class from local repository");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
    
            // (3) 由parent再去尝试加载一下
            if (!delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader at end: " + parent);
                ClassLoader loader = parent;
                if (loader == null)
                    loader = system;
                try {
                    clazz = loader.loadClass(name);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return (clazz);
                    }
                } catch (ClassNotFoundException e) {
                    ;
                }
            }
    
            throw new ClassNotFoundException(name);
        }
    

      

     

    转载于:https://www.cnblogs.com/metoy/p/3917535.html

    展开全文
  • 加载器Tomcat加载器加载器 1.启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath 参数指定路径中的,且被虚拟机认可(按文件名识别,如 rt.jar)的类。 2.扩展...
  • tomcat加载器

    2012-12-11 11:09:48
    DevLoader.zip tomcat加载器
  • 文章目录《Tomcat源码解析》Tomcat加载器前言一、J2SE类加载器1.1 启动类加载器1.2 扩展类加载器1.3 系统类加载器1.4 代码获取各种类加载器1.5 双亲委派机制1.6 ClassLoader二、Tomcat加载器三、Web应用类加载器...
  • Tomcat加载器

    2018-11-20 10:26:26
     这是一篇尝试讲解清楚Tomcat的类加载器的文章,估摸着能讲清楚六成左右,待后续再理理思路。  文末有彩蛋,可以直接翻到文章末尾。 Tomcat加载器概览 说明: BootstrapClassLoader : 系统类加载器,加载%...
  • Tomcat加载器

    2020-04-21 18:27:40
    Tomcat加载器 Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托...
  • Tomcat加载器机制

    2020-07-07 21:52:15
      上一篇文章介绍了Tomcat的组件及架构设计理念,本章将来了解Tomcat的类加载器机制。   类加载器是一切Java应用运行的基础,了解类加载器机制便于我们掌握它的运行边界,有助于运行异常时的快速定位。 1、J2SE...
  • 加载器如何发生内存泄露,以及Tomcat与类加载器有关的源代码,分析了Tomcat的启动流程
  • 1类与类加载器比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两...
  • Tomcat加载器(附JVM类加载器简介)  学习tomcat加载器,首先得先看下JVM提供了几种类加载器,毕竟tomcat加载器是依赖于JVM类加载器的。  JVM类加载器:引导类加载器(bootstrapclassloader)、扩展类加载...
  • 什么是类加载器加载器就是用来加载类(*.class)的东西!类加载器也是一个类:ClassLoader(继承该类) Java提供了三种类加载器,分别是: bootstrap classloader:引导类加载器,加载rt.jar中的类; sun.misc....
  • Tomcat加载器的实现

    2020-05-05 09:36:59
    Tomcat加载器的实现 Tomcat 内部定义了多个 ClassLoader,以便应用和容器访问不同存储库中的类和资源,同时达到应用间类隔离的目的。 1. Java 类加载机制 类加载就是把编译生成的 class 文件,加载到 JVM ...
  • 加载器基本概念 顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,041
精华内容 816
关键字:

tomcat加载器