《Java类加载器以及Websphere的类加载器说明.docx》由会员分享,可在线阅读,更多相关《Java类加载器以及Websphere的类加载器说明.docx(13页珍藏版)》请在金锄头文库上搜索。
1、类加载器说明1 问题来源ESB中读取版本号的信息是从version.properties文件中读取,但是部署在websphere上就是读取不到版本号的信息。采用的方式如下:public static String getVersion() /直接使用Log4j中的Loader来加载 URL versionFileURL = Loader.getResource(version.properties); return doConfigure(versionFileURL).getProperty(version); public static Properties doConfigure(.UR
2、L configURL) log.debug(Reading configuration from URL + configURL); Properties props = new Properties(); InputStream istream = null; URLConnection uConn = null; try uConn = configURL.openConnection(); uConn.setUseCaches(false); istream = uConn.getInputStream(); props.load(istream); catch (Exception
3、e) if (e instanceof InterruptedIOException | e instanceof InterruptedException) Thread.currentThread().interrupt(); log.error(Could not read configuration file from URL + configURL + ., e); log.error(Ignoring configuration file + configURL + .); finally if (istream != null) try istream.close(); catc
4、h (InterruptedIOException ignore) Thread.currentThread().interrupt(); catch (IOException ignore) catch (RuntimeException ignore) return props; 其中的Loader直接使用的是log4j中的类,它查找资源的方式如下:1. 首先使用Thread.currentThread().getContextClassLoader()类加载器去加载资源文件2. 如果没找到就使用当前类的加载器即Loader.class.getClassLoader()去加载资源从打出来的
5、日志信息来看URL的路径信息为:jar:file:/opt/WebSphere/AppServer/java/jre/lib/ext/healthcenter.jar!/version.properties即获取到的资源文件为jre的ext目录下的version.properties文件。为什么会出现这种情况呢?那么就需要了解java的类加载器和websphere的类加载器的原理。2 JVM的类加载器2.1 类加载器的层次结构 类加载器树状组织结构示意图。左右两幅图是等价的 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不
6、继承自 java.lang.ClassLoader。 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。 系统类/应用类加载器(system/Application class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。 自定义类加载器,一般是继承系统类加载器2.2 类加载器
7、的行为方式Java中的类加载器加载某个类是采用父类优先,委托代理的方式进行加载。类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推直到引导类加载器,而如果引导类加载器没有找到,则由扩展类加载器去加载,如果还是没有,则最后由系统类加载器去加载。那么为什么要这么做呢?在介绍之前需要了解JVM中是如何判定两个类是否相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。试图对这两个类的对象进行相
8、互赋值,会抛出运行时异常 ClassCastException。代理模式的设计动机是为了保证 Java 核心库的类型安全,所有 Java 应用都至少需要引用 java.lang.Object类,也就是说在运行的时候,java.lang.Object这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object类,而且这些类之间是不兼容的。通过代理模式,对于Java核心库的类的加载工作由引导类加载器来统一完成,保证了Java应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。2.3 类加载
9、的过程在前面介绍类加载器的代理模式的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处
10、在于:一个类的定义加载器是它引用的其它类的初始加载器。如类 com.example.Outer引用了类 com.example.Inner,则由类 com.example.Outer的定义加载器负责启动类 com.example.Inner的加载过程。方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;方法 defineClass()抛出的是 java.lang.NoClassDefFoundError异常。类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使
11、用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。2.4 JVM类加载器例子public class WhichClassLoader1 public static void main(String args) throws javax.naming.NamingException / Get classpath values String bootClassPath = System.getProperty(sun.boot.class.path); String extClassPath = Syst
12、em.getProperty(java.ext.dirs); String appClassPath = System.getProperty(java.class.path); / Print them out System.out.println(Bootstrap classpath = + bootClassPath + n); System.out.println(Extensions classpath = + extClassPath + n); System.out.println(Application classpath= + appClassPath + n); Whic
13、hClassLoader1 wcl1 = new WhichClassLoader1(); WhichClassLoader2 wcl2 = new WhichClassLoader2(); / Who loaded what? System.out.println(WCL1 was loaded by + wcl1.getClass().getClassLoader(); System.out.println(WCL2 was loaded by + wcl2.getClass().getClassLoader(); wcl2.getTheClass(); public class WhichClassLoader2 / This method is invoked from WhichClassLoader1 public void getTheClass() WhichClassLoader3 wcl3 = new WhichClassLoader3(); System.out.println(WCL3 was loaded by + wcl3.getClass().getClassLoader(); public class WhichClassLoader3 1. 如果上述三个类在应用的路径下,则都会被AppCla