java类加载器

详细内容请参考原链接:http://blog.csdn.net/jiangwei0910410003/article/details/17733153,此文只是转载
1、新建一个java对象,JVM需要把这个对象对应的字节码加载到内存中,字节码就是对应的Class文件,自己java工程,其在bin目录下,也就是把.class文件的内容读到内存中;
2、Java虚拟机中类加载器
java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:BootStarp,ExtClassLoader,AppClassLoader,其中BootStrap是用C++写的,内置在JVM内核中,也就是所有类的鼻祖;
之所以有不同的类加载器,是因为不同的类加载器加载不同目录下的.class文件;采用委托的方式,向父类请求加载,每个父类去指定的目录加载,成功结束;不成功再顺序往下请求,直到加载成功,所有加载类都没有成功,则抛出ClassNotFoundException异常

类加载器的委托机制:
当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?
(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)
(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B
(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类
每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->Ext::ClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束;
因为BootStrap不是java类,所以打印出来的class是null
3、System类、list、map这样的类都在rt.jar中,需要由BootStrap来加载,所以其加载类打印出来为null;也就是说,谁加载的你当前这个jar,那么它的classloader就是谁,比方说把MyClassLoader打包成jar,然后放到jre1.x/lib/ext目录下,那么它的第一个加载类就会变成sun.misc.Launcher$ExtClassLoader
4、可以定义自己的类加载器,需要将自己的类加载器挂载到系统类加载器树上,在构造函数时可以指定类加载器
5、定义了自己的类加载器,每个加载器都有自己的搜索目录,自己类加载器有自己的目录,而它的父类也有自己的目录,所以类名字,如果父类找到了,那么子类不会继续查找了;所以即使使用了自己的类加载器,如果是父类找到了对应名称的类class,其加载器就是父类
编写自己的加载类:
先写一个测试类:

public class ClassLoaderAttachment extends Date{
    private static final long serialVersionUID = 98398492342L;
    @Override
    public String toString() {
        // TODO Auto-generated method stub

        return "Hell ClassLoaderAttachment";
    }
}

编译后,在bin目录会生成它的.class文件,然后在项目目录(bin的上一层把)创建一个”temp_folder”目录,用于生成加密后的.class文件
写一个main函数,把上面的ClassLoaderAttachment.class文件加密

        //测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下  
        public static void main(String[] args) throws Exception{  
            //配置运行参数  
            String srcPath = args[0];//ClassLoaderAttachment.class原路径  
            String desPath = args[1];//ClassLoaderAttachment.class输出的路径  
            String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);  
            String desPathFile = desPath + "/" + desFileName;  
            FileInputStream fis = new FileInputStream(srcPath);  
            FileOutputStream fos = new FileOutputStream(desPathFile);  
            //将class进行加密  
            encodeAndDecode(fis,fos);  
            fis.close();  
            fos.close();  
        }  

        /** 
         * 加密和解密算法 
         * @param is 
         * @param os 
         * @throws Exception 
         */  
        private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{  
            int bytes = -1;  
            while((bytes = is.read())!= -1){  
                bytes = bytes ^ 0xff;//和0xff进行异或处理  
                os.write(bytes);  
            }  
        }

会在temp_folder目录下生成一个新的ClassLoaderAttachment.class,此目录和class文件名用于创建自己的ClassLoader时,作为参数传入
下面编写自己的加载类

public class myLoadClass extends ClassLoader{  

        //需要加载类.class文件的目录  
        private String classDir;  

        //无参的构造方法,用于class.newInstance()构造对象使用  
        public myLoadClass(){  
        }  

        public myLoadClass(String classDir){  
            this.classDir = classDir;  
        }  

        @SuppressWarnings("deprecation")  
        @Override  
        protected Class findClass(String name) throws ClassNotFoundException {  
            //class文件的路径  
            String classPathFile = classDir + "/" + name + ".class";  
            try {
                //将class文件进行解密  
                FileInputStream fis = new FileInputStream(classPathFile);  
                ByteArrayOutputStream bos = new ByteArrayOutputStream();  
                encodeAndDecode(fis,bos);  
                byte[] classByte = bos.toByteArray();  
                //将字节流变成一个class  
                return defineClass(classByte,0,classByte.length);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return super.findClass(name);  
        }  

        //测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下  
        public static void main(String[] args) throws Exception{  
            //配置运行参数  
            String srcPath = args[0];//ClassLoaderAttachment.class原路径  
            String desPath = args[1];//ClassLoaderAttachment.class输出的路径  
            String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);  
            String desPathFile = desPath + "/" + desFileName;  
            FileInputStream fis = new FileInputStream(srcPath);  
            FileOutputStream fos = new FileOutputStream(desPathFile);  
            //将class进行加密  
            encodeAndDecode(fis,fos);  
            fis.close();  
            fos.close();  
        }  

        /** 
         * 加密和解密算法 
         * @param is 
         * @param os 
         * @throws Exception 
         */  
        private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{  
            int bytes = -1;  
            while((bytes = is.read())!= -1){  
                bytes = bytes ^ 0xff;//和0xff进行异或处理  
                os.write(bytes);  
            }  
        }  
}

其中的main函数就是刚才加密class文件用的
然后写测试myDexLoader的测试方法:

public class myMain {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
            //输出ClassLoaderText的类加载器名称  
            System.out.println("ClassLoaderText类的加载器的名称:"+myMain.class.getClassLoader().getClass().getName());  
            System.out.println("System类的加载器的名称:"+System.class.getClassLoader());  
            System.out.println("List类的加载器的名称:"+List.class.getClassLoader());

            ClassLoader cl = myMain.class.getClassLoader();
            while(cl != null){
                System.out.print(cl.getClass().getName()+"->");  
                cl = cl.getParent();  
            }
            System.out.println(cl);
            /**
             * 如果在bin目录下有ClassLoaderAttachment.class,那么用的是系统的appClassLader,因为父类已经找到了
             * 如果想自己的类加载,那么需要把bin目录下对应的.class文件删掉,使父类找不到,这样自己的加载类才有机会去执行
             * **/
            Class myClass = new myLoadClass("temp_folder").loadClass("ClassLoaderAttachment");
            Date myDate = (Date)myClass.newInstance();
            System.out.println("classLoader is :"+myDate.getClass().getClassLoader().getClass().getName());
            System.out.println("date result is "+myDate.toString());
            ClassLoader cll = myDate.getClass().getClassLoader();
            while(cll != null){
                System.out.print(cll.getClass().getName()+"->");
                cll = cll.getParent();
            }
            System.out.println(cll);
    }
}

也是一个main方法,执行即可;把bin目录下的ClassLoaderAttachment.class删除后,最后得到的加载类链表是:

执行结果,加载类名称以及父类链表

由于temp_folder目录下是加了密的class文件,所以只能被我们自己能解密的ClassLoader进行加载执行,其实就是返回真是的字节码;
如果把它拷贝到bin目录,那么系统的sun.misc.Launcher$AppClassLoader会进行加载此class,但是文件是加了密的,所以其解析错误,则会报执行错误

你可能感兴趣的:(java)