了解线程上下文类加载器、服务器类加载器、OSGI原理
线程上下文类加载器
-
双亲委托机制以及默认类加载器的问题
一般情况下, 保证同一个类中所关联的其他类都是由当前类的类加载器所加载的.。比如,ClassA本身在Ext下找到,那么他里面new出来的一些类也就只能用Ext去查找了(不会低一个级别),所以有些明明App可以找到的,却找不到了。
JDBC API,他有实现的driven部分(mysql/sql server),我们的JDBC API都是由Boot或者Ext来载入的,但是JDBC driver却是由Ext或者App来载入,那么就有可能找不到driver了。在Java领域中,其实只要分成这种Api+SPI(Service Provide Interface,特定厂商提供)的,都会遇到此问题。
常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers 包中。SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。
-
通常当你需要动态加载资源的时候 , 你至少有三个 ClassLoader 可以选择 :
1.系统类加载器或叫作应用类加载器 (system classloader or application classloader)
2.当前类加载器
3.当前线程类加载器 -
当前线程类加载器是为了抛弃双亲委派加载链模式。
每个线程都有一个关联的上下文类加载器。如果你使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文类加载器。如果程序对线程上下文类加载器没有任何改动的话,程序中所有的线程将都使用系统类加载器作为上下文类加载器。 -
Thread.currentThread().getContextClassLoader()
测试线程上下文类加载器:
package 测试类加载全过程;
/**
* 线程上下文类加载器测试
*/
public class Demo05 {
public static void main(String[] args) throws Exception {
ClassLoader loader = Demo05.class.getClassLoader();
System.out.println(loader);
ClassLoader loader2 = Thread.currentThread().getContextClassLoader();
System.out.println(loader2);
Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("f:/MyJava"));
System.out.println(Thread.currentThread().getContextClassLoader());
Class<?> c = Thread.currentThread().getContextClassLoader().loadClass("测试类加载全过程.Demo01");
System.out.println(c);
System.out.println(c.getClassLoader());
}
}
TOMCAT服务器的类加载机制
- 一切都是为了安全!
TOMCAT不能使用系统默认的类加载器。原因如下:
1、如果TOMCAT跑你的WEB项目使用系统的类加载器那是相当危险的,你可以直接无忌惮操作系统的各个目录了
2、对于运行在 Java EE™容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。
3、每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式(不同于前面说的双亲委托机制),所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的但也是为了保证安全,这样核心库就不在查询范围之内。
OSGI原理介绍
- OSGi™(Open Service Gateway Initative)是 面向Java 的动态模块系统。它为开发人员提供了面向服务和基于组件的运行环境,并提供标准的方式用来管理软件的生命周期。
- OSGi 已经被实现和部署在很多产品上,在开源社区也得到了广泛的支持。Eclipse就是基于 OSGi 技术来构建的。
- 原理:
OSGi 中的每个模块(bundle)都包含 Java 包和类。模块可以声明它所依赖的需要导入(import)的其它模块的 Java 包和类(通过 Import-Package),也可以声明导出(export)自己的包和类,供其它模块使用(通过 Export-Package)。也就是说需要能够隐藏和共享一个模块中的某些 Java 包和类。这是通过 OSGi 特有的类加载器机制来实现的。OSGi 中的每个模块都有对应的一个类加载器。它负责加载模块自己包含的Java 包和类。当它需要加载 Java 核心库的类时(以 java开头的包和类),它会代理给父类加载器(通常是启动类加载器)来完成。当它需要加载所导入的 Java 类时,它会代理给导出此 Java 类的模块来完成加载。模块也可以显式的声明某些 Java 包和类,必须由父类加载器来加载。只需要设置系统属性org.osgi.framework.bootdelegation的值即可。