13.tomcat加载器

库(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(){}



	

}

猜你喜欢

转载自501565246-qq-com.iteye.com/blog/1724545