java程序用到一个类,来看怎么用
在java程序里,用到一个System的类,Java虚拟机首先把这个类的字节码加载到内存里面来,字节码的原始信息放在硬盘里的pathclass指定的目录下,我们把.class里面的内容加载进硬盘里来,再对他进行一些处理,处理完的结果就是字节码,就把.class文件从硬盘上加载进来,然后在对它进行一些处理,这些工作都是类加载器做,这就是类加载器的作用。
只要遇到某个类,就把它加载进来/把它的二进制加进来,就是类加载器去加他
一、概念
1、Java
虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:
BootStrap,ExtClassLoader,AppClassLoader
2、类加载器也是
Java
类,他是把其他java类加载进来,那么它是由谁加载进来的。
因为其他是
java
类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是
java
类,这正是
BootStrap
。(java虚拟机第一个类加载器是BootStrap)
BootStrap不是java类,所以不需要被别人加载,他是嵌套在java虚拟机内核里面的,java虚拟机内核一启动的时候,它已经在里面了,是用c++语言写的一段代码,它 可以去加载别的类,其中别的类就包括了别的类加载器,
public static void main(String[] args) {
// TODO Auto-generated method stub
/*类加载器如何加载*/
//ClassLoaderTest.class:ClassLoaderTest的字节码是由一个classLoader对象加载进来的
//getClassLoader是一个对象,对象也属于某个class,类加载器是一个具体的对象
//对象也有自己的类型,getClass()就得到这个类加载器的类,这个类加载器的名字是getName
System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
.getName());//结果:sun.misc.Launcher$AppClassLoader这个类加载器的名字叫AppClassLoader
//对象为null,是由BootStrap这个特殊的类加载器加载的
System.out.println(System.class.getClassLoader());
System.out.println("***************");
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
//让loader等于上边loader的爸爸,这样就可以推出来了
loader=loader.getParent();
}
System.out.println(loader);//爷爷,根节点
}
结果:
sun.misc.Launcher$AppClassLoader
null
***************
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
null
***************
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
BootStrap 根 专门加载rt.jar里面的类 -->JRE/lib/rt.jar
^
ExtClassLoader 专门加载ext文件夹下jar包里面那些类的-->JRE/lib/ext/*.jar
^
System classLoader--> AppClassLoader <-- (System classLoader) -->CLASSPATH指定的所有jar或目录
^ ^
MyClassLoader ItcastClassLoader (也可以自己写类加载器,挂在AppClassLoader上) -->传智播客指定的特殊目录
每个类加载器都有自己的加载范围
3、Java
虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
项目右键->属性->Run/Debug Settings->点击 ClassLoaderTest (运行环境)->点击Edit->JRE 看看JDK是哪个版本的
右键ClassLoaderTest->Export->Java->JAR File->next按钮->勾选 Export generated class files and resources(输出产生的class文件)
输出到JAR file路径里
只要是jar包,放在ext目录下就可以被java虚拟机加载 (D:\Program Files\Java\jdk1.6.0_10\jre\lib\ext\itcast.jar)
在export destination JAR(在jdk下面)jdk\jre\lib\ext\ib ->finish
ext(扩展的包 放在这个文件夹下就会被java虚拟机加载)
rt.jar系统默认的包
结果:
***************
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$ExtClassLoader
null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$ExtClassLoader
null
爸爸找不到时(ExtClassLoader),就用儿子(AppClassLoader)来管理,找到时就用爸爸来管理
这就是类加载器的委托机制
二、类加载器的委托机制
当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢
1,首先当前线程的类加载器去加载线程中的第一个类。
2,如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B
第一个类又用到了第二个类,第一个类调用了第二个类,或者说继承了第二个类
3,如果不想用线程类加载器,想强制性指定自己的类加载器,可以直接用自己的类加载器对象直接loadClass也可以,调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
当我们派第一个类加载器去加载类的时候,有可能会出现:
派AppClassLoader出去找,找System,没找到,不抛异常说找不到这个类,它会交给上级ExtClassLoader,但是这个过程是反的,当我们派AppClassLoader去找System的时候,它先不找,交给爸爸ExtClassLoader,爸爸也找不到就交给爷爷BootStrap,爷爷没办法,找JRE/lib/rt.jar,找到就ok了;如果爷爷没有找到,那就推回到儿子,儿子在JRE/lib/ext/*.jar里找,如果没有找到,推给孙子AppClassLoader,如果还没找到,报异常ClassNotFoundException。
不会说交给儿子MyClassLoader,因为还有其他儿子;
所以BootStrap会很忙,是总管,这样的好处是集中管,
假设有两个类加载器MyClassLoader、ItcastClassLoader,去加载System,让MyClassLoader加载时加载到了;让ItcastClassLoader去加载也加载到了;这时内存里就出现两份字节码;
如果先找爷爷,爷爷曾经加载过System,又让爷爷加载System,爷爷就会知道曾经加载过,直接拿出来用,就不会出现多份字节码重复的现象;如果不委托给上级,那么每个兄弟各干各的,相互不来往,内存要出现多份字节码;如果首先交给爷爷,爷爷如果能做的话,就让爷爷来做,
有一道面试题:能不能自己写个类叫java.lang.System
写了也是白写。写完后肯定放在CLASSPATH下面,但是每次要加载System的时候,通常情况下委托机制要直接交给到爷爷,在rt.jar包里找到了,直接加载成功了,儿子根本轮不到,根本不轮不到自己写的那个;
但可以自己写类加载器,要写自己的委托加载机制
这个ext\itcast.jar就属于委托加载机制
解释前面讲ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后运行结果为ExtClassLoader的原因
刚才把类输入到ext的jar包下面,它找了Ext类加载器,Ext是App的爸爸,Ext优先找,结果找到了
ClassLoader(类加载器)
加载【查找并加载类的二进制数据】,连接【验证:确保被加载的类的正确性;准备:为类的静态变量分配内存,并将其初始化为默认值;解析:把类中的符号引用转换为直接引用;】,初始化【为类的静态变量赋予正确的初始值】
三、编写自己的类加载器
例:
写类加载器MyClassLoader ,要发到AppClassLoader下面,要用MyClassLoader加载一个特定目录下的类,把写的类放到特定的目录下,其他的类加载器都加载不了,只能被类加载器MyClassLoader加载,并且MyClassLoader类还要加密,别人拷回去用不了,必须用我的类加载器才能跑起来,因为我的类加载器在加载过程中会解密,这个是个目标
(一)要完成这个目标,必须具备的预备知识:
1、 自定义的类加载器必须继承ClassLoader
loadClass是ClassLoader的方法
2、 loadClass方法与findClass方法
loadClass里面放一个指定的字符串、一个类名,返回一个class;我们要自己写一个类加载器,不用从写一个loadClass,因为这个loadClass的内部它会去找他的爸 爸,当爸爸返回来后再接着调用findClass;也就是说loadClass先找爸爸,找完爸爸接着找findClass,只要写完findClass就可以了。这样如果覆盖findClass,那你 的类加载器的loadClass有委托机制,还会找爸爸,找完爸爸不行在找自己的findClass。如果直接覆盖loadClass,不去找爸爸的话,就是直接自己干了,不找爸爸 了,或者你去找爸爸还要写上找爸爸的代码,比较难;如果继承loadClass就由人家来干这事,人家干这事就是来找爸爸,爸爸找不到时再接着调用findClass,在自己 干,也就是说findClass专门自己干,你所要干的事就是覆盖findClass,这是模板、方法、设计模式。
模板方法设计模式
- 父类里面有个loadClass方法
- 不管是哪个子类,他们的loadClass的过程是一样的
- 子类1,子类2我们希望它都按照统一的流程去搞,即找爸爸,爸爸做不了在自己搞。但是每个子类自己干的代码是不一样的,但是他们的流程,就是找爸爸的 流程是一样的,那么就把这个流程的代码放在父类里面编写,所有的子类都继承父类的方法,但是父类的方法在干的过程中,碰到了一个局部的细节,不会干,那么 就把它定义为抽象方法,等待子类去填写那个抽象方法。总体的流程在父类里面已经规定好了,细节父类无法确定,就把它空出来,留给子类去完成。
- 我们要覆盖的不是loadClass而是findClass,是为了保留loadClass里面的流程,只是局部细节findClass要自己干。当我们得到了class文件里面的二进制文件数 据,怎样把class文件的内容转换成字节码,由defindClass()来完成(父类里面提供的)。
这是写类加载器的原理
3、defineClass方法
把一个字节的数组变成Class,defineClass(byte[] b,int off,int len)
例子:写了一个字节的类加载器,覆盖findClass方法,覆盖完后,内部有段段代码,方法loadClassDate(name),能根据name能搞到一堆二进制数据byte[] b,接 着就把这堆数据交给defineClass方法,defineClass方法就会把它(name)变成Class对象,也就是字节码
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
(二)编写步骤:
public static void cypher(InputStream istream,OutputStream ostream) throws Exception{
while(b=(byte)istream.read())!=-1)
{
byte b=0;
ostream.write(b^0xff);
}
}
A、简单的加密
1、写个静态方法cypher
给我一个输入流,输入流里面有源源不断的二进制数据,给他加完密语以后写到一个输出流里面去
以父类的形式定义其变量类型InputStream istream,是为了通用,并不是FileInputStream和FileOutputStream ,
读取每一个字节(byte)istream.read(),读取的每一个字节跟0xff异或一下,结果就是原来是1改成0,全部反着,加密和解密是同一个,这个既可以加密又可以解密,
public class MyClassLoader{
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//文件的路径,保存文件的路径
//先传进源和目标
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);//相对路径
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);//交给cypher进行加密
fis.close();
fos.close();
}
/*加密*/
private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
} }
2、再写一个类,让能对这个类加密,
ClassLoaderAttachment
新建类时Supperclass选择 java.util.Date
覆盖toString方法
import java.util.Date;
public class ClassLoaderAttachment extends Date {
public String toString(){
return "hello,itcast";
}
}
3、现在对这个ClassLoaderAttachment类进行加密,为了方便,可以把它放到缺醒包下。
要对这个class文件进行加密处理,加密完的结果要放到工程javaehance下面的叫itcastlib的文件夹下,
运行MyClassLoader.java的时候,要把ClassLoaderAttachment给加密,找到它的class文件给加密。
点击工程,进入源文件,javaehance\bin,找到ClassLoaderAttachment.class文件,找到这个文件的完整路径,拷贝下来
MyClassLoader.java文件上点击右键,Run As 选择Open Run Dialog
a、选择Arguments选项框(第一个参数是源,第二个参数是目标)
在Program arguments:
C:\Workspace1\javaenhance\bin\ClassLoaderAttachment.class
(源)
itcastlib(目标)
b、 Main选项框 Main class 中主要类要选择cn.itcast.day2.MyClassLoader
点击 run
在 itcastlib文件夹下出现\ClassLoaderAttachment.class
MyClassLoader.java
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//文件的路径,保存文件的路径
//先传进源和目标
//destFileName得到目标的filename
// '\\'是windows路径 没考虑通用性,通用性要用一个cyprate常量
String srcPath = args[0];//源路径
String destDir = args[1];//目标的目录
FileInputStream fis = new FileInputStream(srcPath);//相对路径
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);//srcPath.lastIndexOf把最后一个斜杠找出来
String destPath = destDir + "\\" + destFileName;//目标的路径
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);//交给cypher进行加密
fis.close();
fos.close();
}
/*加密*/
private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
}
}
运行完后,class文件存入itcastlib文件夹中,是一个加过密的类
有包名的类不能调用无包名的类
public class ClassLoaderTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
.getName());
System.out.println(System.class.getClassLoader());
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader != null){
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
System.out.println(new ClassLoaderAttachment().toString());
}
tsum.misc.Launcher$ExtClassLoader
null
sum.misc.Launcher$ExtClassLoader
null
ClassLoaderAttachment.java右键Export...->Java 选择JAR file,把itcast.jar删除掉
结果(注意eclipse版本)改之后:
sum.misc.Launcher$AppClassLoader
sum.misc.Launcher$ExtClassLoader
null
hello,itcast
给字节码加密,拷贝路径 C:\Workspace1\javaenhance\bin\ClassLoaderAttachment.class,
在MyClassLoader.java右键Run As 选Open Run Dialog
选择Arguments选项框(第一个参数是源,第二个参数是目标)
在Program arguments:
C:\Workspace1\javaenhance\bin\cn\itcast\day2\ClassLoaderAttachment.class
(源)
itcastlib(目标)
Main选项框 Main class 中主要类要选择cn.itcast.day2.MyClassLoader
点击 run(
加密了)
在 itcastlib文件夹下\ClassLoaderAttachment.class文件更新了,拷贝下来
把\javaenhance\bin\cn\itcast\day2\ClassLoaderAttachment.class文件替换掉
此时为加过密的,再运行ClassLoaderTest.java文件就有错误出现
主动使用
创建类的实例
访问某个类或接口的静态变量,或者对该静态变量赋值
调用类的静态方法
反射
初始化一个类的子类
Java虚拟机启动时被表明为启动类的类
除了以上六种情况,其他都属于
被动使用,都不会导致类的初始化
加载.class文件的方式
从本地系统中直接加载
通过网络下载.class文件
从zip,jar等归档文件中加载.class文件
从专有数据中提取.class文件
将Java源文件动态编译为.class文件
有两种类型的类加载器
1、
Java虚拟机自带加载器:
根类加载器(Bootstrap):
使用c++编写,程序员无法在Java代码中获得该类
扩展类加载器(Extension):使用Java代码实现
系统类加载器(System):使用Java代码实现
2、用户自定义的类加载器:java.lang.ClassLoader的子类,用户可以定制类的加载方式
加密:
1、 新建ClassLoaderAttachment.java ,browse->选java.util.Date
2、 将新建class文件放到工程目录下ClassLoaderAttachment.java
3、 拷贝路径: C:\Workspace1\javaenhance\bin\ClassLoaderAttachment.class
4、再工程下新建文件夹itcastlib
在MyClassLoader.java右键Run As 选Open Run Dialog
选择Arguments选项框(第一个参数是源,第二个参数是目标)
在Program arguments:
C:\Workspace1\javaenhance\bin\ClassLoaderAttachment.class
(源)
itcastlib(目标)
点击 run
在 itcastlib文件夹下出现\ClassLoaderAttachment.class
5、把ClassLoaderAttachment.java从
检查哪里出现问题可以:window->show view->problems
B、解密
public class MyClassLoader{}是普通程序
public class MyClassLoader extends ClassLoader{}变成类加载器
之后覆盖find方法(保存方法)
@Override :覆盖
public class MyClassLoader extends ClassLoader{
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//文件的路径,保存文件的路径
//先传进源和目标
//destFileName得到目标的filename
// '\\'是windows路径 没考虑通用性,通用性要用一个cyprate常量
String srcPath = args[0];//源路径
String destDir = args[1];//目标的目录
FileInputStream fis = new FileInputStream(srcPath);//相对路径
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);//srcPath.lastIndexOf把最后一个斜杠找出来
String destPath = destDir + "\\" + destFileName;//目标的路径
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);//交给cypher进行加密
fis.close();
fos.close();
}
/*加密*/
private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {//给子类用的应该是保护类型的protected
// TODO Auto-generated method stub
String classFileName=classDir+"\\"+name.substring(name.lastIndexOf('.')+1) + ".class";//name是类的名字,ClassLoaderTest类中的loadClass("cn .itcast.day2.ClassLoaderAttachment");带上包名时要截取
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义字节数组流
cypher(fis,bos);
fis.close();
byte[] bytes=bos.toByteArray();//得到一个字节数组
return defineClass(bytes,0,bytes.length);//defineClass是把一个字节数组生成出来
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.findClass(name);//调用父类的findClass(name)方法
}
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir=classDir;
}
}
类加载器写好了
在ClassLoaderTest.java里用这个类加载器
public class ClassLoaderTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
.getName());
System.out.println(System.class.getClassLoader());
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader != null){
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
//System.out.println(new ClassLoaderAttachment().toString());
/*用写好的MyClassLoader类加载器
* 把itcastlib目录传进去
* loadClass("cn.itcast.day2.ClassLoaderAttachment"),把ClassLoaderAttachment这个classload进去,得到一个字节码,返回的是Class字节码Class c lazz
* newInstance对象的类型是ClassLoaderAttachment类型
* */
Class clazz=new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
/*ClassLoaderAttachment d1=(ClassLoaderAttachment)clazz.newInstance();
* 这么写不可以,在源程序里出现ClassLoaderAttachment类的名字,不能很好的加载
* 编译器过不了说类不兼容
* 要用ClassLoaderAttachment的父类Date
*/
Date d1=(Date)clazz.newInstance();
System.out.println(d1);
}
}
让爸爸类加载器招不着,把爸爸目录下的ClassLoaderAttachment删除,javaenhance\bin\cn\itcast\day2下的ClassLoaderAttachment.class文件删掉,爸爸类加载器在找
看问题处在哪里时,在window->Show View->Problems 窗口里找
写完后,把javaenhance\bin\cn\itcast\day2下的ClassLoaderAttachment.class文件删掉,这样父类加载器找不到,只能被我的类加载器MyClassLoader.java加载
想要的结果:hello,itcast
四、类加载器的高级问题
web项目,web项目要放在tomcatweb服务器上,tomcat服务器也是一个java程序
tomcat启动起来后,java虚拟机里面就有很多类加载器,其中很多都是tomcat这个项目里面自己提供的,我们在tomcat下开发web应用程序,开发的主要是servlet,servlet也是java类,这个java类就是对tomcat自己提供的类加载器,在应用中,可能会出现一些问题,
(一)WebappClassLoader加载
新建一个web工程Web Project
工程名是itcastweb,把透视图切换成Myeclipse
web项目是java意义上的项目了,这个功能不是eclipse自带的,而是Myeclipse插件带的
新建一个class,我们建的class是一种特殊的class,他是可以放在tomcat里面运行的class,这个class的父类是servlet
Myeclipse就直接提供了Sevlet菜单项,选择Servlet菜单,这是一个java类,他的父类是HttpServlet
大的项目要分层,有表现层,业务层,数据访问层
package cn.ticast.itcastweb.web.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();//out(往浏览器上打)类似于System.out(网控制台上打),
/*
* 自定义一个类加载器ClassLoader loader
* this servlet是一个对象,getClass()对象的字节码
* 这个字节码一定是由getClassLoader加载的
* 打印ClassLoader的名字,以及打印出他的祖辈们
* */
ClassLoader loader=this.getClass().getClassLoader();
while (loader!=null) {//loader,最顶层的类加载器
out.println(loader.getClass().getName()+"</br>");//loader的爸爸
loader=loader.getParent();//loader的上一级load,爸爸的爸爸
}
out.close();
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out
.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the POST method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}
点击itcast项目,选择部署快捷键
把web项目放到tomcat服务器里面去,点击Add按钮,选择配置好的tomcat服务器
之后itcast项目就放到tomcat的webapps文件夹下
启动tomcat:找到路径D:\apache-tomcat-6.0.33\bin路径下startup.bat文件,tomcat启动后,就可以访问项目了。
tomcat下面有好些独立的项目,也就是好些虚拟目录,其中itcast是其中一个,这个路径下写的servlet,这个servlet可以用一个地址来访问他,这个地址在WebRoot/web-inf/web.xml中
<servlet-name>MyServlet</servlet-name> 名字
<url-pattern>/servlet/MyServlet</url-pattern>映射的路径,用这个映射的路径就可以访问到serlet
浏览器输入:http://localhost:8080/itcast/servlet/MyServlet
结果:
org.apache.catalina.loader.WebappClassLoader
org.apache.catalina.loader.StandardClassLoader ----WebappClassLoader的父亲是StandardClassLoader
sun.misc.Launcher$AppClassLoader----------------------父亲的上一级是AppClassLoader
sun.misc.Launcher$ExtClassLoader-----------------------AppClassLoader上一级是ExtClassLoader
org.apache.catalina.loader.StandardClassLoader ----WebappClassLoader的父亲是StandardClassLoader
sun.misc.Launcher$AppClassLoader----------------------父亲的上一级是AppClassLoader
sun.misc.Launcher$ExtClassLoader-----------------------AppClassLoader上一级是ExtClassLoader
这个Servlet是由tomcat自己提供的一个类加载器加载的:WebappClassLoader。这里不是sun公司,而是apache
(二)ExtClassLoader加载
把MyServlet.java打成jar包输出到jdk里面去,右键->Export...选择Java file 里的JAR file
选择jar file 路径:
如果输出的jdk和tomcat的jdk不是同一个的话,输进去也没用
tomcat是有一个java虚拟机启动的,那个jdk运行就输出到哪个jdk,也就是tomcat用的那个jdk
D:\Program Files\Java\jdk1.6.0_10\jre\lib\ext\itcast.jar 选next->finish
tomcat从启一下,这时java虚拟机就从新启动了
例1:
a、刚开始MyServlet类加载器是由WebappClassLoader类加载器加载的
MyServlet又用到了HttpServlet,这样HttpServlet将由WebappClassLoader类加载器加载,加载成功
b、ExtClassLoader类加载器加载MyServlet,MyServlet用到了HttpServlet,这中情况下HttpServlet将由谁加载,正常情况下是由ExtClassLoader加载
但ExtClassLoader加载的时候,先委托给自己的爸爸,在爸爸中没有找到HttpServlet,回到自己找,自己也没找到就报错,也不会交给儿子,因为它是发起者。
要解决这个问题,就把HttpServlet的jar包放在ext管理的下面,
HttpServlet的jar包在:D:\apache-tomcat-6.0.33\lib路径下的servlet-api.jar,把它复制到D:\Program Files\Java\jdk1.6.0_10\jre\lib\ext路径下
此时打印的是:sun.misc.Launcher$ExtClassLoader