前言
由java类加载机制和类加载器(classLoader)文章我们知道,java默认的类加载机制是通过双亲委派模式实现的。如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类的加载器还存在父类加载器,则进一步向上委托,依次递归,最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成加载任务,子加载器才会尝试自己加载。
对于运行在java EE 容器中的web应用来说,类加载器的实现方式与一般的java应用有所不同。不同的web容器的实现方式也会有所不同。今天我们来讲讲主流web 容器tomcat和weblogic类加载机制的实现方式。
tomcat类加载器
tomcat 作为一个java web容器,也有自己的类加载机制,通过自定义的类加载机制以实现共享类库的抽取,不同web应用之间的资源隔离还有热加载等功能。除了java自身的类加载之外,它主要实现的类加载器主要有:Common ClassLoader,Catalina ClassLoader,Shared ClassLoader,WebApp ClassLoader以及JasperLoader。
从图中的委派关系可以看出:Common类加载器能加载的类都可以被Catalina类加载器和Shared类加载器使用,从而实现了公有类库的共用,而Catalina类加载器和Shared类加载器自己能加载的类则与对方相互隔离,属于兄弟关系。WebApp类加载器可以使用Shared类加载器加载到的类,但各个WebApp类加载器实例之间相互隔离。而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。
为啥这样设计,我们先了解下每个类加载器的用途:
- Common类加载器:负责加载tomcat和web应用都复用的类。
- Catalina类加载器:负责加载tomcat专用类,而这些被加载的类在web应用中不可见。
- Shared类加载器:负责加载tomcat下所有的web应用程序都复用的类,而这些被加载的类在tomcat中不可见。
- WebApp类加载器:负责加载具体的某个Web应用程序所使用到的类,而这些被加载的类在Tomcat和其他的Web应用程序都将不可见。
- Jsp类加载器:每个jsp页面一个类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔。
双亲委派机制存在一种缺陷,基础类加载器只能加载自己指定范围内的类,对于用户提供的类是没办法加载的,以至于如果基础类需要用户类,那就没办法加载。比如JDBC,我们都知道这是java提供的一种规范,由不同的厂商去实现。规范是通过接口体现,作为基础类。而厂商的实现是驱动包,放在项目的依赖包中。我们可以使用DriverManager.getConnection()去获取连接,使用Class.forName()去加载驱动。如果严格按照双亲委派机制,DriverManager是java.sql包中的,属于BootStrapClassLoader的范围,它只能看到这个文件夹中的类,而我们的驱动包是不在这里面的,就无法找到类,也就没法去加载了。
tomcat为了实现隔离性,破坏了双亲委派机制,每个web应用都有一个对应的类加载器实例。该类加载器使用代理模式,它首先尝试去加载class,如果找不到再代理给父类加载器。这样就使得web应用自己的类优先级高于web容器提供的类,保证每个应用程序的类库是相互独立的,并且相互隔离。
weblogic 类加载器
weblogic中classloader是分层次的,它只能加载比它层次高的类及它自身的类,同层次的类及比它层次低的类都不能加载。
- JDK Classloader
- JDK ext Class Loader
- Weblogic System Class Loader
- Domain Class Loader(Child of System Class Loader)
- App Class Loader (负责装载应用中的所有的EJB JAR文件)
- Web Class Loader (负责装载所有的Web application 中的WAR文件(所有得jsp文件除外)
- JSP Class Loader (负责装载Web application 中的所有的jsp文件)
注意:第6条,先加载classes中的类,再加载lib中的类,当然也可以通过修改Weblogic.xml修改加载顺序。
由上面可知:App Class Loader不能实例化Web application下的类,否则会报类找不到的错误。
当部署一个应用的时候,weblogic server 会自动创建一个具有层次结构的类装载器:
- Application Classloader负责装载应用中的所有的EJB JAR文件;
- Web Application Classloader负责装载所有的Web application 中的WAR 文件(所有得jsp文件除外);
- Jsp Classloader 负责装载Web application 中的所有的jsp 文件。
在Weblogic服务启动的过程中,自动形成一个具有层次结构的类装载器,首先装载jdk及java扩展jar包或类;然后再加载Weblogic本身使用的各个jar包或类;然后再加载Web应用文件夹里面的classes下的类,然后再加载Web应用文件夹里面lib下的jar包或类。也就是说,每个层次的类装载器均对应不同的类路径,它们是一一对应的。 比如System装载器对应着jdk及扩展路径;Application装载器对应着Weblogic的相关类;而web 应用装载器对应着webapp应用下的classes和lib下的路径;而jsp装载器则对应着jsp文件。
当然,在加载过程中,若在高层次的加载器中已经加载了某类,那么再以后的加载中,再次遇到该类也不会加载,只是会忽略。加载完成之后,将类放入Cache中供系统应用调用。
在系统的运行过程中,若遇到使用该类的情况,则会遵循双亲委派机制,先通过其父类加载器进行加载的原则,比方说,我要加载一个WSWordManager类,则系统会首先在Cache中寻找,若找不到,则调用父装载器到与之对应的路径里面去寻找,一直向上,找着了则进行加载,若找不着则报出ClassNotFound的异常。