• java基础--jvm类加载器和动态编译JAVA代码和java执行js脚本语言


    1.JVM类加载器

    启动类加载器(bootstrap classLoader):启动类加载器,负责加载java的核心类库,加载如(%JAVA_HOME%/lib)下的rt.jar(包含System,String等核心类)这样的核心类库。根类加载器不是classLoader的子类,它是J VM自身内部由C/C++实现的,并不是Java实现的。

    扩展类加载器(Extension classLoader):扩展类加载器,负责加载扩展目录(%JAVA_HOME%/jre/lib/ext)下的jar包,用户可把自己开发的类的jar放入ext目录下,即可扩展除核心类以为的新功能。

    系统类加载器(Application classLoader):系统类加载器或称为应用程序类加载器,是加载CLASSPATH环境变量所指定的jar包与类路径。一般来说,用户自定义的类就是由APP ClassLoader加载的。

    2.类加载器的双亲委派模型机制

    当一个类收到了类加载的请求,他首先不会自己尝试加载这个类,而是将这个请求委派给父类加载器来完成,父类加载器收到请求后,也会找到找到其父类加载器。所以类加载的请求都会传到bootstrap classLoader,只有当父类加载器无法加载时(在其类加载路径中找不到所需加载的class),子类加载器才会尝试自己去加载。

    3、JVM加载class文件到内存有两种方式

    1. 隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存,例如:当类中继承或者引用某个类时,JVM在解析当前这个类不在内存中时,就会自动将这些类加载到内存中。
    2. 显示加载:在代码中通过ClassLoader类来加载一个类,例如调用this.getClass.getClassLoader().loadClass()或者Class.forName()。

    4.自定义加载类

    若要实现自定义类加载器,只需要继承java.lang.ClassLoader 类,并且重写其findClass()方法即可。java.lang.ClassLoader 类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等,ClassLoader 中与加载类相关的方法如下:

     
    方法                                 说明
    getParent()  返回该类加载器的父类加载器。

    loadClass(String name) 加载名称为 二进制名称为name 的类,返回的结果是 java.lang.Class 类的实例。

    findClass(String name) 查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。

    findLoadedClass(String name) 查找名称为 name 的已经被加载过的类,返回的结果是 java.lang.Class 类的实例。

    resolveClass(Class<?> c) 链接指定的 Java 类。

    4.1  URLClassLoader  的使用

    File file = new File("D:/myjava/");
    URL url = file.toURL();
    URLClassLoader loader = new URLClassLoader(new URL[] { url });
    Class tidyClazz = loader.loadClass("Test");
    System.out.println(tidyClazz.getClassLoader());
    /*java.net.URLClassLoader@1f26ecd2*/

    5、实现类的热部署

      1、什么是类的热部署?

      所谓热部署,就是在应用正在运行的时候升级软件,不需要重新启用应用。

      对于Java应用程序来说,热部署就是运行时更新Java类文件。在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色。大多数基于Java的应用服务器,包括EJB服务器和Servlet容器,都支持热部署。

      类装入器不能重新装入一个已经装入的类,但只要使用一个新的类装入器实例,就可以将类再次装入一个正在运行的应用程序。

      2、如何实现Java类的热部署

      前面的分析,我们已经知道,JVM在加载类之前会检查请求的类是否已经被加载过来,也就是要调用findLoadedClass方法查看是否能够返回类实例。如果类已经加载过来,再调用loadClass会导致类冲突。

      但是,JVM判断一个类是否是同一个类有两个条件:一是看这个类的完整类名是否一样(包括包名),二是看加载这个类的ClassLoader加载器是否是同一个(既是是同一个ClassLoader类的两个实例,加载同一个类也会不一样)。

      所以,要实现类的热部署可以创建不同的ClassLoader的实例对象,然后通过这个不同的实例对象来加载同名的类

     6.代码区

       

    package com.zwj.commons;
    
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 自定义文件系统类加载器
     *
     */
    public class FileSystemClassLoader extends ClassLoader {
        
        private String rootDir;
        
        public FileSystemClassLoader(String rootDir){
            this.rootDir = rootDir;
        }
        
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            
            Class<?> c = findLoadedClass(name);
            
            //应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
            if(c!=null){
                return c;
            }else{
                ClassLoader parent = this.getParent();
                try {
                    c = parent.loadClass(name);       //委派给父类加载
                } catch (Exception e) {
    //                e.printStackTrace();
                }
                
                if(c!=null){
                    return c;
                }else{
                    byte[] classData = getClassData(name);
                    if(classData==null){
                        throw new ClassNotFoundException();
                    }else{
                        c = defineClass(name, classData, 0,classData.length);
                    }
                }
                
            }
            
            return c;
            
        }
        
        private byte[] getClassData(String classname){   //   d:/myjava/  com/bjsxt/test/User.class
            String path = rootDir +"/"+ classname.replace('.', '/')+".class";
            
    //        IOUtils,可以使用它将流中的数据转成字节数组
            InputStream is = null;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try{
                is  = new FileInputStream(path);
                
                byte[] buffer = new byte[1024];
                int temp=0;
                while((temp=is.read(buffer))!=-1){
                    baos.write(buffer, 0, temp);
                }
                
                return baos.toByteArray();
            }catch(Exception e){
                e.printStackTrace();
                return null;
            }finally{
                try {
                    if(is!=null){
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if(baos!=null){
                        baos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }        
        }    
    }
    FileSystemClassLoader 自定义文件系统类加载器
    package com.zwj.commons;
    
    /**
     * 测试自定义的FileSystemClassLoader
     *
     */
    public class Demo3 {
        public static void main(String[] args) throws Exception{
            FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava");
            FileSystemClassLoader loader2 = new FileSystemClassLoader("d:/myjava");
            
            Class<?> c = loader.loadClass("Test");
            Class<?> c2 = loader.loadClass("Test");
            Class<?> c3 = loader2.loadClass("Test");
    
            Class<?> c4 = loader2.loadClass("java.lang.String");
            Class<?> c5 = loader2.loadClass("Test");
            
            
            System.out.println(c.hashCode());
            System.out.println(c2.hashCode());
            System.out.println(c3.hashCode());    //同一个类,被不同的加载器加载,JVM认为也是不相同的类
            System.out.println(c4.hashCode());
            System.out.println(c4.getClassLoader());    //引导类加载器
            System.out.println(c3.getClassLoader());    //自定义的类加载器
            System.out.println(c5.getClassLoader());    //系统默认的类加载器
            
        }
    }
    /*1177842774
    1177842774
    499244572
    713035865
    null
    com.zwj.commons.FileSystemClassLoader@4d4bb075
    com.zwj.commons.FileSystemClassLoader@4d4bb075*/
    Demo3 测试自定义的FileSystemClassLoader
    package com.zwj.commons;
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    
    /**
     * 网络类加载器
     * @author 尚学堂高淇 www.sxt.cn
     *
     */
    public class NetClassLoader extends ClassLoader {
        
        //com.bjsxt.test.User   --> www.sxt.cn/myjava/  com/bjsxt/test/User.class      
        private String rootUrl;
        
        public NetClassLoader(String rootUrl){
            this.rootUrl = rootUrl;
        }
        
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            
            Class<?> c = findLoadedClass(name);
            
            //应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
            if(c!=null){
                return c;
            }else{
                ClassLoader parent = this.getParent();
                try {
                    c = parent.loadClass(name);       //委派给父类加载
                } catch (Exception e) {
    //                e.printStackTrace();
                }
                
                if(c!=null){
                    return c;
                }else{
                    byte[] classData = getClassData(name);
                    if(classData==null){
                        throw new ClassNotFoundException();
                    }else{
                        c = defineClass(name, classData, 0,classData.length);
                    }
                }
                
            }
            
            return c;
            
        }
        
        private byte[] getClassData(String classname){   //com.bjsxt.test.User   d:/myjava/  com/bjsxt/test/User.class
            String path = rootUrl +"/"+ classname.replace('.', '/')+".class";
            
    //        IOUtils,可以使用它将流中的数据转成字节数组
            InputStream is = null;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try{
                URL url = new URL(path);
                is  = url.openStream();
                
                byte[] buffer = new byte[1024];
                int temp=0;
                while((temp=is.read(buffer))!=-1){
                    baos.write(buffer, 0, temp);
                }
                
                return baos.toByteArray();
            }catch(Exception e){
                e.printStackTrace();
                return null;
            }finally{
                try {
                    if(is!=null){
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if(baos!=null){
                        baos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }        
        }    
    }
    NetClassLoader 网络类加载器
    package com.zwj.commons;
    
    
    /**
     * 线程上下文类加载器的测试
     * @author 尚学堂高淇 www.sxt.cn
     *
     */
    public class Demo05 {
        public static void main(String[] args) throws Exception {
            ClassLoader loader = Demo05.class.getClassLoader();
            System.out.println(loader);
            
            
            ClassLoader loader2 = Thread.currentThread().getContextClassLoader();
            System.out.println(loader2);
            
            Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("d:/myjava/"));
            System.out.println(Thread.currentThread().getContextClassLoader());
            
            Class<Test> c = (Class<Test>) Thread.currentThread().getContextClassLoader().loadClass("Test");
            System.out.println(c);
            System.out.println(c.getClassLoader());
            
        }
    }
    /*sun.misc.Launcher$AppClassLoader@3de5627c
    sun.misc.Launcher$AppClassLoader@3de5627c
    com.zwj.commons.FileSystemClassLoader@2b571dff
    class Test
    com.zwj.commons.FileSystemClassLoader@2b571dff*/
    Demo05 线程上下文类加载器的测试

    动态编译和执行class文件     用java调用js引擎执行js脚本

    package com.bjsxt.test;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.Writer;
    
    import javax.tools.JavaCompiler;
    import javax.tools.ToolProvider;
    
    public class Test {
     public static void main(String[] args) throws IOException {
          //通过IO流操作,将字符串存储成一个临时文件(Hi.java),然后调用动态编译方法!  
           String str = "public class Hi {public static void main(String[] args){System.out.println("HaHa,sxt!");}}";
           File file=new File("D://Hi.java");
           writeFile(file,str);
           JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            int result = compiler.run(null, null, null, "D://Hi.java");
            System.out.println(result==0?"编译成功":"编译失败");
            
            
            //通过Runtime调用执行类
          Runtime run = Runtime.getRuntime();  
          Process process = run.exec("java -cp  D://  Hi");  
        
         InputStream in = process.getInputStream();
         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
         String info = "";
         while((info=reader.readLine())!=null){
            System.out.println(info);
        }    
        
            /* try {
              URL[] urls = new URL[] {new URL("file:/"+"C:/myjava/")};
              URLClassLoader loader = new URLClassLoader(urls);
              Class c = loader.loadClass("HelloWorld");
              //调用加载类的main方法
              Method m = c.getMethod("main",String[].class);
              m.invoke(null, (Object)new String[]{});
              //由于可变参数是JDK5.0之后才有。
              //m.invoke(null, (Object)new String[]{});会编译成:m.invoke(null,"aa","bb"),就发生了参数个数不匹配的问题。
              //因此,必须要加上(Object)转型,避免这个问题。
              //public static void main(String[] args)
              
          } catch (Exception e) {
              e.printStackTrace();
          }    */
    
     
     }
     
     
     private static void writeFile(File file,String str) throws IOException {
         //1.定义字符输出流对象
         Writer fw=new FileWriter(file);
         BufferedWriter bw=new BufferedWriter(fw);
         
         //2.按行写入
         bw.write(str);
         bw.newLine();
         bw.write("//我爱你");
         bw.flush();
         
         //3.关闭流
         fw.close();
         System.out.println("写入成功");
         
     }
     
    }
    denamicCompile
    package com.bdqn.service;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Reader;
    import java.io.Writer;
    import java.net.URL;
    import java.util.List;
    
    import javax.script.Invocable;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    
    public class Demo {
         public static void main(String[] args) throws ScriptException, NoSuchMethodException, IOException {
            //获取脚本引擎对象
            ScriptEngineManager sem=new ScriptEngineManager();
            ScriptEngine engine=sem.getEngineByName("javascript");
            //定义变量,存储到引擎上下文中
            engine.put("msg", "i is a good man!"); 
            String str="var user={name:'i',age:19,schools:['清华','北大']};";
            str +="println(user.name);";//输出 user.name
            //执行脚本
            engine.eval(str);
            engine.eval("msg='sxt id  a good school';");
            System.out.println(engine.get("msg"));
            
            //定义函数
            engine.eval("function add(a,b){var sum=a+b; return sum;}");
           //取得调用接口         
           Invocable jsvocable=(Invocable)engine;
           //执行脚本中定义的方法
           Object result1=jsvocable.invokeFunction("add",new Object[]{13,20});
           System.out.println(result1);
           
           
           //导入其他java包,使用其他包中的java类
           //若深入了解细节,可以访问官网学习Rhino的语法 
           String jsCode="importPackage(java.util); var list=Arrays.asList(["北大","清华"])";
           engine.eval(jsCode);
           List<String> list=( List<String>) engine.get("list");
           for (String object : list) {
            System.out.println(object);
         }
          //执行一个js文件(我们将a.js至于项目的sr下即可)
           /* function test(){
             var a=3;
              var b=4;
             println("invoke js file:"+(a+b));
             }
             test();
            */
           URL url=Demo.class.getClassLoader().getResource("a.js");
           FileReader reader=new FileReader(url.getPath());
           engine.eval(reader);
           reader.close();//由于知识测试,就不用那么规范了。大家实际要用try catch
           
        } 
    }
    /*
     i
    sxt id  a good school
    33.0
    北大
    清华
    invoke js file:7
    */
    ScriptEngine
  • 相关阅读:
    excel unixtime与北京时间互转
    vim的漫漫长征路
    const常量
    第一章:绪论
    2.4奇偶校验
    2.3数据校验的基本原理
    2.2定点与浮点数据表示
    2.1机器数及其特点
    1.2计算机系统性能评价
    冯诺依曼结构原理及层次分析
  • 原文地址:https://www.cnblogs.com/ou-pc/p/7672770.html
Copyright © 2020-2023  润新知