package com.easyway.commons.ispace.dev.oop.classloaders;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
/**
* 类加载机制:
* 创建自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)
* 方法即可。该方法根据参数指定的类的名称,返回它对应的Class对应的引用。
* 本类的:用户自定义的泪加载程序,它从path属性指定的文件目录中加载.class 文件,
* 他的私有方法loadClassData(String name )能根据参数指定的类的名字,把相应的。
* class文件中二进制数据读入到内存中,并且以字节数组形式返回。
*
* 由java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。
* java 虚拟机自带的类加载器包括根类加载器,扩展类加载器,系统类加载器。java
* 虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象。
* 由用户自定义的类加载器所加载的类是可以被卸载的。在类的加载器的内部实现中,用一个java集合来存放所加载
* 类的引用。另一方面,一个Class对象总是会引用它的类加载器。调用Class对象的getClassLoader()方法。
* 就能获取它的类加载器。一个类的实例总是引用代表这个类的Class对象。在Object类中定义了getClass()方法,
* 这个方法返回代表对象所属类的Class对象的引用。此外所有的java类都有一个静态属性class,它引用代表这个类的class对象。
* @author longgangbai
* @date 2010-5-8
* @version 1.0
* @since JDK6.0
*/
public class CustomClassLoader extends ClassLoader{
private String name;
private String path="";
private final String fileType=".class";
public CustomClassLoader(String name){
super();
this.name=name;
}
public CustomClassLoader(ClassLoader parent,String name){
super(parent);
this.name=name;
}
@Override
public String toString(){
return name;
}
/**
* Loads the class with the specified <a href="#name">binary name</a>.
* This method searches for classes in the same manner as the {@link
* #loadClass(String, boolean)} method. It is invoked by the Java virtual
* machine to resolve class references. Invoking this method is equivalent
* to invoking {@link #loadClass(String, boolean) <tt>loadClass(name,
* false)</tt>}. </p>
* @param name
* The <a href="#name">binary name</a> of the class
* @return The resulting <tt>Class</tt> object
* @throws ClassNotFoundException
* If the class was not found
*/
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
byte[] data=loadClassData(name);
return defineClass(name,data, 0, data.length);
}
/**
* 将类的二进制数据读入到内存中
* @param name
* @return
* @throws ClassNotFoundException
*/
@SuppressWarnings("unused")
private byte[] loadClassData(String name )throws ClassNotFoundException
{
FileInputStream fin=null;
byte[] data=null;
ByteArrayOutputStream baos=null;
try {
name=name.replaceAll("\\.", "\\\\");
fin=new FileInputStream(new File(path+name+fileType));
baos=new ByteArrayOutputStream();
int ch=0;
while((ch=fin.read())!=-1){
baos.write(ch);
}
data=baos.toByteArray();
} catch (Exception e) {
throw new ClassNotFoundException(" class is not found : "+name,e);
}finally{
try {
fin.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/**
* 在父委托机制中,各个加载器按照父子关系形成了树形结构。除了根类加载器以外其余的类加载器都有且只有一个父加载器。
* 需要指出的是:
* 加载器之间的父子关系实质上指的是加载对象之间的包装关系。而不是类之间的继承关系。一对父子关系加载器可能是同
* 一个加载类的两个实例,也可能不是。在子加载器对象中包装了一个父加载器对象。
* 如load2的父加载load1
* Class sampleClass=load2.loadClass(ClassLifeLine.class.getName());
* 如下:load2首先从自己的命名空间中查找ClassLifeLine类是否被加载,如果已经加载,就直接返回代表ClassLifeLine
* 类的class对象应用。如果没有加载,load1首先请求load2代为加载,load1再请求系统类加载器代为加载,系统类加载
* 器再请求扩展类加载器代为加载,扩展类加载器再请求根类加载器代为加载。若根类加载器和扩展类加载器都不能加载,
* 则系统类加载器尝试加载若能加载成功,则ClassLifeLine类所对应的Class对象的引用返回给load1,load1将引用返回
* load2,从而加载ClassLifeLine到虚拟机。如果系统类加载器不能加载成功,则load1尝试加载ClassLifeLine类,
* 如load1也加载不成功,则load2加载。若所有的附加载器以及load2本省不能加载,则抛出ClassNotFoundException异常。
*
* 父委托机制的优点:提供了软件系统的安全性。因为在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类。
* 从而防止不可靠的甚至恶意的代码代替父加载器的可靠代码。
*
* 如java.lang.Object总是由有根类加载器加载,其任何用户自定义的泪加载器都不可能加载含有恶意代码的java.lang.Object类。
* 1.命名空间:每一个类加载器都有自己的命名空间,命名空间有该类加载器及所有父加载器所加载的类组成。在同一命名空间中,
* 不会出现类的完整名字(包括类的包名)相同的两个类;在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的类。
*
* 2.运行时包:
* 由同一类加载的属于相同包的类成了运行时包,决定两个类是不是属于同一个运行时包。不仅要看他们的包名是否相同。还要看
* 定义的类加载器是否相同。只有属于同一运行是宝的类次啊能互相访问包可见(默认访问的级别)的类和类成员,这样的限制能
* 避免用户自定义的类冒充核心库的类,去访问核心类库的包可见成员。 例如:用户自己定义的java.lang.Spy ,并有用户
* 自定义的类加载器加载。由于java.lang.* 和java.lang.Spy 由不同的加载器,他们属于不同的运行时包。所以java.lang.Spy
* 不能访问核心类苦苦java.lang包中的包可见成员。
* @param args
*/
public static void main(String[] args) {
CustomClassLoader load1=new CustomClassLoader(ClassLifeLine.class.getName());
load1.setPath(System.getProperty("user.dir"));
CustomClassLoader load2=new CustomClassLoader(load1,ClassCircleLine.class.getName());
load2.setPath(System.getProperty("user.dir"));
CustomClassLoader load3=new CustomClassLoader(null,SystemClassLoad.class.getName());
load3.setPath(System.getProperty("user.dir"));
}
}