Java动态加载

1、需求

运行一个程序,每隔10秒去动态加载一个HelloWorld.java的文件,并执行里面的方法。在这个过程中, 随时可以修改HelloWorld.java文件中的内容,并让修改后的程序及时生效。

其中,HelloWorld.java代码如下:

package com.zhuyun.classloader;

public class HelloWorld{
	
	public void say(String name, int age){
		System.out.println("hello, " + name + ", age=" + age);
	}
}

2、失败案例

使用ClassLoader.getSystemClassLoader().loadClass()或者Class.forName()的方式动态加载类,代码如下:

public static void main(String[] args) throws Exception {

		while (true) {
			Class name = Class.forName("com.zhuyun.classloader.HelloWorld");
			Object newInstance = name.newInstance();
			Method method = name.getDeclaredMethod("say", String.class,int.class);
			method.invoke(newInstance, "world", 18);

			Thread.sleep(10000);
		}
	}

执行结果如下:

Java动态加载_第1张图片

在执行过程中修改HelloWorld.java的内容,如下:

package com.zhuyun.classloader;

public class HelloWorld{
	
	public void say(String name, int age){
		System.out.println("==========hello, " + name + ", age=" + age);
	}
}

等10秒后看打印的内容,发现并没有发生变化

Java动态加载_第2张图片

说明没有实现我们的需求。

3、失败原因

ClassLoader.getSystemClassLoader().loadClass()或者Class.forName()方法使用的类加载器是AppClassLoader,一般也称作系统加载器。它在加载类之前首先会检查类是否已被加载,如果是,它就不会重新加载一次,因此修改的结果不会生效。

因此,使用Java的内置类加载器不可能重新加载类。重新加载一个类你必须实现自己的ClassLoader子类。

4、自定义类加载器

自定义类加载器MyClassLoader.java代码如下:

package com.zhuyun.classloader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {

	@Override
	public Class findClass(String name) throws ClassNotFoundException {
		try {
			byte[] classDate = getDate(name);

			if (classDate == null) {
			}else {
				// defineClass方法将字节码转化为类
				return defineClass(null, classDate, 0, classDate.length);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return super.findClass(name);
	}

	// 返回类的字节码
	private byte[] getDate(String className) throws IOException {
		InputStream in = null;
		ByteArrayOutputStream out = null;
		try {
			in = new FileInputStream(className);
			out = new ByteArrayOutputStream();
			byte[] buffer = new byte[2048];
			int len = 0;
			while ((len = in.read(buffer)) != -1) {
				out.write(buffer, 0, len);
			}
			return out.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			in.close();
			out.close();
		}
		return null;
	}
}

这个类继承了ClassLoader,我们主要实现findClass的方法,其中defineClass()方法是将字节码转化为类,而提供的HelloWorld.java不是class字节码,因此在加载之前,我们需要先将它编译成字节码。代码如下:

package com.zhuyun.classloader;

import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Main {
	public static Class c;
	
	public static void main(String[] args) throws Exception{
		while (true) {
			//HelloWorld.java文件的路径
	        String fileName = "D:/workspace/cassandra_workspace/jvm/HelloWorld.java";
			
	        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
	        StandardJavaFileManager standardFileManager = javaCompiler.getStandardFileManager(null, null, null);
	        Iterable iterable = standardFileManager.getJavaFileObjects(fileName);

	        // 执行编译任务
	        CompilationTask task = javaCompiler.getTask(null, standardFileManager, null, null, null, iterable);
	        task.call();
	        standardFileManager.close();
			
			 //自定义类加载器的加载路径
	        MyClassLoader myClassLoader=new MyClassLoader();
	        
	        //编译后生成的字节码
	       c = myClassLoader.findClass("D:/workspace/cassandra_workspace/jvm/HelloWorld.class");
	        
	        if(c!=null){
	            Object obj=c.newInstance();
	            Method method=c.getDeclaredMethod("say", String.class, int.class);
	            method.invoke(obj, "world", 18);
	        }
			
			Thread.sleep(10000);
		}
	}
}

运行结果如下:

Java动态加载_第3张图片

在运行过程中修改HelloWorld.java文件

package com.zhuyun.classloader;

public class HelloWorld{
	
	public void say(String name, int age){
		System.out.println("==========hello, " + name + ", age=" + age);
	}
}

此时,运行结果发生变化

Java动态加载_第4张图片

如此一来,就能实现我们的需求了。

你可能感兴趣的:(Java)