`
hrtc
  • 浏览: 54744 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

动态加载java类

    博客分类:
  • java
阅读更多

目标:

当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

7
1
分享到:
评论
3 楼 ye_pj 2014-06-12  
不错解决了我的问题
2 楼 homelink 2009-03-19  
很想知道怎样才能动态加载带参数构造的类。
1 楼 my_lovelove 2009-01-18  
不错,谢谢分享

相关推荐

    springboot+java类热加载

    综上所述,"springboot+java类热加载"是一个涉及SpringBoot框架、Java类加载机制、动态编译以及依赖管理的综合技术话题。通过理解这些概念和相关工具,开发者可以更高效地进行迭代开发,提高生产力。在实际应用中,...

    动态编译、加载java类

    在Java编程中,动态编译和加载类是一种高级特性,它允许程序在运行时编译源代码并将其加载到Java虚拟机(JVM)中。这种能力对于开发灵活性高、可扩展性强的应用程序非常有用,比如在服务器端处理动态生成的代码、...

    Java 动态加载jar文件示例

    下面我们将深入探讨Java动态加载jar文件的原理和实践方法。 首先,我们需要理解Java的类加载机制。Java中的类是由类加载器(ClassLoader)负责加载的。默认情况下,Java虚拟机(JVM)提供了三个内置的类加载器:...

    Java反射动态加载实例类

    ### Java反射机制与动态加载实例类 在Java中,反射是一种强大的工具,允许程序在运行时检查和修改其结构和行为。通过反射,我们可以动态地加载类、创建对象、访问和修改字段、调用方法等。本文将深入探讨Java反射...

    JAVA类加载机制与动态代理

    ### JAVA类加载机制与动态代理 #### 一、类加载机制 ##### 1.1 类加载的时机 类加载机制负责将描述类的数据从`.class`文件加载到内存,并进行必要的校验、转换解析和初始化,使之成为可以被Java虚拟机直接使用的...

    动态加载类机制JAVA

    在Java编程语言中,动态加载类机制是一种强大的功能,它允许程序在运行时根据需要加载新的类或资源,而不是在编译时确定所有类。这种技术对于提高软件的灵活性、可扩展性和模块化至关重要,特别是在大型系统和插件式...

    Java语言-动态编译代码并热加载类

    在Java编程中,动态编译代码并热加载类是一项重要的技术,它允许程序在运行时修改或添加新的类,而无需重启应用。这种能力对于快速迭代开发、调试和性能优化非常有用。本主题将深入探讨Java中的动态编译与热加载机制...

    反射,动态加载指定类调用类中的方法

    1. `Class&lt;?&gt;`: 这是所有Java类的通用表示,通过`Class.forName()`方法可以动态加载指定的类。例如: ```java Class&lt;?&gt; clazz = Class.forName("com.example.YourClassName"); ``` 2. `Constructor&lt;?&gt;`: 代表类...

    Java技术----实现JAVA的动态类载入机制

    在Java编程语言中,动态类加载机制是一种强大的特性,它允许程序在运行时加载、实例化和执行未在编译时硬编码的类。这种能力是通过Java的反射API实现的,它为开发者提供了深入洞察和操作Java对象的能力。本文将深入...

    java类加载器

    ### Java 类加载器详解 #### 一、类加载器概述 在Java中,类加载器(Class Loader)是一项核心机制,用于将字节码(.class文件)加载到JVM中,使其成为运行时的对象。类加载器不仅实现了类的加载功能,还确保了...

    java类加载器实例

    它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java Applet 需要从远程下载 Java 类文件到浏览器中并执行。现在类加载器在 ...

    Java类加载器.pdf

    它不仅管理类的生命周期,还确保了类的正确加载和初始化,是Java动态特性的基石。 #### 类加载器的工作原理 Java类加载器遵循按需加载原则,即只有当应用程序真正需要使用某个类时,类加载器才会去加载它。这一...

    java 动态加载jar包

    总之,Java动态加载jar包涉及到反射、自定义类加载器以及插件系统等多个核心概念。通过这些技术,开发者可以构建出高度可扩展和灵活的应用程序,适应不断变化的需求。在具体实现过程中,还需要考虑安全性、性能优化...

    Java类的反射与动态加载

    动态加载则是利用反射技术,在程序运行时动态地加载类并实例化对象,这为Java应用程序提供了高度的灵活性和可扩展性。下面将深入探讨Java类的反射与动态加载相关知识点。 首先,我们要了解什么是类的反射。在Java中...

    Java 实现的面向接口的动态加载驱动的方法

    当我们谈论“Java实现的面向接口的动态加载驱动的方法”,我们实际上在讨论如何在运行时动态地加载实现了特定接口的类,以便于在不修改原有代码的情况下,插入新的功能或替换旧的实现。 以MySQL数据库驱动为例,...

    JAVA动态加载JAR zip包

    总结来说,Java动态加载JAR或ZIP包是通过自定义类加载器实现的,它可以让我们在运行时按需加载外部库,提高系统的可扩展性和灵活性。这个过程涉及到类加载器的创建、文件的读取、类的解析和实例化等多个步骤,是一项...

    Java类加载器原理

    Java 类加载器原理 Java 类加载器是Java虚拟机...类加载器的机制保证了Java的类加载过程是有序且安全的,同时也支持了Java的动态加载和模块化特性。在自定义类加载器时,需要遵循这些规则,以便正确地扩展JVM的功能。

    Java动态编译Java代码,运行在内存中,并执行

    添加动态执行的编译环境 options 是个集合,添加内容,字符集,classpath等 * 6.传入JavaFileObject的java文件,是个集合,创建JavaSourceObject实现这个接口,Kind.SOURCE.extension = '.java' * 7.创建任务并...

    Java调用Groovy,实时动态加载数据库groovy脚本

    2. 创建GroovyClassLoader:使用这个类加载器可以动态加载和执行Groovy脚本。它继承自Java的ClassLoader,能解析Groovy源码并生成字节码。 3. 加载并执行Groovy脚本:通过GroovyClassLoader的`parseClass()`方法...

    java swing 多选下拉框 支持动态加载数据

    Java Swing 是Java GUI(图形用户界面)库,用于构建...通过以上知识点的应用,你可以创建一个能够动态加载数据且支持多选的Java Swing下拉框。这种组件在各种数据选择场景中都非常有用,如配置设置、过滤器选择等。

Global site tag (gtag.js) - Google Analytics