首页 > 技术文章 > java基础--jvm类加载器和动态编译JAVA代码和java执行js脚本语言

ou-pc 2017-10-15 17:16 原文

 

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

 

推荐阅读