目标:
当class重新编译后无需重启JVM就能加载更新过的类
术语:
目标类:指需要动态更新的类
对于目标类的限制:
- 构造函数不能有参数
- 必须实现一个接口
- 只对实例方法有效(因为接口中不能有静态方法)
- 没有考虑全局变量(可以在重新加载时复制原对象的成员,不过目前没实现)
测试代码:
ClassManager manager = new ClassManager();
String className = "com.hrtc.test.Test";//可换成符合上述约束的类
ITest t = (ITest) manager.getInstanceProxy(className);//当然接口也要随之变化
t.test();//test只是输出一段信息
//等待size秒,在这段时间内你必须重新编译生成你的.class文件,这里是com.hrtc.test.Test.class
int size = 5;
int i = 0;
while (i < size) {
System.out.println(i);
i++;
Thread.currentThread().sleep(1000);
}
t.test();//如果你修改了test输出内容则输出的内容会发生变化,此过程中没有重启jvm,也没有重新创建Test(上述代码中)
//测试动态代理和直接访问的效率,发现代理慢得多
long beginTime1 = System.currentTimeMillis();
int count = 10000;
for(int k = 0;k < count;k++){
t.test();
}
long endTime1 = System.currentTimeMillis();
ITest t2 = new Test();
long beginTime2 = System.currentTimeMillis();
for(int k = 0;k < count;k++){
t2.test();
}
long endTime2 = System.currentTimeMillis();
System.out.println("proxy time======="+(endTime1-beginTime1));
System.out.println("no proxy time======="+(endTime2-beginTime2));
要解决的问题及解决方法
如何重新加载类
重新加载class示例代码
/**
* 加载某个类
* @param c
* @return
* @throws IOException
*/
@SuppressWarnings( { "unchecked" })
public Class loadClass(Class c) throws IOException {
byte[] bs = loadByteCode(c);
Class cNew = super.defineClass(c.getCanonicalName(), bs, 0, bs.length);
return cNew;
}
/**
* 加载某个类的字节码
* @param c
* @return
* @throws IOException
*/
private byte[] loadByteCode(Class c) throws IOException {
int iRead = 0;
String path = c.getResource(c.getSimpleName() + ".class").getPath();
FileInputStream in = null;
ByteArrayOutputStream buffer = null;
try {
in = new FileInputStream(path);
buffer = new ByteArrayOutputStream();
while ((iRead = in.read()) != -1) {
buffer.write(iRead);
}
return buffer.toByteArray();
} finally {
FileUtility.safelyCloseInputStream(in);
FileUtility.safelyCloseOutputStream(buffer);
}
}
检测类变化的方法
判断类创建的时间如变化则重新加载
/**
* 保存类路径和时间
*/
private static Map mapModify = new HashMap();
private boolean hasChanged(Class c) throws IOException {
boolean isChanged = false;
String path = c.getResource(c.getSimpleName() + ".class").getPath();
File f = new File(path);
if (f.exists()) {
Date newDate = new Date(f.lastModified());
Date oldDate = null;
String key = f.getCanonicalPath();
if (mapModify.containsKey(key)) {
oldDate = (Date) mapModify.get(key);
} else {
oldDate = firstDate;
}
isChanged = oldDate.compareTo(newDate) < 0;
if (isChanged) {
mapModify.put(key, newDate);
}
}
return isChanged;
}
检测类变化的时机
创建类时检查
/**
* 如果class文件重新生成过会自动加载
*
* @param name
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
* @throws IOException
*/
public Object getInstance(String name) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, IOException {
Class c = Class.forName(name);
Class cNew = reloadClass(c);
if (cNew == null) {
cNew = c;
}
Object o = cNew.newInstance();
return o;
}
public synchronized Class reloadClass(Class c) throws IOException {
Class cNew = null;
if (hasChanged(c)) {
cNew = loadClass(c);
}
return cNew;
}
调用方法时检查
创建代理对象,自定义方法拦截器,intercept为方法拦截器中的一个方法如下
private Object target;
/**
* 在调用类时判断该类是否重新编译过,如编译过则调用新类的方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object targetObject = null;
Class c = target.getClass();
Class cNew = manager.reloadClass(c);
if (cNew == null) {
targetObject = target;
} else {
targetObject = cNew.newInstance();
this.setTarget(targetObject);
}
Object returnValue = method.invoke(targetObject, args);
return returnValue;
}
public void setTarget(Object target) {
this.target = target;
}
如何更新原来已创建的对象
当class发生改变时可以更新生成新的Class类从而创建新的对象,但是原来已创建的对象怎么办呢,可以用委托实现,代码同上,此处拦截类其实是个委托类,他把方法转给target对象,并在类更新时重新定义target的引用,而从外部调用看是感觉不到这点的。
所有类代码
package com.hrtc.dynamic.hot;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import net.sf.cglib.proxy.Enhancer;
import com.hrtc.dynamic.proxy.DynamicProxyFactory;
import com.hrtc.test.ITest;
import com.hrtc.test.Test;
/**
* 创建可以动态更新的java对象<br>
* 限制:构造函数不能有参数
* 必须实现一个接口
* 只能有实例方法(因为接口中不能有静态方法)
* @author xuwei
* Jul 9, 2008 12:01:00 PM
*/
public class ClassManager {
/**
* 保存类路径和时间
*/
private static Map mapModify = new HashMap();
/**
* 该类被加载时的时间
*/
private static Date firstDate = new Date();
/**
* 如果class文件重新生成过会自动加载,只有重新创建才会更新
*
* @param name
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
* @throws IOException
*/
public Object getInstance(String name) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, IOException {
Class c = Class.forName(name);
Class cNew = reloadClass(c);
if (cNew == null) {
cNew = c;
}
Object o = cNew.newInstance();
return o;
}
/**
* 创建代理对象,如果class文件重新生成过会自动加载,调用原先以实例化的方法时也会更新类
*
* @param name
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
* @throws IOException
*/
public Object getInstanceProxy(String name) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, IOException {
Object target = getInstance(name);
DynamicProxyFactory factory = new DynamicProxyFactory(
new HotInvocationHandler(this));
return factory.newProxyInstance(target);
}
/**
* 重新加载类
*
* @param c
* @return
* @throws IOException
*/
public synchronized Class reloadClass(Class c) throws IOException {
Class cNew = null;
if (hasChanged(c)) {
cNew = loadClass(c);
}
return cNew;
}
private boolean hasChanged(Class c) throws IOException {
boolean isChanged = false;
String path = c.getResource(c.getSimpleName() + ".class").getPath();
File f = new File(path);
if (f.exists()) {
Date newDate = new Date(f.lastModified());
Date oldDate = null;
String key = f.getCanonicalPath();
if (mapModify.containsKey(key)) {
oldDate = (Date) mapModify.get(key);
} else {
oldDate = firstDate;
}
isChanged = oldDate.compareTo(newDate) < 0;
if (isChanged) {
mapModify.put(key, newDate);
}
}
return isChanged;
}
private Class loadClass(Class c) throws IOException {
ClassLoaderAdvisor classLoader = new ClassLoaderAdvisor();
Class cNew = classLoader.loadClass(c);
return cNew;
}
public static void main(String[] args) throws IOException,
InstantiationException, IllegalAccessException,
ClassNotFoundException, InterruptedException {
ClassManager manager = new ClassManager();
String className = "com.hrtc.test.Test";
ITest t = (ITest) manager.getInstanceProxyJAVA(className);
t.test();
int size = 5;
int i = 0;
while (i < size) {
System.out.println(i);
i++;
Thread.currentThread().sleep(1000);
}
t.test();
i = 0;
while (i < size) {
System.out.println(i);
i++;
Thread.currentThread().sleep(1000);
}
t.test();
long beginTime1 = System.currentTimeMillis();
int count = 10000;
for(int k = 0;k < count;k++){
t.test();
}
long endTime1 = System.currentTimeMillis();
ITest t2 = new Test();
long beginTime2 = System.currentTimeMillis();
for(int k = 0;k < count;k++){
t2.test();
}
long endTime2 = System.currentTimeMillis();
System.out.println("proxy time======="+(endTime1-beginTime1));
System.out.println("no proxy time======="+(endTime2-beginTime2));
}
}
package com.hrtc.dynamic.hot;
import java.lang.reflect.Method;
import com.hrtc.dynamic.proxy.DefaultInvocationHandler;
/**
* 拦截java方法,更新新的类
* @author xuwei
* Jul 9, 2008 12:02:26 PM
*/
public class HotInvocationHandler extends DefaultInvocationHandler {
private ClassManager manager;
public HotInvocationHandler(ClassManager manager) {
this.manager = manager;
}
/**
* 在调用类时判断该类是否重新编译过,如编译过则调用新类的方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object targetObject = null;
Class c = target.getClass();
Class cNew = manager.reloadClass(c);
if (cNew == null) {
targetObject = target;
} else {
targetObject = cNew.newInstance();
this.setTarget(targetObject);
}
Object returnValue = method.invoke(targetObject, args);
return returnValue;
}
}
package com.hrtc.dynamic.hot;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import com.hrtc.util.FileUtility;
public class ClassLoaderAdvisor extends ClassLoader {
public ClassLoaderAdvisor() {
}
public ClassLoaderAdvisor(ClassLoader parentLoader) {
super(parentLoader);
}
/**
* 加载某个类
* @param c
* @return
* @throws IOException
*/
@SuppressWarnings( { "unchecked" })
public Class loadClass(Class c) throws IOException {
byte[] bs = loadByteCode(c);
Class cNew = super.defineClass(c.getCanonicalName(), bs, 0, bs.length);
return cNew;
}
/**
* 加载某个类的字节码
* @param c
* @return
* @throws IOException
*/
private byte[] loadByteCode(Class c) throws IOException {
int iRead = 0;
String path = c.getResource(c.getSimpleName() + ".class").getPath();
FileInputStream in = null;
ByteArrayOutputStream buffer = null;
try {
in = new FileInputStream(path);
buffer = new ByteArrayOutputStream();
while ((iRead = in.read()) != -1) {
buffer.write(iRead);
}
return buffer.toByteArray();
} finally {
FileUtility.safelyCloseInputStream(in);
FileUtility.safelyCloseOutputStream(buffer);
}
}
}
package com.hrtc.dynamic.proxy;
import java.lang.reflect.Proxy;
/**
* java代理工厂实现
* @author xuwei
* Jul 9, 2008 12:02:48 PM
*/
public class DynamicProxyFactory {
/*
* 方法处理者
*/
private DefaultInvocationHandler invocationHandler;
public DynamicProxyFactory() {
this(null);
}
/**
*
* @param invocationHandler
*/
public DynamicProxyFactory(DefaultInvocationHandler invocationHandler) {
if (invocationHandler == null) {
this.invocationHandler = new DefaultInvocationHandler();
} else {
this.invocationHandler = invocationHandler;
}
}
/**
* 创建代理对象
* @param target
* @return
*/
public Object newProxyInstance(final Object target) {
invocationHandler.setTarget(target);
Object proxy = Proxy.newProxyInstance(target.getClass()
.getClassLoader(), target.getClass().getInterfaces(),
invocationHandler);
return proxy;
}
}
package com.hrtc.dynamic.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 默认代理处理类
* @author xuwei
* Jul 9, 2008 12:03:00 PM
*/
public class DefaultInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
protected Object target;
public DefaultInvocationHandler() {
}
/**
* 处理方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before invoke");
Object returnValue = method.invoke(target, args);
System.out.println("after invoke");
return returnValue;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
package com.hrtc.test;
public interface ITest {
void test();
}
package com.hrtc.test;
public class Test implements ITest {
public void test() {
System.out.println("call test method:modify here");
}
}
目前为止上述讲到的限制没有解决,不知道有没有解决方案?
本文参考了http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1
分享到:
相关推荐
综上所述,"springboot+java类热加载"是一个涉及SpringBoot框架、Java类加载机制、动态编译以及依赖管理的综合技术话题。通过理解这些概念和相关工具,开发者可以更高效地进行迭代开发,提高生产力。在实际应用中,...
这篇博客“Java类动态加载(一)——java源文件动态编译为class文件”可能主要探讨了如何在运行时将Java源代码(.java)编译成对应的字节码文件(.class),并将其加载到Java虚拟机(JVM)中。以下是对这个主题的详细解析...
在Java编程中,动态编译和加载类是一种高级特性,它允许程序在运行时编译源代码并将其加载到Java虚拟机(JVM)中。这种能力对于开发灵活性高、可扩展性强的应用程序非常有用,比如在服务器端处理动态生成的代码、...
下面我们将深入探讨Java动态加载jar文件的原理和实践方法。 首先,我们需要理解Java的类加载机制。Java中的类是由类加载器(ClassLoader)负责加载的。默认情况下,Java虚拟机(JVM)提供了三个内置的类加载器:...
### Java反射机制与动态加载实例类 在Java中,反射是一种强大的工具,允许程序在运行时检查和修改其结构和行为。通过反射,我们可以动态地加载类、创建对象、访问和修改字段、调用方法等。本文将深入探讨Java反射...
在Java编程中,动态编译代码并热加载类是一项重要的技术,它允许程序在运行时修改或添加新的类,而无需重启应用。这种能力对于快速迭代开发、调试和性能优化非常有用。本主题将深入探讨Java中的动态编译与热加载机制...
在Java编程语言中,动态类加载机制是一种强大的特性,它允许程序在运行时加载、实例化和执行未在编译时硬编码的类。这种能力是通过Java的反射API实现的,它为开发者提供了深入洞察和操作Java对象的能力。本文将深入...
### Java 类加载器详解 #### 一、类加载器概述 在Java中,类加载器(Class Loader)是一项核心机制,用于将字节码(.class文件)加载到JVM中,使其成为运行时的对象。类加载器不仅实现了类的加载功能,还确保了...
它不仅管理类的生命周期,还确保了类的正确加载和初始化,是Java动态特性的基石。 #### 类加载器的工作原理 Java类加载器遵循按需加载原则,即只有当应用程序真正需要使用某个类时,类加载器才会去加载它。这一...
动态加载则是利用反射技术,在程序运行时动态地加载类并实例化对象,这为Java应用程序提供了高度的灵活性和可扩展性。下面将深入探讨Java类的反射与动态加载相关知识点。 首先,我们要了解什么是类的反射。在Java中...
当我们谈论“Java实现的面向接口的动态加载驱动的方法”,我们实际上在讨论如何在运行时动态地加载实现了特定接口的类,以便于在不修改原有代码的情况下,插入新的功能或替换旧的实现。 以MySQL数据库驱动为例,...
总结来说,Java动态加载JAR或ZIP包是通过自定义类加载器实现的,它可以让我们在运行时按需加载外部库,提高系统的可扩展性和灵活性。这个过程涉及到类加载器的创建、文件的读取、类的解析和实例化等多个步骤,是一项...
添加动态执行的编译环境 options 是个集合,添加内容,字符集,classpath等 * 6.传入JavaFileObject的java文件,是个集合,创建JavaSourceObject实现这个接口,Kind.SOURCE.extension = '.java' * 7.创建任务并...
它不仅关乎程序的启动与运行,更是Java动态性与灵活性的基础。本文旨在深入探讨Java类加载的过程、原理及其在实际开发中的应用。 #### 二、类与数据的概念 在Java中,一个类定义了对象的行为与状态。类定义了可...
Java Swing 是Java GUI(图形用户界面)库,用于构建...通过以上知识点的应用,你可以创建一个能够动态加载数据且支持多选的Java Swing下拉框。这种组件在各种数据选择场景中都非常有用,如配置设置、过滤器选择等。
JTable实现下拉动态加载数据,滑动动态加载数据,纯原生态java。
在Java编程语言中,类的加载是一个至关重要的过程,它涉及到程序运行时的动态性与可扩展性。Java类是从JAR(Java Archive)包中加载的,JAR文件是Java平台特有的归档格式,用于收集多个类文件、相关的元数据和其他...
基于接口、抽象类实现不停机动态调整代码的目的,将修改后的源码文件放置于指定目录下,读取文件后执行动态编译方法,即可将该类重新加载,新的类可以在Spring容器从新注册,且仅在当前窗口生效。如果重启了服务或...
Java 动态类加载机制研究及应用 Java 动态类加载机制研究及应用是基于 Java 虚拟机(JV M)机制的,旨在实现 Java 应用程序中动态加载类文件,而不影响其他功能模块的正常运行。为了实现这个目标,需要对 Java 类...