黑马程序员_高新技术类加载器,黑马高新技术
分享于 点击 47206 次 点评:89
黑马程序员_高新技术类加载器,黑马高新技术
一、类加载器概念(jdk1.0) 顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码(.class文件),并转换成java.lang.Class类的一个实例(字节码)。每个这样的实例用来表示一个 Java 类。 简单地说就是:将.class文件从硬盘上加载进来,对它进行处理,处理的结果就是字节码. 默认的三个主要类加载器:BootStrap,ExtClassLoader,AppClassLoader 二、类加载器的结构 类加载器本身也是Java类,其他类加载器也是被类加载器加载,那么必须有一个类加载器不是java类,这就是BootStrap(引导类加载器).它是有C++写的一段二进制代码,它并不继承java.lang.ClassLoader,它是嵌套在java虚拟机内核中,java虚拟机内核一启动,BootStrap就已经存在了.System是由BootStrap加载.
Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供
的类加载器主要有下面三个:
(1)引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader
。
(2)扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
(3)系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()
来获取它。
三、类加载器的委托机制 每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类加载器去加载类.这就是类加载器的委托模式。 类加载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后一级级回退到子孙类加载器去加载,当退到最初的类加载器,还无法完成时,会报ClassNotFoundException异常。 在这里我们思考一下能不能自己写个类java.lang.System?? 通常是不可以的,因为类加载采用委托机制,而System是由最顶层父类加载,所以这样总是使用java系统提供的System,但是可以自己定义一个类加载器去加载自己写的这个类.
public class ClassLoadTest_委托机制 {
public static void main(String[] args) {
System.out.println(ClassLoadTest_委托机制.class.getClassLoader().getClass().getName());//AppClassLoader加载
System.out.println(System.class.getClassLoader());//BootStrap加载
ClassLoader loader = ClassLoadTest_委托机制.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent(); //类加载器会通过委托机制一级一级向上找自己的父类
}
System.out.println(loader);//打印的就是最后的父类
/*
* AppClassLoader ClassPath指定的所有jar或目录
* ExtClassLoader jre/lib/ext/*.jar
* BootStrap Jre/lib/rt.jar
*/
}
}
四、自定义类加载器 虽然在绝大多数情况下,系统默认提供的类加载器实现已经可以满足需求。但是在某些情况下,您还是需要为应用开发出自己的类加载器。比如您的应用通过网络来传输 Java 类的字节代码,为了保证安全性,这些字节代码经过了加密处理。这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在 Java 虚拟机中运行的类来。下面将通过两个具体的实例来说明类加载器的开发。 模板方法设计模式 总体流程在父类已经规定好了,而流程的一些细节父类无法确定,就将它定义成一个抽象方法,就交给子类完成抽象方法。 父类-->loadClass/findClass(覆盖)/得到class文件的转换成字节码--->definClass() 子类1(自己干的代码) 子类2(自己干的代码) 第一个类加载器用来加载存储在文件系统上的 Java 字节代码。完整的实现如下所示。 实现步骤:1.自定义的类加载器必须继承ClassLoader 2.loadClass()方法,直接加载自定义的findClass()方法,那么这个流程就不会走,也就不是模板设计模式(一般不覆盖) findClass()方法,先通过委托机制寻找,找不到就用自己覆盖的方法加载.(一般只覆盖这个方法) 因此,为了保证类加载器都正确实现委托机制模式,在开发自己的类加载器时,最好不要覆写 loadClass()方法 ,而是覆写
findClass()
方法。
3.defineClass()方法:将一个字节数组转换为Class文件
在硬盘上查找类的字节代码文件(.class
文件),然后读取该文件内容,最后通过 defineClass()
方法来把这些字节代码转换
成 java.lang.Class
类的实例。
public class MyClassLoader extends ClassLoader
{
private String path = null;
public MyClassLoader(String path) throws Exception//检查文件是否存在
{
File f = new File(path);
if(!f.isDirectory())
{
throw new RuntimeException(path + " is not a directory");
}
this.path = path;
}
public Class findClass(String name) //throws Exception //为什么不能抛出
{
try
{
File f = new File(path,name.substring(name.lastIndexOf('.')+1) + ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte [] buf = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name,buf,0,buf.length);
}catch(Exception e)
{
throw new ClassNotFoundException(name + " is not found!");
}
return null;
}
public static void cypher(InputStream istream,OutputStream ostream) throws Exception
{
//下面这段代码可能遇到255的字节,当成byte就成了-1
/*byte b = 0;
while((b = (byte)istream.read()) != -1)
{
ostream.write(b ^ 0xff);
}*/
int b = 0;
while((b = istream.read()) != -1)
{
ostream.write(((byte)b) ^ 0xff);
}
}
public static void main(String [] args) throws Exception
{
//下面省略了错误检查
if(!args[0].endsWith("class"))
{
ClassLoader loader = new MyClassLoader(args[1]);
Class cls = loader.loadClass(args[0]);
/*
让自定义类继承Date类
System.out.println(cls.getClassLoader().getClass().getName());
java.util.Date d = (java.util.Date)cls.newInstance();
System.out.println(d.toString());
*/
//Method m = cls.getMethod("test",null);//在jdk1.5中报警告,为什么?
Method m = cls.getMethod("test");
//m.invoke(cls.newInstance(),null);
m.invoke(cls.newInstance());
//((Test)cls.newInstance()).test();
return;
}
else
{
FileInputStream fis = new FileInputStream(args[0]);
File f = new File(args[1], new File(args[0]).getName());//不用检查目录最后是否有目录分割符
FileOutputStream fos = new FileOutputStream(f);
cypher(fis,fos);
fis.close();
fos.close();
}
}
}
//类加载器不能加载这种非public的类
/*
Exception in thread "main" java.lang.IllegalAccessException: Class MyClassLoader
can not access a member of class MyTest with modifiers ""
*/
/*
class MyTest
{
public void test()
{
System.out.println("hello,www.it315.org");
}
}
*/
五、类加载器的一个高级问题 tomcath是被自己的类加载器加载的 编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果 为WebAppClassloader。
上面表示一个问题,父级类加载器加载的类无法引用只能被子级类加载器加载的类.
把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。 把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader
相关文章
- 暂无相关文章
用户点评