虚拟机7.4 类加载器 及 9.2.2OSGi

来源:互联网 发布:千度云秒赞源码 编辑:程序博客网 时间:2024/06/08 11:12

7.4.1 类与类加载器
比较两个类是否相等的两个条件:同一个类加载器加载、两个类来源于同一个class文件。

public class ClassLoaderTest {    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {        ClassLoader myLoader = new ClassLoader(){            @Override            public Class<?> loadClass(String name) throws ClassNotFoundException{                try{                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";                    java.io.InputStream is = getClass().getResourceAsStream(fileName);                    if(is == null){                        return super.loadClass(name);                    }                    byte[] b = new byte[is.available()];                    is.read(b);                    return defineClass(name,b,0,b.length);                }catch(IOException e){                    throw new ClassNotFoundException(name);                }            }        };        Object obj = myLoader.loadClass("Chapter07.ClassLoaderTest").newInstance();        System.out.println(obj.getClass());        System.out.println(  Chapter07.ClassLoaderTest.class.isInstance(obj));//**返回false**        Object obj2 = ClassLoaderTest.class.newInstance();        System.out.println(  Chapter07.ClassLoaderTest.class.isInstance(obj2));//返回true    }}

上面代码中,虚拟机有两个classLoaderTest类,一个是系统类加载器加载,一个是自定义的myLoader加载,虽然来自同一个class文件,但它们是两个不同的类。
7.4.2双亲委派模型
从虚拟机角度看,只有两种类加载器,一种是启动类加载器(虚拟机自身的一部分);另一种是所有其他的类加载器,都由Java语言实现,独立于虚拟机外部,继承自抽象类java.lang.ClassLoader.
从Java程序员角度看,有一下3中系统提供的类加载器。

  • 启动类加载器(即引导类加载器,Bootstrap ClassLoader):这个加载器会把JAVA_HOME\lib下的或被-Xbootclasspath参数指定路径的,并且是虚拟机识别的类库加载到虚拟机内存中。该加载器无法被用户直接引用,在写自定义类加载器时,如果需要启动类加载器加载某个类,,则使用null代替即可。
  • 扩展类加载器:负责加载JAVA_HOME\lib\ext目录的或者被java.ext.dirs系统变量指定的路径的所有类库,开发者可直接使用扩展类加载器。
  • 应用程序类加载器:这个类加载器是ClassLoader中的getSystemClassLoader()方法返回的,所以也叫系统类加载器。负责加载用户类路径的类库,是程序默认的类加载器。
    类加载器的双亲委派模型
    类加载器双亲委派模型
    工作过程:如有一个加载器收到类加载的委派,它会先把这个委派交给父类,只有父类说无法加载,才会自己尝试加载。
    这个模型可以保证加载的类有优先级,如Object类都是只能由顶端的启动类加载器加载,保证类是同一个。
    具体代码可看java.lang.ClassLoader的loadClass()方法,如调用父类加载器加载类失败,则用自己的findClass()方法。
    7.4.3 破坏双亲委派模型
    Object类这些由启动类加载器加载的我们称它是基础类。基础类一般被用户的代码调用。但也有例外,当基础类想调用用户代码,怎么办呢?
    JNDI是Java的标准服务,它的代码由启动类加载器加载,但JNDI的目的是对资源进行集中管理和查找,它需要调用JNDI接口提供者在classpath下的代码,但穹顶类加载器不认识’子加载器’的代码,怎么办?
    线程上下文类加载器解决。这个类加载器可以通过Thread类的setContextClassLoader()方法进行设置。通过线程上下文类加载器可以让父类加载器请求子类加载器完成类加载的动作。
    破坏双亲委派模型,可以实现代码热替换、达到热部署的效果。
    OSGi实现模块化而部署的关键是它自定义类加载器机制的实现。每一个程序模块(Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉以实现代码的热替换。
    在OSGi环境下,类加载器变成了复杂的网状结构,当收到类加载请求时,按如下顺序进行类搜索:
    父类加载器提供的类(以java.*开头的类以及在委派名单中列明的类);
    导入的Package(Import-Package),委派给export这个类的bundle的类加载器加载;
    查找本Bundle的Classpath,使用自己的类加载器加载(私有Package,Bundle-Classpath);
    查找自己的Fragment Bundle,看是否可以加载 ;
    动态导入的Package(DynamicImport-Package)。

9.2.2 osgi:灵活的类加载器架构
osgi每个bundle和普通Java类库区别不大,但一个bundle可以声明它所依赖的Java package(通过import-package描述),也可以声明它允许导出发布的Java package(通过export-package描述,严格控制导出范围)。一个bundle里只有被export过的package才可能由外界访问,其它package和class将会隐藏起来。除了更精确的模块划分和可见性控制以外,还实现了热插拔,当程序升级时,可以只停用、重新安装然后启用程序的其中一部分,这个作用很大。
热插拔归功于osgi 类加载器之间的规则,没有固定的委派关系。
如果 bundle A声明依赖了某个package(import-package描述),如果有其他bundle B声明导出发布过这个package(export-package描述),则所有对这个package的类加载动作都由bundle B完成。各个bundle之间平时都是平级关系,只有在使用某个package和class时,才根据导入、导出定义来构造bundle之间的委派和依赖。

osgi缺点:依赖出现死锁
当Bundle A依赖Bundle B的package B,而bundle B 依赖bundleA的packageA,则容易出现死锁。
当bundleA 想加载package b时,会先同步锁定A自己的类加载器,把请求委派给bundle B,此时如果Bundle B也想加载package A时,就同样的发生死锁。解决方法:用单线程串行化的方式进行类加载操作。

原创粉丝点击