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"));
	}
}