第9章-类加载及执行子系统的案例与实战

本章介绍了4个例子,类加载器(2个)和字节码(2个)。

案例分析

Tomcat:正统的类加载器架构

一个服务器有多个自定义的加载器(如:tomcat、jetty等)。
功能健全的Web服务器要解决的问题:

  1. 同服务器的两个Web应用程序所使用的Java类库可以实现相互隔离
  2. 同服务器的两个Web应用程序所使用的Java类库可以互相共享。
  3. 服务器需要尽可能的保证自身的安全不受部署的Web应用程序影响。
  4. 支持JSP应用的Web服务器,大多数都需要支持HotSwap功能。
    Alt text
    Alt text

OSGi:灵活的类加载架构(网状结构)

Java社区流传观点:学习J2EE规范,去看Jboss源码;学习类加载器,就去看OSGi源码。
最小模块:Bundle
特征:

  1. 依赖关系:从传统的上层模块依赖底层模块转变为平级之间的依赖。
  2. 可见性:只有被Export和Package才可能被外界访问。
  3. 扩展性:可以实现模块级的热插拔功能。
  4. 无关系性:Bundle类加载器之间只有规则,没有固定的委派关系。
  5. 精确访问:一个Bundle类加载器为其他Bundle提供服务时,会根据Export-Package列表严格控制访问范围。
    Alt text

缺点:

  • 额外的复杂度
  • 带来了死锁和内存泄露的风险

字段码生成技术与动态代理的实现

应用场景:

  • Web服务器的JSP编译器
  • 编译时植入的AOP框架
  • 常用的动态代理技术
  • 使用反射的时候虚拟机都有可能会在运行时生成字符码

过程:Alt text

Retrotranslator

跨越JDK版本(Java逆向移植)。
用途:
Alt text

实现原理(略).
JDK升级4类改动:

  • 在编译层面做的改进
  • 对Java API的代码增强
  • 需要在字节码中进行支持的改动
  • 虚拟机内部的改进

实战:自己动手实现远程执行功能

JDK1.6之前:写一个JSP文件上传到服务器,然后通过服务器运行它,或者在服务端程序中加入BeanShell Script等的执行引擎去执行动态脚本。
JDK1.6之后:Compliler API。

目标

在服务端执行临时代码。
Alt text

思路

  1. 如何编译提交到服务器的Java代码?
  • 使用tools.jar包(引入了额外的JAR包,JDK移植要把tools.jar带上)
  • 直接在客户端编译好,把字节码而不是Java代码传到服务端
  1. 如何执行编译之后的Java代码?
  • 让类加载器加载这个类生成一个Class对象,然后反射调用一下某个方法就可以了。(能卸载和回收)
  1. 如何收集Java代码的执行结果?
  • 标准输出System.out(会存在问题)
  • 解决:直接在执行的类中把对System.out的符号引用替换为我们准备的PrintStream的符号引用。

实现

  1. 实现同一类被多次加载(指定HotSwapClassLoader作为父类加载器)
    Alt text
  2. 实现将java.lang.System替换为我们自己定义的HackSystem类的过程
    Alt text
    Alt text

验证(略)