zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    How to build Linux system from kernel to UI layer
    Writing USB driver for Android
    Xposed Framework for Android 8.x Oreo is released (in beta)
    Linux Smartphone Operating Systems You Can Install Today
    Librem 5 Leads New Wave of Open Source Mobile Linux Contenders
    GUADEC: porting GNOME to Android
    Librem 5 – A Security and Privacy Focused Phone
    GNOME and KDE Join Librem 5 Linux Smartphone Party
    Purism计划推出安全开源的Linux Librem 5智能手机
    国产系统之殇:你知道的这些系统都是国外的
  • 原文地址:https://www.cnblogs.com/ou-pc/p/7672770.html
Copyright © 2011-2022 走看看