`
韩悠悠
  • 浏览: 842504 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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



	

}

 

  • 大小: 112.6 KB
分享到:
评论

相关推荐

    免费】eclipse内的tomcat插件(com.sysdeo.eclipse.tomcat_3.1.0)

    4. **日志查看**:插件提供了一个内置的日志查看器,使得开发者能够在Eclipse中直接查看Tomcat服务器的日志,帮助诊断问题。 5. **调试支持**:提供强大的调试工具,允许开发者设置断点,步进执行代码,检查变量值...

    tomcat类加载器

    这个"DevLoader.zip"文件可能包含与Tomcat自定义类加载器相关的资料,特别是名为"DevLoader"的类加载器,这可能是Tomcat为开发者提供的一种特殊加载器。 首先,我们来理解一下类加载器的基本概念。在Java中,类加载...

    tomcat-7.0.26.tomcat-juli.jar

    3. **性能优化**:与Apache Commons Logging相比,Juli避免了类加载器问题,减少了类查找的时间,从而提高了整体性能。 三、配置Juli 在Tomcat中,Juli的配置主要通过两个文件来完成: - **`conf/logging....

    java类加载器-tomcat中的类加载器

    下面我们将深入探讨Java类加载器以及Tomcat中的类加载器。 在Java中,类加载器主要分为三个层次:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader。Bootstrap ClassLoader负责加载JDK的核心库,如rt...

    JVM、Tomcat、OSGI等类加载器整理文档

    JVM、OSGI(Open Service Gateway Initiative)和Tomcat等容器都涉及到了类加载器的概念,理解它们的工作原理对优化应用性能和解决依赖冲突至关重要。 1. JVM类加载器: - **父类加载器**:在Java中,类加载器之间...

    tomcat-7.0.108.zip

    另一种是在`conf/server.xml`中配置Context元素,指定应用的文档基础和类加载器设置。 5. **访问web应用**:一旦应用被部署,用户可以通过浏览器访问`http://localhost:8080/应用名`来访问应用,假设默认端口8080未...

    Java 类在 Tomcat 中是如何加载的(过程分析)

    当Tomcat加载类时,它遵循以下顺序: - 使用Bootstrap类加载器尝试加载。 - 使用System类加载器尝试加载。 - 使用Webapp类加载器加载`WEB-INF/classes`下的类。 - 使用Webapp类加载器加载`WEB-INF/lib`中的jar...

    最新版linux apache-tomcat-8.5.59.tar.gz

    这是一个常见的Linux软件分发格式,用于在不依赖任何其他软件包管理器的情况下进行手动安装。 **Apache Tomcat 8.5.59详解** Apache Tomcat 8.5.59是Tomcat服务器的稳定版本,它提供了许多改进和修复,以增强性能...

    3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4

    3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4

    tomcat6异常问题

    解决方法通常是使用Maven或Gradle等构建工具进行依赖管理,确保每个应用都有其独立的类加载器或者使用统一的jar版本。 2. **缺失的jar**:如果在运行Tomcat时出现“找不到类”的错误,可能是因为缺少了必要的jar。...

    类加载器与Tomcat

    首先,Tomcat的类加载器层次结构由Bootstrap类加载器、Common类加载器、Shared类加载器和Web应用程序类加载器组成。Bootstrap类加载器是JVM启动时的第一个加载器,负责加载JDK的核心类库。Tomcat使用Bootstrap类加载...

    apache-tomcat-9.0.86-windows-x64.zip

    Tomcat作为Servlet和JSP的容器,负责加载、实例化和管理Servlet,以及将JSP转换成Servlet并执行。 2. **版本号**:“9.0.86”代表了Tomcat的主要版本和次要版本号,其中“9.0”表示主版本,而“86”是次版本。每个...

    apache+tomcat分布式部署

    - 在此文件中,我们定义了一个负载均衡控制器`controller`,以及两个Tomcat实例`tomcat1`和`tomcat2`。每个Tomcat实例都有自己的AJP端口(`worker.tomcatX.port`),主机名(`worker.tomcatX.host`),类型(`worker....

    Apache HttpServer 2.4.X 和tomcat连接器

    这个连接器,通常称为 mod_jk 或 ajp (Apache Jserv Protocol),允许两个服务协同工作,使得 Apache 能够处理静态内容,而 Tomcat 处理动态 Java 应用程序,实现高效、灵活的Web服务器架构。 Apache HttpServer 是...

    apache-tomcat-7.0.100

    Tomcat作为Servlet容器,负责加载、执行并管理Servlet实例。 2. **JSP**:JSP是一种基于Java的动态网页技术,允许开发者将静态HTML与动态Java代码混合编写。在Tomcat中,JSP文件会被编译成Servlet类,然后由Tomcat...

    tomcat 类加载机制 —— ClassLoader

    在Tomcat中,我们可以通过配置`catalina.properties`文件和`server.xml`文件来调整类加载策略,例如设置自定义的类加载顺序或启用共享类加载器。 此外,Tomcat还支持热部署,即在不重启服务器的情况下更新Web应用的...

    Apache.Tomcat负载平衡软件、配置

    4. **Tomcat配置**: 需要在每个Tomcat实例的server.xml文件中开启AJP监听器,例如: ```xml ``` **负载均衡策略** mod_jk支持多种负载均衡策略,如轮询(round-robin)、优先级(priority)、最少连接(least ...

    apache-tomcat-7.0.105.zip免费下载

    在Tomcat中,Servlets被加载到内存中并由容器管理,以处理客户端请求。 **JavaServer Pages (JSP)** 是一种用于创建动态Web内容的技术,它将HTML代码与Java代码分离,使得开发者可以专注于页面逻辑而无需关注底层...

Global site tag (gtag.js) - Google Analytics