类加载及执行子系统案例、实战
Tomcat正统的类加载器结构
-
一个完整的web服务器应具备:
(1)部署在同一服务器上的不同web程序使用的java类库可实现隔离
(2)与(1)相反,不同web程序使用的java类库可实现共享,如很多使用spring组织的应用在同一服务器上。
(3)保证服务器自身安全不受web程序影响。
(4)hotswap,热替换
-
综上,部署web程序需要很多classpath。
在/common目录,类库可被tomcat与所有web程序使用。
在/server目录,对Tomcat可见,对web程序不可见。
在/shared目录,对所有web程序可见,对Tomcat不可见。
在/WebApp/WEB-INF目录,对该web程序可见,对其他web及Tomcat不可见。
-
由此,Tomcat采用如下类加载器结构:
启动类加载器bootstrap
<–扩展加载器extension
<–程序加载器Application
<–common加载器<–catalina类加载器(server类加载器)
<–shared加载器<–WebApp加载器<–Jsp类加载器。
-
Tomcat 6之后,为了增加易用性,将/common /server /shared目录合并为一个/lib目录。
以动态代理理解字节码生成技术
-
简单的动态代理代码如下:
package com.sdt; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Base { /** * author: abner */ interface Ihello{ void sayhello(); void sayyes(); } static class Hello implements Ihello{ @Override public void sayhello(){ System.out.println("hello world"); } @Override public void sayyes(){ System.out.println("yes"); } } static class DynamicHello implements InvocationHandler{ private Object object; public Object bind(Object obj){ object=obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method,Object[] args) throws Throwable{ System.out.println("welcome +"); return method.invoke(object,args); } } public static void main(String[] args) { Ihello ihello = (Ihello) new DynamicHello().bind(new Hello()); ihello.sayhello(); ihello.sayyes(); int i = ihello.hashCode(); } }
输出结果:
welcome + hello world welcome + yes welcome +
代码中唯一的黑匣子是
Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
方法,经过层层调用,这个方法产生一个代理类字节码byte数组。经过反编译后可以发现其为ihello对象的每个方法都生成了对应实现并调用了invoke方法。P344.
实战:自己动手实现远程执行功能
-
目标:
不侵入原有程序,不影响原有程序
临时代码不依赖特定的类或接口。
临时代码结果要返回客户端。
-
思路
在客户端编译好后将字节码发送给服务端执行。
让类加载器加载上述字节码并能访问到服务端其他类库。
程序收集标准输出和错误输出的内容,在执行类中把对System.out的符号引用替换为自己的PrintStream。
-
实现
P350