精华内容
下载资源
问答
  • 如何实现自己的ClassLoader

    千次阅读 2018-04-22 22:31:20
    ClassLoader能够完成的事情无非有以下几种情况: ...对我们自己的要加载的类做特殊处理,如保证通过网络传输的类的安全性,可以将类经过加密后再传输,在加载到JVM之前需要对类的字节码再解密...

    ClassLoader能够完成的事情无非有以下几种情况:

    • 在自定义路径下查找自定义的class类文件,也许我们需要的class文件并不总是已经设置好的classpath下,那么我摸嗯必须想办法来找到这个类,在这种情况下,我们需要自己实现一个ClassLoader
    • 对我们自己的要加载的类做特殊处理,如保证通过网络传输的类的安全性,可以将类经过加密后再传输,在加载到JVM之前需要对类的字节码再解密,,这个过程就可以在自定义的ClassLoader中实现。
    • 可以定义类的实现机制,如果我们可以检查已经加载的calss文件是否修改,如果修改了,可以重新加载这个类,从而实现类的热部署。

    加载自定义路径下的class文件

    我们自己实现一个ClassLoader,并指定这个ClassLoader的加载路径可以通过如下方式来实现;

    import java.io.*;
    
    public class PathClassLoader extends ClassLoader{
        private String classPath;
        private String packageName;
    
        public PathClassLoader(String classPath,String packageName) {
            this.classPath = classPath;
            this.packageName = packageName;
        }
    
        protected  Class<?> findClass(String name) throws ClassNotFoundException {
            if (packageName.startsWith(name)){
                byte[] classData = getData(name);
                if (classData!=null){
                    return  defineClass(name,classData,0,classData.length);
                }
                else {
                    throw new ClassNotFoundException();
                }
            }
            else {
                return super.loadClass(name);
            }
        }
    
        private byte[] getData(String className) {
            String path = classPath+ File.separatorChar+className.replace('.',File.separatorChar)+".class";
            try {
                InputStream is = new FileInputStream(path);
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                byte[] buffer = new byte[2048];
                int num =0;
                while ((num=is.read(buffer))!=-1){
                    stream.write(buffer,0,num);
                }
                return stream.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    
    

    在上面这段代码中,到classpath目录下去加载指定包名的class文件,如果不是非设置好的class path,仍然使用父类加载器去加载。
    还有一种方式是继承URLClassLoader类,然后设置自定义路径的URL来加载URL下的类,这种方式更佳,如下:

    import java.net.URL;
    import java.net.URLClassLoader;
    
    public class URLPathClassLoader extends URLClassLoader{
        private String packageName = "your.name.classPackage";
    
        public URLPathClassLoader(URL[] classPath,ClassLoader parent) {
            super(classPath,parent);
        }
    
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            Class<?> aClass = findLoadedClass(name);
            if (aClass!=null){
                return aClass;
            }
            if (packageName.startsWith(name)){
                return super.loadClass(name);
            }else {
                return findClass(name);
            }
        }
    
    }
    

    我们将指定的目录转换为URL路i纪念馆,然后作为参数创建URLPathClassLoader 对象,那么这个ClassLoader在加载时就在UTL指定的目录下查找指定的类文件。

    加载自定义格式的calss文件

    假设我们通过网络从远处主机上下载一个class文件的字节码,但是为了安全性,在传输之前对这个字节码进行了简单的加密处理,然后再通过网络传世。当客户端接收到这个类的字节码后需要经过解密才能还原成原始的格式,然后再通过ClassLoader的defineClass()方法创建这个java.lang.class的实例,最后完成类的加载工作,如下:

    import java.io.*;
    
    public class NetClassLoader extends ClassLoader{
        private String classPath;
        private String packageName = "net.you.classloader";
    
        public NetClassLoader(String classPath) {
            this.classPath = classPath;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            Class<?> aClass = findLoadedClass(name);
            if (aClass!=null){
                return aClass;
            }
            if (packageName.startsWith(name)){
                        byte[] classData = getData(name);
                       if (classData!=null){
                           return  defineClass(name,classData,0,classData.length);
                       }
                       else {
                           throw new ClassNotFoundException();
                       } 
            }else {
                   return super.loadClass(name);
               }
            }
    
            private byte[] getData(String className) {
                String path = classPath+ File.separatorChar+className.replace('.',File.separatorChar)+".class";
                try {
                    InputStream is = new FileInputStream(path);
                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
                    byte[] buffer = new byte[2048];
                    int num =0;
                    while ((num=is.read(buffer))!=-1){
                        stream.write(buffer,0,num);
                    }
                    return stream.toByteArray();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
            protected byte[] deCode(byte[] src){
                byte[] decode = null;
                //对src字节码进行解码处理
                return decode;
            }
        }
    

    在方法decode()中 ,可以对i网络传输过来的字节码进行某种解码处理, 然后返回正确的class字节码,调用defineClass()来创建java.lang.class的实例

    实现类的热部署

    我们知道,JVM再加载类之前会 检查请求的类是否已经被加载过来,也就是要调用findLoadedClass()方法查看是否能够返回实例。如果类已经加载过来,再调用loadClass()将会导致类冲突。但是JVM表示一个类是否是同一个类会有两个条件。一是看这个类的完整类名是否一样,这个类名包括类所在的包名。二是看加载这个类的ClassLoader是否是通过一个,这里所说的同一个是指ClassLoader的实例是否是同一个实例。即便是同一个ClassLoader类的两个实例,加载同一个类也会不一样。所以要实现类的热部署可以创建不同的ClassLoader的实例对象,然后通过这个不同的实例对象来加载同名的类,如下:

    import java.io.*;
    
    public class ClassReloader extends ClassLoader{
        private  String classPath;
    
    
        public ClassReloader(String classPath) {
            this.classPath = classPath;
        }
        protected  Class<?> findClass(String name) throws ClassNotFoundException {
                byte[] classData = getData(name);
                if (classData!=null){
                    return  defineClass(name,classData,0,classData.length);
                }
                else {
                    throw new ClassNotFoundException();
                }
        }
    
        private byte[] getData(String className) {
            String path = classPath+ File.separatorChar+className.replace('.',File.separatorChar)+".class";
            try {
                InputStream is = new FileInputStream(path);
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                byte[] buffer = new byte[2048];
                int num =0;
                while ((num=is.read(buffer))!=-1){
                    stream.write(buffer,0,num);
                }
                return stream.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static void main(String[] args)  {
           try {
               String path = "E:\\workspace\\jwt_demo\\target\\classes";
               ClassReloader reloader = new ClassReloader(path);
               String classname = "com.hxuhao.model.User";
               Class r = reloader.findClass(classname);
               System.out.println(r.newInstance());
               ClassReloader reloader1 = new ClassReloader(path);
               Class r1 = reloader1.findClass(classname);
               System.out.println(r1.newInstance());
           }catch (Exception e){
               e.printStackTrace();
           }
    
        }
    }
    

    运行结果

    com.hxuhao.model.User@6d6f6e28
    com.hxuhao.model.User@330bedb4
    

    运行上面的代码打印出来的是两个不同
    的类实例对象,如果不是创建了两个不同的ClassReloader对象,如将上面main方法改成下面的代码:

     public static void main(String[] args)  {
           try {
               String path = "E:\\workspace\\jwt_demo\\target\\classes";
               ClassReloader reloader = new ClassReloader(path);
               String classname = "com.hxuhao.model.User";
               Class r = reloader.findClass(classname);
               System.out.println(r.newInstance());
               Class r1 = reloader.findClass(classname);
               System.out.println(r1.newInstance());
           }catch (Exception e){
               e.printStackTrace();
           }
    
        }

    运行后,报错:
    重复加载一个类就会抛出java.lang.LinkageError

    com.hxuhao.model.User@6d6f6e28
    Exception in thread "main" java.lang.LinkageError: loader (instance of  ClassReloader): attempted  duplicate class definition for name: "com/hxuhao/model/User"
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
        at ClassReloader.findClass(ClassReloader.java:13)
        at ClassReloader.main(ClassReloader.java:46)

    使用不同的Classloader实例加载同一个类,会不会导致JVM的PermGen区无限增大?答案是否定的,因为我们的classloader对象也会和其他对象一样,当没有对象再引用它以后,也会被JVM回收。但是需要注意的一点是,被这个Classloader加载的类的字节码会保存在JVM的PermGen区,这个数据一般只是在执行Full GC时才会被回收的,所以如果在你的应用中都是大量的动态类加载,FUll GC 又不是太频繁,也要主要permGen区的大小,防止内存溢出。

    Java应不应该动态加载类

    我想大家都知道用JAVA有一个痛楚,就是修改一个类,必须重启一遍,很费时。于是就想能不能来个动态类的加载而不需要重启JVM,如果你了解JVM的工作机制,就应该放弃这个念头。

    Java的优势正式基于共享对象的机制,达到信息的高度共享,也就是通过保存并持有对象的状态而省去类信息的重复创建和回收。我们知道对象一旦被创建,这个对象就可以被人持有和利用。
    假如,我只说说,假如我们能够动态加载一个对象进入JVM,但是如何做到JVM中对象的平滑过渡?几乎不可能!虽然在JVM中对象只有一份,在理论上可以直接替换这个对象,然后更新Java栈中所有对原对象的引用关系。看起来好像对象可以被替换了, 但是这仍然不可行,因为它违反了JVM的设计原则,对象的引用关系只有对象的创建者持有和使用,JVM不可以干预对象的引用关系,因为JVM并不知道对象时怎么被使用的,这就涉及JVM并不知道对象的运行时类型而只知道编译时类型。
    假如一个对象的属性结构被修改,但是在运行时其他对象可能仍然引用该属性。
    虽然完全的无障碍的替换时不现实的。但是如果你非要那么做,也还是有一些“旁门左道”的。前面的分析造成不能动态提供类对象的关键是,对象的状态被保存了,并且被其他对象引用了,一个简单的解决方法就是不保存对象的状态,对象被创建使用后就被释放掉,下次修改后,对象也就是新的了
    这种方式是不是很好呢?这就是JSP,它难道不是可以动态加载吗?也许你已经想到了,所有其他解释性语言都是如此。

    展开全文
  • 如何实现自己的classloader 如何实现自己的classloader来加载类 刚开始学习加载的时候,接触到的是HeloWorld程序,当时不知道为什么在public static void main(String [] args) 方法里写了System.out.println(&...

    如何实现自己的classloader

    如何实现自己的classloader来加载类      刚开始学习加载的时候,接触到的是HeloWorld程序,当时不知道为什么在public static void main(String [] args) 方法里写了System.out.println("Hello World!") 就可以在控制台打出“Hello World!”来,确实的说,是什么东西隐蔽在后面执行了我们写的这段代码,通过后来的学习,知道了所有的class都是通过classloader来加载的。java规范这么说,Java的ClassLoader就是用来动态装载class的,ClassLoader对一个class只会装载一次,JVM使用的ClassLoader一共有4种:
              启动类装载器,标准扩展类装载器,类路径装载器和网络类装载器。
    这4种ClassLoader的优先级依次从高到低,使用所谓的“双亲委派模型”。确切地说,如果一个网络类装载器被请求装载一个java.lang.Integer,它会首先把请求发送给上一级的类路径装载器,如果返回已装载,则网络类装载器将不会装载这个java.lang.Integer,如果上一级的类路径装载器返回未装载,它才会装载java.lang.Integer。

            再说说Package权限。Java语言规定,在同一个包中的class,如果没有修饰符,默认为Package权限,包内的class都可以访问。但是这还不够准确。确切的说,只有由同一个ClassLoader装载的class才具有以上的Package权限。比如启动类装载器装载了java.lang.String,类路径装载器装载了我们自己写的java.lang.Test,它们不能互相访问对方具有Package权限的方法。这样就阻止了恶意代码访问核心类的Package权限方法。     
            现在来通过扩展ClassLoader类实现一个自己的类装载器,每个Class对象都有一个引用指向装载他的ClassLoader,可以通过public ClassLoader getClassLoader()方法得到它。为了创建自己的类装载器我们应该扩展ClassLoader类,这是一个抽象类。假设要从本地文件系统使用我们实现的类装载器装载一个类,创建一个FileClassLoader extends ClassLoader,需要覆盖ClassLoader中的findClass(String name)方法,这个方法通过类的名字而得到一个Class对象。

    package test.International;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    public class FileClassLoader extends ClassLoader{
        
    public static final String drive = "d:/";
        
    public static final String fileType = ".class";
        
    public FileClassLoader() {
             super();
         }

        
    public FileClassLoader(ClassLoader arg0) {
             super(arg0);
         }

        
    public Class findClass(String name) {
            
    byte[] data = loadClassData(name);
            
    return defineClass(name, data, 0, data.length);
         }

        
    public byte[] loadClassData(String name) {
             FileInputStream fis
    = null;
            
    byte[] data = null;
            
    try {
                 fis
    = new FileInputStream(new File(drive + name + fileType));
                 ByteArrayOutputStream baos
    = new ByteArrayOutputStream();
                
    int ch = 0;
                
    while ((ch = fis.read()) != -1) {
                     baos.write(ch);
                 }

                 data
    = baos.toByteArray();
             }
    catch (IOException e) {
                 e.printStackTrace();
             }

            
    return data;
         }

        
    public static void main(String[] args) throws Exception {
             FileClassLoader loader
    = new FileClassLoader();
             Class objClass
    = loader.loadClass("HelloWorld", true);
             Object obj
    = objClass.newInstance();
             System.
    out.println(objClass.getName());
             System.
    out.println(objClass.getClassLoader());
         }

    }

    现在把HelloWorld.java放到默认包下,然后编译,把HelloWorld.class放到D:\盘根目录下,运行FileClassLoader类,控制台输出:

    HelloWorld
    sun.misc.Launcher$AppClassLoader@82ba41

    可见,我们的确加载上了HelloWorld.class

    展开全文
  • 只需要继承ClassLoader,然后重写一些方法实现自己的业务就可以了。 下面以 加载 ASM(等其他方式)产生的 class 的 byte数组 生成 Class 对象 为例子: private static class MyClassLoader extends ClassLoader { ...

    自定义ClassLoader

    如何自定义 自己的 classLoader,其实很简单;只需要继承ClassLoader,然后重写一些方法实现自己的业务就可以了。
    下面以 加载 ASM(等其他方式)产生的 class 的 byte数组 生成 Class 对象 为例子:

    private static class MyClassLoader extends ClassLoader {
            public Class<?> defineClass(String name, byte[] b) {
                // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
                // 所以我们需要定义一个子类将这个方法暴露出来
                return super.defineClass(name, b, 0, b.length);
            }
    
        }
    

    利用字节码 ASM 技术 即时生成 class

    为什么要 利用ASM 即时生成 class 啦?
    在一些高级框架里面一般会使用到 某一个 实体类 来完成数据格式的自动转化,例如 spark 的 DataSet[Row] 转化为 DataSet[User] , DataSet[Person] 等,但是这个 Row 里面的字段可能是变动的,所以 可以 对应的 利用 row的字段信息 自动生成 实体类和getter setter 方法,完成自动转化 输出到别的系统。

    public class GeneratorClassByASM {
    	//这里是因为 ASM 有好人类型的 return 和 load 指令
        private static Map<Class, Integer> mappingReturns = new HashMap<>();
        private static Map<Class, Integer> mappingLoads = new HashMap<>();
        public static MyClassLoader cl = new MyClassLoader();
        static {
            mappingReturns.put(Integer.class, IRETURN);
            mappingLoads.put(Integer.class, ILOAD);
    
            mappingReturns.put(int.class, IRETURN);
            mappingLoads.put(int.class, ILOAD);
    
            mappingReturns.put(String.class, ARETURN);
            mappingLoads.put(String.class, ALOAD);
    
            mappingReturns.put(Long.class, LRETURN);
            mappingLoads.put(Long.class, LLOAD);
    
            mappingReturns.put(long.class, IRETURN);
            mappingLoads.put(long.class, ILOAD);
    
            mappingReturns.put(Double.class, DRETURN);
            mappingLoads.put(Double.class, DLOAD);
    
            mappingReturns.put(double.class, DRETURN);
            mappingLoads.put(double.class, DLOAD);
    
            mappingReturns.put(Float.class, FRETURN);
            mappingLoads.put(Float.class, FLOAD);
    
            mappingReturns.put(float.class, FRETURN);
            mappingLoads.put(float.class, FLOAD);
        }
    
        /**
         * ASM Class 的  set 方法
         * @param cw
         * @param fieldName
         * @param fileType
         * @param packageName 这里是 包 路径 /../../
         * @param className
         */
        private static void generatorSetMethod(ClassWriter cw, String fieldName, Class fileType, String packageName, String className){
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC , "set" + initUpper(fieldName),
                    "("+ Type.getType(fileType).getDescriptor() +")V", null, null);
            mv.visitVarInsn(mappingLoads.get(fileType), 0);
            mv.visitVarInsn(mappingLoads.get(fileType), 1);
            mv.visitFieldInsn(Opcodes.PUTFIELD, packageName + className, fieldName, Type.getType(fileType).getDescriptor());
            mv.visitInsn(RETURN);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
        }
    
        /**
         * ASM Class 的  get 方法
         * @param cw
         * @param fieldName
         * @param fileType
         * @param packageName 这里是 包 路径 /../../
         * @param className
         */
        private static void generatorGetMethod(ClassWriter cw, String fieldName, Class fileType, String packageName, String className){
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC , "get" + initUpper(fieldName),
                    "()" + Type.getType(fileType).getDescriptor(), null, null);
            mv.visitVarInsn(mappingLoads.get(fileType), 0);
            mv.visitFieldInsn(GETFIELD, packageName  + className, fieldName, Type.getType(fileType).getDescriptor());
            mv.visitInsn(mappingReturns.get(fileType));
            mv.visitMaxs(1,1);
            mv.visitEnd();
        }
    
        /**
         * ASM Class 的无参构造方法
         * @param cw
         */
        private static void generatorInitmethos(ClassWriter cw){
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC , "<init>",
                    "()V" , null, null);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
    
        /**
         * ASM Class 的  属性字段
         * @param cw
         * @param fieldName
         * @param fileType
         */
        private static void generatorFields(ClassWriter cw, String fieldName, Class fileType){
            cw.visitField(Opcodes.ACC_PRIVATE, fieldName, Type.getType(fileType).getDescriptor(), null, null);
        }
    
        private static String initUpper(String fieldName){
            return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        }
    
        public static byte[] geneClassMain(String packageName, String className){
            String packageName1 = packageName.replace(".", "/");
            System.out.println(packageName1);
            ClassWriter cw = new ClassWriter(0);
            // 定义对象头:版本号、修饰符、全类名、签名、父类、实现的接口
            cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, packageName1 + className,
                    null, "java/lang/Object", null);
            generatorInitmethos(cw);
            generatorFields(cw, "id", String.class);
            generatorFields(cw, "time", String.class);
            generatorSetMethod(cw, "id", String.class, packageName1, className);
            generatorGetMethod(cw, "id", String.class, packageName1, className);
            generatorSetMethod(cw, "time", String.class, packageName1, className);
            generatorGetMethod(cw, "time", String.class, packageName1, className);
            cw.visitEnd();
            return cw.toByteArray();
        }
    
        public static void main(String[] args) throws Exception {
    //        GeneratorClassByASM generatorClassByASM = new GeneratorClassByASM();
    //        generatorClassByASM.run1();
    
    //        GeneratorClassByASM.getPiClass();
    
        }
    
        private static class MyClassLoader extends ClassLoader {
            public Class<?> defineClass(String name, byte[] b) {
                // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
                // 所以我们需要定义一个子类将这个方法暴露出来
                return super.defineClass(name, b, 0, b.length);
            }
    
        }
    }
    

    如何 加载上面产生的 class 的 byte数组 到 特定的 ClassLoader

    大家都知道 不同的 classloader 产生的 class 对象 是不同的;相应的 你的 自定义 classloader 可以 通过 自己重写 的 defineClass 方法 把 你产生的 class 的 byte数组 转化为 Class 对象;但是 你的 正常的程序 所使用的 classloader 一般都是 Appclassloader(没有public defineClass 的方法 哦) ;所以 你的 非 自定义 的 classlaoder 是加载不到。

    那么怎么才能加载到啦?
    下面介绍2中方法。

    利用 java -classpath 间接加载 class 文件的方式

    这种方式比较简单,就是先把 class byte数组 保存到 java 运行是的 -classpath 的其中的某个path里面。那么当 jvm 需要加载这个class的时候,就会自动在 classpath 里面查找的,如果程序第一步就 生成这个 class 的class文件,那么别的 classloader 必然会加载到的。
    有一个最大的缺点 就是 必须 保存文件。

    利用 反射技术 加载 class byte 数组 到 指定的 classloader

    这种方法是比较复杂的。我们先来看看如何想到的。
    简单说一个怎么发现的,我们基本都知道 java 使用 ASM 技术 的一般是在 动态代理上。并且动态代理一共有2中 java自己的动态代理Proxy 和 cglib的 动态代理。哪它们产生的 class byte数组 是怎么 加载到 某个 classloader的。

    1. 看看 Proxy的实现 ,最后你会发现 把 class byte数组 转化为 class 对象的方法 是
      private static native Class<?> defineClass0(ClassLoader loader, String name,
      byte[] b, int off, int len);
      可以看到 它是 一个 native的方法。
      好吧 我们无能为力了。
    2. 接下来看看 cglib 是怎么 把 产生的 class byte数组 是怎么 加载到 某个 classloader的。
      通过查看源码,你可以看到 AbstractClassGenerator 的 create 方法 找到 gen = ReflectUtils.defineClass(className, b, loader); 这个 方法,这个不就是我们 苦苦 寻找的 方法吗!这个 方法在 ReflectUtils 类中。后面我们会看看它是怎么实现的。

    这里展示一下 使用方法:
    前置条件 需要 cglib的依赖。

    <dependency>
    	<groupId>cglib</groupId>
    	<artifactId>cglib</artifactId>
    	<version>3.1</version>
    </dependency>
    
    String packageName = "com.yyb.flink10.xxx.";
    String className = "Pi";
    byte[] byteOfClass = GeneratorClassByASM.geneClassMain(packageName, className);
    Class pi = ReflectUtils.defineClass(packageName + className, byteOfClass, LoadClassByClassloader.class.getClassLoader());
    Class<?> xx = Class.forName(packageName + className);
    System.out.println(xx.newInstance());
    

    看看就是这么简单!!!

    解析 cglib 把 class byte数组加载到 某个 classloader 的原理

    先看看 ReflectUtils.defineClass 方法:

    // className 类的全路径类名, byte[] b 就是 class 的 byte数组, loader 就是需要 那个 加载器 加载
    public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
            Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), PROTECTION_DOMAIN };
            //注意这个 DEFINE_CLASS ,是 java.lang.ClassLoader 的 特定 defineClass 方法,并设置方法是 可以访问的,最后通过 方法的invoke 调用 目标 classloader的 这个 特定 defineClass ,得到 加载完成 class 的 byte数组 后的 classloader 对象。自此大功告成!!!
            Class c = (Class)DEFINE_CLASS.invoke(loader, args);
            // Force static initializers to run.
            //最后 反射 检查 是否 已经 加载。
            Class.forName(className, true, loader);
            return c;
        }
    

    DEFINE_CLASS

    private static Method DEFINE_CLASS;
    private static final ProtectionDomain PROTECTION_DOMAIN;
    static {
       PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() {
       public Object run() {
            return ReflectUtils.class.getProtectionDomain()
       }
       });
            
       AccessController.doPrivileged(new PrivilegedAction() {
          public Object run() {
            try {//通过反射 拿到 java.lang.ClassLoader class 对象
                  Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this
                  //获取 java.lang.ClassLoader class 对象 的 getDeclaredMethod 的 特定方法
                 DEFINE_CLASS = loader.getDeclaredMethod("defineClass",
                                                                new Class[]{ String.class,
                                                                             byte[].class,
                                                                             Integer.TYPE,
                                                                             Integer.TYPE,
                                                                             ProtectionDomain.class });
                        //设置方法是 可以访问的
                        DEFINE_CLASS.setAccessible(true);
                    } catch (ClassNotFoundException e) {
                        throw new CodeGenerationException(e);
                    } catch (NoSuchMethodException e) {
                        throw new CodeGenerationException(e);
                    }
                    return null;
                }
            });
        }
    
    展开全文
  • ClassLoader类结构分析 ClassLoader的等级加载机制 如何加载class文件 ...如何实现自己的ClassLoader 实现类的热部署 Java应不应该动态加载类 转载于:https://www.cnblogs.com/hzzjj/p/9825603.html...

    ClassLoader类结构分析

    ClassLoader的等级加载机制

    如何加载class文件

    常见加载类错误分析

    常用的ClassLoader分析

    如何实现自己的ClassLoader

    实现类的热部署

    Java应不应该动态加载类

    转载于:https://www.cnblogs.com/hzzjj/p/9825603.html

    展开全文
  • 如果是一个遵循java已经规定好的机制的classloader(双亲委托以及加载依赖类的classloader继续加载剩下的类)。直接继承classloader就可以。(一般选择urlclassloder,他帮你实现了不少功能)。...
  • classloader

    2018-02-26 18:54:25
    类加载器加载类开放性类加载器...这一动作是放在Java虚拟机外部去实现的,以便让应用程序自己决定如何获取所需类。虚拟机规范并没有指明二进制字节流要从一个Class文件获取,或者说根本没有指明从哪...
  • 很多情况下,不得以...如果是一个遵循java已经规定好的机制的classloader(双亲委托以及加载依赖类的classloader继续加载剩下的类)。直接继承classloader就可以。(一般选择urlclassloder,他帮你实现了不少功能)。...
  • Classloader类加载器

    2008-01-19 21:19:00
    如何实现自己的classloader来加载类 刚开始学习加载的时候,接触到的是HeloWorld程序,当时不知道为什么在public static void main(String [] args) 方法里写了System.out.println("Hello World!") 就可以在控制台...
  • 我们看到,前面3个类加载和默认一致,CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义类加载器,它们分别加载/common/*、/server/*、/shared/*(在tomcat 6...
  • ClassLoader详解

    千次阅读 2018-05-09 20:07:04
    虚拟机设计团队把类加载阶段中“通过一个类全限定名来获取描述此类二进制字节流” 这个动作放到java虚拟机外部去实现,以便让应用程序自己决定去如何获取所需要类,实现这个动作代模块称之为“类加载器”...
  • 一:前言最近给一个非Java方向的朋友讲了下双亲委派模型,朋友让我写篇文章深度研究下JVM的ClassLoader,我确实也好久没写JVM相关的文章了,有点手痒痒,涂了皮炎平也抑制不住。我在向朋友解释的时候是这么说的:...
  • ClassLoader笔记

    2016-03-16 20:49:48
    类与类加载器类加载器阶段“通过一个类全限定名来获取描述此类二进制字节流”这个动作被放到了Java虚拟机外部去实现,以便让应用程序自己定义如何获取所需要类...
  • 最近excel工具包正在研究如何动态注入模板文件,妄图实现的功能是实现对正在运行代码进行不停机模板注入,顺便也加深自己对反射理解,使用了一下自定义类加载器。 后续可能还会加入支持数据库表自定义模板...
  • 双亲委派模式JVM加载类的实现方式,我们称为 双亲委托模型: 如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都是如此,因此所有的类...
  • Object、System等类都是BootstrapClassLoader加载的,Main是AppClassLoader加载的,People是MyClassLoader加载的,如果按照不同的ClassLoader加载的类不能相互访问,那么这里的People是如何访问Object System Main的...
  • 聊聊ClassLoader

    2019-09-05 19:45:45
    聊聊ClassLoader1、什么是类加载器2、需要注意点3、类...虚拟机设计团队把类加载阶段中“通过一个类全限定名来获取描述此类二进制字节流”这个动作放到Java虚 机外部实现,以便让应用程序自己决定如何去获...
  • 类加载器ClassLoader

    2018-09-15 18:46:09
     定义:将“通过一个类全限定名来获取描述此类二进制字节流”这个动作放到JVM外部去实现,以便让应用程序自己决定如何去获取所需要类。实现这个代码模块类就是类加载器——ClassLoader。其实说通俗一点...
  • Java ClassLoader学习总结

    2012-07-13 10:02:57
    主要内容包括 Java类加载机制及加载流程,以及如何定义自己的类加载器,如何实现类的热替换。
  • tomcat之所以创造了一堆自己的classloader体系,其目的之一就是为了实现应用的相互隔离,而对于许多应用,需要有共享的lib以便不浪费资源。那么,tomcat是如何实现这些功能的呢?通过tomcat源码,我会为大家详细介绍
  • 把类加载阶段中“通过一个类全限定名(博主注:绝对路径)来获取描述此类二进制字节流”这个动作放在Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要类。实现这个动作代码模块成为”类加载...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 148
精华内容 59
关键字:

如何实现自己的classloader