学习Tomcat动态加载JSP的Class类

时间:2023-12-19 08:42:38

今天在修改项目一个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,并执行 学习Tomcat动态加载JSP的Class类