tomcat系列之类加载器
2015-01-02 18:42
393 查看
jvm中有三种类加载器,好吧,为了介绍tomcat中的类加载器,先来普及下类加载器的概念,参见这篇文章——JVM类加载器 。
就像JVM的类加载器一样,为了安全tomcat中自己扩展了几种类加载器,分别来加载不同路径下的lib库,因为一个tomcat可以部署不同的应用,如果各个应用的lib库由相同的类加载器加载那么带来的安全隐患不言而喻 。来看一下标准的tomcat目录:
lib目录下的类文件是整个tomcat需要的,webapp下所有应用是不应该有权限访问到lib下的类的。我们通过源码来一步步看tomcat是如何处理类加载问题的,先来看下Bootstrap的主函数:
首先要的逻辑是init初始化,init的代码就不贴了,主要的逻辑讲一下,初始化catalinahome和catalineabase,可以将catalinahome简单理解为tomcat的安装目录,catalinabase目录为tomcat的工作目录,默认这两个目录是一致的。紧接着是初始化类加载器的动作,重点看下这里的代码:
三种类加载器,其中commonLoader是另外两个的父加载器,这三种类加载器都是继承自URLClassLoader。实际上还有一种类加载器,WebAppClassLoader专门负责加载webapp中的类库。初始化方法中有这么一句代码:Thread.currentThread().setContextClassLoader(catalinaLoader);这就是JVM类加载中讲到过的线程上下文加载器,目前还不知道这里设置的目的是什么。来看看WebappClassLoader的源码,其主要是覆写了loadClass方法:
首先去本地的缓存查找,如果没有找到则去父类的缓存查找,如果还没找到则根据用户的配置决定是由本加载器加载或者由父类加载器加载:
比如WEB-INF\classes或WEB-INF\lib下面的类库通常需要从findClass中获取。
就像JVM的类加载器一样,为了安全tomcat中自己扩展了几种类加载器,分别来加载不同路径下的lib库,因为一个tomcat可以部署不同的应用,如果各个应用的lib库由相同的类加载器加载那么带来的安全隐患不言而喻 。来看一下标准的tomcat目录:
lib目录下的类文件是整个tomcat需要的,webapp下所有应用是不应该有权限访问到lib下的类的。我们通过源码来一步步看tomcat是如何处理类加载问题的,先来看下Bootstrap的主函数:
public static void main(String args[]) { if (daemon == null) { daemon = new Bootstrap(); try { daemon.init(); } catch (Throwable t) { t.printStackTrace(); return; } } try { String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[args.length - 1] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[args.length - 1] = "stop"; daemon.stop(); } else if (command.equals("start")) { daemon.setAwait(true); daemon.load(args); daemon.start(); } else if (command.equals("stop")) { daemon.stopServer(args); } else { log.warn("Bootstrap: command \"" + command + "\" does not exist."); } } catch (Throwable t) { t.printStackTrace(); } }
首先要的逻辑是init初始化,init的代码就不贴了,主要的逻辑讲一下,初始化catalinahome和catalineabase,可以将catalinahome简单理解为tomcat的安装目录,catalinabase目录为tomcat的工作目录,默认这两个目录是一致的。紧接着是初始化类加载器的动作,重点看下这里的代码:
private void initClassLoaders() { try { //commonLoader,专门负责加载catalina.properties配置文件里的common.loader=后面的类库 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专门负责加载server.loader=后面的类库 catalinaLoader = createClassLoader("server", commonLoader); //专门加载shared.loader=后面的类库 sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { log.error("Class loader creation threw exception", t); System.exit(1); } }
三种类加载器,其中commonLoader是另外两个的父加载器,这三种类加载器都是继承自URLClassLoader。实际上还有一种类加载器,WebAppClassLoader专门负责加载webapp中的类库。初始化方法中有这么一句代码:Thread.currentThread().setContextClassLoader(catalinaLoader);这就是JVM类加载中讲到过的线程上下文加载器,目前还不知道这里设置的目的是什么。来看看WebappClassLoader的源码,其主要是覆写了loadClass方法:
// (0) Check our previously loaded local class cache clazz = findLoadedClass0(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return (clazz); } // (0.1) Check our previously loaded class cache clazz = findLoadedClass(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return (clazz); } // (0.2) Try loading the class with the system class loader, to prevent // the webapp from overriding J2SE classes try { clazz = system.loadClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) { // Ignore }
首先去本地的缓存查找,如果没有找到则去父类的缓存查找,如果还没找到则根据用户的配置决定是由本加载器加载或者由父类加载器加载:
boolean delegateLoad = delegate || filter(name); // (1) Delegate to our parent if requested if (delegateLoad) { if (log.isDebugEnabled()) log.debug(" Delegating to parent classloader1 " + parent); //由父加载器去加载,也就是sharedClassLoader 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) Search local repositories 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) Delegate to parent unconditionally 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);
比如WEB-INF\classes或WEB-INF\lib下面的类库通常需要从findClass中获取。
相关文章推荐
- tomcat解析之类加载器
- Tomcat内核之类加载器工厂
- J2SE基础夯实系列之类加载器
- Tomcat内核之类加载器工厂
- 读书笔记——《深入理解Java虚拟机》系列之类加载器与双亲委派模型
- 黑马程序员--【强哥笔记】系列之Java高新技术笔记之类加载器(第7天)
- Tomcat7.0.42源码研读之类加载器(五)
- 细说tomcat之类加载器
- J2SE基础夯实系列之类加载器
- Android插件化框架系列之类加载器
- Redhat Enterprise Linux 5 实战系列(三)为RHEL5安装JDK和配置tomcat
- JAVA/JSP学习系列之二(Tomcat安装)
- Windows Mobile 进阶系列.第二回.初窥.NET CF类型加载器
- JBoss JBPM 实践系列(一)--- 安装配置(Tomcat 6.0 + MySQL 5.1)
- Java虚拟机深入详解JVM之类加载器深度剖析、类的主动使用、被动使用
- 基于tomcat5.5的数据库连接池环境设置(省的以后找系列)
- JBoss JBPM 实践系列(一)--- 安装配置(Tomcat 6.0 + MySQL 5.1)
- IBM资料UML系列之类图
- Tomcat系列一:tomcat安装配置及启动(整理)
- JBoss JBPM 实践系列(一)--- 安装配置(Tomcat 6.0 + MySQL 5.1)