《深入理解java虚拟机》---类加载案例与实战(9)

一、概述

通过前面几个章节的介绍,我们Class文件以何种文件格式储存,类型何时加载、如何连接,以及虚拟机如何执行字节码指令等都是由虚拟机直接控制的行为,用户程序无法对其进行改变,能通过程序进行操作的,主要是字节码生成与类加载器这两部分的功能。

二、案例分析

1.Tomcat正统的类加载器架构

当前主流的java web服务器主要有Tomcat、Jetty、Weblogic、WebSphere等都实现了自己定义的类加载器,因为一个功能健全的web服务器,要解决以下几个问题

  • 部署在同一个服务器上的两个web应用程序所使用的java类库可以实现相互隔离
  • 部署在同一个服务器上的两个web应用程序所使用的java类库可以相互共享
  • 服务器需要尽可能地保证自身的安全不受部署的web应用程序影响
  • 支持JSP应用的web服务器,大多数都需要支持HotSwap(热部署,无需重启)功能

所以各种web服务器都提供了好几个ClassPath路径供用户存放第三方类库,每一个路径下都有一个相应的自定义类加载器去加载放置在里面的java类库,下图是Tomcat服务器的类加载架构

《《深入理解java虚拟机》---类加载案例与实战(9)》

其实在Tomcat6之后就把之前版本的/commom、/server、/shared三个目录合并成一个/lib目录,这个目录里面的类库相当于以前/commom里面的类库的作用,这是Tomcat设计团队为了简化大多数部署场景所做的一项改进,如果默认设置不能满足要求,用户可以通过修改配置文件指定server.loader和share.loader的方式重新启用Tomcat5.x的加载器结构。

2.OSGi灵活的类加载器架构

OSGi是OSGi联盟制定的一个基于java语言的动态模块化规范,这个规范最初由Sun、IBM、爱立信等公司联合发起的,目的是使服务提供商通过住宅网关为各种家用智能设备提供各种服务,后来这个规范在java的其他技术领域也有相当不错的发展,现在已经成为java世界中事实上的模块化标准,并且已经有了Equinox、Felix等成熟的实现。

OSGi 的优点就是可以实现模块级的热拔插功能,当程序升级更新或者调试除错时,可以只停用、重新安装然后启用程序的其中一部分,这对企业级开发是一个非常有诱惑力的特征,实现的基础主要是归功于他灵活的类加载器架构。OSGi的Bundle类加载器之间没有规则,没有固定的委派关系,发展成一种更为复杂、运行时才能确定的网状结构,但是他也有自己的弊端,可能会发生死锁现象,解决办法就是用户可以启用osgi.classloader.singleThreadLoads参数来按单线程串行化的方式强制进行类加载动作,同时JDK1.7在这方面也做了优化。

3.字节码生成技术与动态代理的实现

public class DyamicProxyTest {
    interface IHello{
        void sayHello();
    }
    static  class Hello implements IHello{

        @Override
        public void sayHello() {
            System.out.println("hello world!");
        }
    }

    static class DynamicProxy implements InvocationHandler{
        Object obj;
        Object bind(Object obj){
            this.obj=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(obj,args);
        }
    }

    public static void main(String[] args) {
        IHello iHello = (IHello) new DynamicProxy().bind(new Hello());
        iHello.sayHello();
    }
}

结果展示:
welcome!
hello world!
Disconnected from the target VM, address: '127.0.0.1:62106', transport: 'socket'

上面的代码就在原始方法执行前打印了一句“welcome!”,其实动态代理可以在原始类和借口未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接关系后,就可以很灵活的重用于不同的应用场景之中了。

4.Retrotranslator:跨越JDK版本

Retrotranslator的作用就是将JDK1.5编译出来的Class文件转变为可以在JDK1.4或者JDK1.3上部署的版本,那么是怎么做到的呢?首先我们来看看JDK每次升级新增的功能大致可以分为以下4类

  • 在编译器层面的改进,如自动拆装箱
  • 对java API的代码增强,如增加java,util.concurrent包
  • 需要在字节码中支持的改动,如动态语言支持
  • 虚拟机内部的改进,增加G1收集器

但是Retrotranslator只能模拟前面两类,后面的两类在虚拟机层面的改动无能为力。

三、实战

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/hy_coming/article/details/82317701
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞