How Tomcat works 8: Loader类

本文详细介绍了Tomcat的类加载机制,包括为何需要定制加载类、Java类加载器的工作原理、Tomcat如何实现类的热加载以及WebappClassLoader的具体实现。探讨了Loader和Reloader接口的功能,以及类加载过程中的缓存和权限设置。

1. 概述

  (1)需要定制加载类的原因:

  • 安全性:每个context需要有自己的class loader来加载所有内部的servlet类。而不能用系统自带的system loader来加载,否则违反安全性约束
  • 重新加载:当WEB-INF/classes中的类改变时,需要重新加载

2. Java类加载器

(1)JVM每次加载java类,先创建class对象,后载入内存

(2)JVM加载类时,会先搜索core java lib,和CLASSPATH环境变量下所有文件夹

(3)JVM三个类加载器:Bootstrap class loader, extension class loader, system class loader

  • Bootstrap class loader使用native code,加载core java类,如java.lang,java.io包,搜索core lib如rt.jar, i18n.jar
  • Extension class loader: 加载标准拓展目录, 如 /jdk/jre/lib/ext
  • System class loader: 加载CLASSPATH环境变量下的jar和目录

(4)JVM使用双亲委派模型: system->extension->bootstrap。底层先被调用,然后会把加载要求先层级委派给parent。当parent无法加载,再自己加载,都找不到则throw ClassNotFoundException

(5)Tomcat定制化的类加载器的作用:

    a. 制定特殊的加载类

    b. 缓存已经加载的类

    c. 预加载已经准备好使用的类

3. Loader接口

(1)servlet只能由访问WEB-INF/lib目录的权限

(2)Tomcat loader是一个web application loader而不是类加载

(3)定义了仓库集合:一个web应用的WEB-INF/classes和WEB-INF/lib

(4)loader通常绑定给Context,因为需要reload,所以接口有modify方法(完成return true,通过context.reload方法进行调用), 允许重新加载在context配置文件中配置<Context … reloadable="true"/>

(5)loader的实现类也可以设置是否委派给parent class loader

代码:

		package com.cisco.tomcat.loader;
		
		import java.beans.PropertyChangeListener;
		
		import com.cisco.tomcat.lifecycle.Container;
		
		public interface Loader {
			public ClassLoader getClassLoader();
			public Container getContainer();
			public void setContainer(Container container);
			public DefaultContext getDefaultContext();
			public void setDefaultContext(DefaultContext defaultContext);
			public String getInfo();
			
			/**
			 * Reload Servlet class
			 * @return
			 */
			public boolean modified();
			public boolean getDelegate();
			public void setDelegate(boolean delegate);
			public boolean getReloadable();
			public void setReloadable();
			public void addRepository(String repository);
			public String[] findRepositories();
			public void removeRepository(String repository);
			public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener);
			public PropertyChangeListener getPropertyChangeListener();
			
		}

(6)类加载UML结构图:

4. Reloader接口:

		package com.cisco.tomcat.loader;
		
		public interface Reloader {
			public void addRepository(String repository);
			public String[] findRepositories();
			public boolean modified();
		}

(1)start方法会做五个动作:

创建一个class loader->设置repository->设置class path->设置permission->启动auto-load线程

(2)创建一个类加载器

			public class WebappLoader implements Loader{
			
				private String loaderClass;
				private ClassLoader parentClassLoader = null;
				
				private WebappClassLoader createClassLoader() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
					Class<?> clazz = Class.forName(loaderClass);
					WebappClassLoader classLoader = null;
					if(parentClassLoader == null) {
						classLoader = (WebappClassLoader)clazz.newInstance();
					}else {
						Class<?>[] argType = {ClassLoader.class};
						Object[] args = {parentClassLoader};
						Constructor<?> constructor = clazz.getConstructor(argType);
						classLoader = (WebappClassLoader)constructor.newInstance(args);
					}
					return classLoader;
				}
			}

(3)设置repository:

/WEB-INF/classes传给loader.addRepository(), /WEB-INF/lib传给classloader.setJarPath()

(4)设置class path:

setClassPath,Jasper JSP compiler会使用这部分数据

(5)设置permission会把如/WEB-INF/classes和/WEB-INF/lib的权限给classloader,若未设置,立即返回

(6)为Auto-reload启动新线程

			@Override
			public void run() {
				while(!threadDone) {
					// 等待检查间隔时间
					threadSleep();
					if(!started) {
						break;
					}
					// 未结束继续
					if(!classLoader.modified()) {
						continue;
					}
					// reload结束则通知context
					notifyContext();
					break;
				}
			}
			
			
			private void threadSleep() {}
			private void notifyContext() {
				WebappContextNotifier notifier = new WebappContextNotifier();
				(new Thread(notifier)).start();
			}
			
			protected class WebappContextNotifier implements Runnable{
		
				@Override
				public void run() {
					// TODO Auto-generated method stub
					((Context) container).reload();
				}
				
			}

6. WebappClassLoader:

(1)缓存:使用ResourceEntry来代表先前加载过的class,使用HashMap<ResourceEntry>来缓存加载后的class资源

			package com.cisco.tomcat.loader;
			
			import java.net.URL;
			import java.security.cert.Certificate;
			import java.util.jar.Manifest;
			
			public class ResourceEntry {
				public long lastModified = -1;
				// 资源的二进制内容 
				public byte[] binaryContent = null;
				public Class<?> loadedClass = null;
				// 资源的加载url地址
				public URL source = null;
				public URL CodeBase = null;
				public Manifest manifest = null;
				public Certificate[] certificates = null;
			}

(2)加载类的过程

WebappClassLoader使用以下规则:

    a. 所有先前使用的类都缓存在本地缓存,所以先查询本地缓存

    b. 如果本地缓存没查询到,检查内存(ClassLoader.findLoadedClass)

    c. 如果两种内存都未查到,使用system's class loader来阻止覆盖J2EE类

    d. 如果使用了SecurityManager,如果这个类不允许使用,抛ClassNotFoundException

    e. 如果delegate = on或者这个类属于package trigger,使用parent classloader加载,如果parentclassloader=null使用system class loader

    f. 从当前repository加载类

    g. 若不再当前repository, 则委派给parent class loader若无则委派给systemclasssloader

    h. 若仍找不到,抛ClassNotFoundException

7. Bootstrap启动:

Connector.start()->context.start(),在context.start()中会调用loader.start()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值