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文件到内存有两种方式
- 隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存,例如:当类中继承或者引用某个类时,JVM在解析当前这个类不在内存中时,就会自动将这些类加载到内存中。
- 显示加载:在代码中通过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(); } } } }
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*/
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(); } } } }
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*/
动态编译和执行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("写入成功"); } }
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 */