一、Tomcat:正统的类加载器架构
从图中的委派关系中可以看出:
-
CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用。
-
CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。
-
WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
-
JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。
WebAppClassLoader
1.检查我们先前加载的本地类缓存,加载过就不加载了
2.通过javase 的 ClassLoader检查源码是否有重复类,有的话就通过javaseLoader加载
3.通过filter方法判断一些特定的类,如果属于特定的类,就通过父加载器加载
4.剩余的就自己加载,加载失败的通过父类加载(破坏了双亲委派)
如果tomcat的CommonClassLoader想加载 WebAppClassLoader中的类,那么可以使用线程上下文类加载器实现。
二、字节码生成技术与动态代理的实现
使用Proxy.newProxyInstance(类加载器,类,被代理对象)生成代理对象,可使用参数sun.misc.ProxyGenerator.saveGeneratedFiles=true保存代理后的class文件。源码jdk/src/share/classes/sun/misc下的sun.misc.ProxyGenerator。
三、自己动手实现远程执行功能

ByteUtils:主要实现字节数组的操作,如字节数组的的替换。
ClassModifier:实现修改Class字节码,修改常量池的符号引用。
ClassLoader:Java抽象的ClassLoader
HotSwapClassLoader:主要是暴露
ClassLoader的defineClass()。
HackSystem:挟持System,其中out、err替换,其余都转发到System处理。
JavaClassExecuter:执行器,创建ClassModifer修改目标Class byte的System的符号引用为HackSystem,然后使用HotSwapClassLoader加载修改后的类,再使用反射运行该类的main方法,异常信息也输出到
HackSystem,返回HackSystem中的缓存字符串。
代码如下:
public class JavaClassExecuter {
public static String execute(byte[] classByte){
//清理HackSystem中缓存
HackSystem.clearBuffer ();
//创建Class修改器
ClassModifier cm = new ClassModifier (classByte);
//修改System符号引用为HackSystem
byte[] modifyBytes = cm.modifyUTF8Constant ("java/lang/System","javasource/jvm/hotswap/HackSystem");
//创建类加载器
HotSwapClassLoader loader = new HotSwapClassLoader ();
//加载修改后的Class
Class clazz = loader.loadByte (modifyBytes);
try {
//反射调用main方法
Method method = clazz.getMethod ("main",new Class[]{String[].class});
method.invoke (null,new String[]{});
}catch (Exception e){
//将异常也输出到HackSystem中
e.printStackTrace (HackSystem.out);
}
//返回HackSystem中的缓存内容
return HackSystem.getBufferString ();
}
}