今天在修改项目一个JSP文件时,突然想到Tomat是怎么实现动态实时加载JSP编译后的class类的?
查了半天资料,看了很多文章,终于明白是怎么回事了:ClassLoader,当tomcat发现jsp改变后,将用新的ClassLoader去加载新的类
具体原理我将单独总结一下,这里简单实现了动态加载类
1.定义服务类
public
class
Servlet {
public
void
service(){
System.out.println(
"运行服务方法"
);
}
}
2.定义服务线程
public
class
ServiceThread
extends
Thread{
public
void
run(){
try
{
ClassLoader classLoader
=
this
.getContextClassLoader();
Class clazz
= classLoader.loadClass("Servlet"
);
Method service
= clazz.getMethod("service",
null
);
service.invoke(clazz.newInstance(),
null
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
3.自定义ClassLoader
public
class
MyClassLoader
extends
ClassLoader{
@Override
public
Class loadClass(String name,
boolean
resolve)
throws
ClassNotFoundException{
try
{
//
我们要创建的Class对象
Class clasz =
null
;
//
必需的步骤1:如果类已经在系统缓冲之中
//
我们不必再次装入它
clasz =
findLoadedClass(name);
if
(clasz !=
null
)
return
clasz;
try
{
//
读取经过加密的类文件
if
(name.equals("Servlet"
)){
//加载class文件字节
byte
classData[] = Util.readFile(ProgramPathHelper.getProgramPath()+"/"+name + ".class"
);
if
(classData !=
null
) {
//
... 再把它转换成一个类
clasz = defineClass(name, classData, 0
,
classData.length);
}
}
}
catch
(Exception e) {
e.printStackTrace();
}
//
必需的步骤2:如果上面没有成功
//
我们尝试用默认的ClassLoader装入它
if
(clasz ==
null
)
clasz
=
findSystemClass(name);
//
必需的步骤3:如有必要,则装入相关的类
if
(resolve && clasz !=
null
)
resolveClass(clasz);
//
把类返回给调用者
return
clasz;
}
catch
(Exception ie) {
throw
new
ClassNotFoundException(ie.toString());
}
}
}
4.实现文件监听类
public
class
CCFileListener
implements
FileAlterationListener{
public
static
HashMap<String,ClassLoader> claMap =
new
HashMap<String, ClassLoader>
();
ZJPFileMonitor monitor
=
null
;
@Override
public
void
onStart(FileAlterationObserver observer) {
//
System.out.println("onStart");
}
@Override
public
void
onDirectoryCreate(File directory) {
System.out.println(
"onDirectoryCreate:" +
directory.getName());
}
@Override
public
void
onDirectoryChange(File directory) {
System.out.println(
"onDirectoryChange:" +
directory.getName());
}
@Override
public
void
onDirectoryDelete(File directory) {
System.out.println(
"onDirectoryDelete:" +
directory.getName());
}
@Override
public
void
onFileCreate(File file) {
System.out.println(
"onFileCreate:" +
file.getName());
dyncLoadClass(file.getName());
}
@Override
public
void
onFileChange(File file) {
//文件改变处理函数
System.out.println(
"onFileChange : " +
file.getName());
dyncLoadClass(file.getName());
}
private
void
dyncLoadClass(String className){
if
(className.contains("Servlet"
)){
//
ZJPFileMonitor.thread.setContextClassLoader(new MyClassLoader());
claMap.put(className,
new
MyClassLoader());
}
}
@Override
public
void
onFileDelete(File file) {
System.out.println(
"onFileDelete :" +
file.getName());
}
@Override
public
void
onStop(FileAlterationObserver observer) {
//
System.out.println("onStop");
}
}
public
class
CCFileMonitor {
FileAlterationMonitor monitor
=
null
;
public
CCFileMonitor(
long
interval)
throws
Exception {
monitor
=
new
FileAlterationMonitor(interval);
}
public
void
monitor(String path, FileAlterationListener listener) {
FileAlterationObserver observer
=
new
FileAlterationObserver(
new
File(path));
monitor.addObserver(observer);
observer.addListener(listener);
}
public
void
stop()
throws
Exception{
monitor.stop();
}
public
void
start()
throws
Exception {
monitor.start();
}
public
static
void
main(String[] args)
throws
Exception {
CCFileMonitor m
=
new
CCFileMonitor(5000
);
m.monitor(ProgramPathHelper.getProgramPath(),
new
CCFileListener());
m.start();
Servlet servlet
=
new
Servlet();
servlet.service();
MyClassLoader defaultCl
=
new
MyClassLoader();
while
(
true
){
Thread.currentThread().sleep(
3000
);
Thread t
=
new
ServiceThread();
//设置新线程的类加载器
ClassLoader classLoader
= CCFileListener.claMap.get("Servlet.class"
);
if
(classLoader==
null
){
classLoader
=
defaultCl;
}
t.setContextClassLoader(classLoader);
t.start();
}
}
}
public static HashMap<String,ClassLoader> claMap = new HashMap<String, ClassLoader>
();
在监听到文件改变后,依据类名重new一个类加载器,用于加载类。
ClassLoader classLoader = CCFileListener.claMap.get("Servlet.class"
);
if(classLoader==null
){
classLoader =
defaultCl; }
首先获取类名对应的加载器,如果没有使用默认的加载器
ClassLoader classLoader = this
.getContextClassLoader(); Class clazz = classLoader.loadClass("Servlet"
); Method service = clazz.getMethod("service", null
); service.invoke(clazz.newInstance(), null
);
在线程内部使用刚才在外部设置的线程上下文加载器加载新的Servlet,并执行

