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();
}
}
}
}
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