关于Java的ClassLoader

关于理论不多说,本人不善于理论。这里有一篇文章讲ClassLoad的,讲的很详细:Java ClassLoad详解

我这里所讲的是,我实际开发中曾经遇到一个问题,是这样的,以前有个需求是每天爬取多个日报的网站。然后我自己写了个爬虫框架,每个日报都用java实现了一个爬取规则类,框架会选出当天需要爬取的报纸,并对这些报纸没分报纸启动一个每日任务,对一份报纸来说,每隔半小时尝试爬取一次,爬取到数据结束任务。然而对于规则而言,有些网站经常会有变动,爬取规则就需要有相应的更新。那么问题来了,规则更新了怎么办?

最简单解决办法是,重新上传规则,然后重启Tomcat,然后继续运行。显然不太现实,后来通过对ClassLoader的研究,自己实现了一种ClassLoader,从而使每次加载类的时候重新读取该类。

下面是我的MyClassLoader的实现,以及测试办法

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
 *
 * @author yun
 * @date 2015年3月26日
 * @time 下午3:21:38
 * @todo 以字节数据读取类文件并加载
 *
 */
public class MyClassLoader extends ClassLoader {
	
	public MyClassLoader() {
		super(MyClassLoader.class.getClassLoader());
	}

	public Class<?> forName(String name) throws Exception {
		String path = "/" + name + ".class";
		InputStream is = System.class.getResourceAsStream(path);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int size = 0;
		while ((size = is.read()) != -1) {
			baos.write(size);
		}
		byte[] classData = baos.toByteArray();
		baos.close();
		is.close();
		return super.defineClass(name, classData, 0, classData.length, null);
	}
}


测试

任务类

import java.text.SimpleDateFormat;
import java.util.Date;
 
public class Task {
     
	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss ");
	
    public void execute() {
        //执行任务
        System.out.println(format.format(new Date()) + "Just A Test");//输出语句a
        System.out.println(format.format(new Date()) + "The Other Test");//输出语句b
    }
}

Quartz定时Job

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class ActionJob implements Job {

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		while (true) {
			try {
				String path = "Task";
				// 加载任务类
				Class<?> clas = Class.forName(path);//默认ClassLoad
//				Class<?> clas = new MyClassLoader().forName(path);//我自己实现的ClassLoad
				Object o = clas.newInstance();// 实例化已任务类对象
				// 获取启动方法,并启动
				clas.getMethod("execute").invoke(o);
				Thread.sleep(20000);//每隔20秒加载并执行一次Task
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

主方法入口

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class Start {

	public static void main(String[] args) {
		try {
			StdSchedulerFactory factory = new StdSchedulerFactory();
			Scheduler scheduler = factory.getScheduler();
			JobDetail detail = newJob(ActionJob.class)
					.withIdentity("d_name", "d_group")
					.build();
			Trigger trigger = newTrigger()
					.withIdentity("t_name", "t_group")
					.startNow()
					.build();
			scheduler.scheduleJob(detail, trigger);
			scheduler.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

测试方法:

1、以默认ClassLoader加载任务类,只开输出语句a,编译获取四个class文件

2、以默认ClassLoader加载任务类,打开输出语句a和b,编译获取四个class文件

3、以MyClassLoader加载任务类,只开输出语句a,编译获取四个class文件

4、以MyClassLoader加载任务类,打开输出语句a和b,编译获取四个class文件

在1中执行Start,每隔20秒输出一次语句a,不停止把2中的Task.class文件copy覆盖1中的Task.class,继续观察输出,会发现一只输出语句a,然后在删掉Task.class文件,继续观察输出,结果是继续输出语句a

技术分享

技术分享


可以看出在删除Task.class后程序依旧执行,说明修改Task.class不会影响任务的执行


然后停止之前的人物,执行3中的Start,只有输出语句a,然后修改把4中的Task.class覆盖3中的,继续观察数据,会发现输出语句a和b都有了

技术分享

删除Task.class之后

技术分享

报异常了,说明同过MyClassLoad每次都是重新读取任务类并加载的。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。