天津网站吗,郑州seo外包收费标准,建设网站的公司济南兴田德润o评价,青岛网站建设策划JAVA 双亲委派之一
JVM类加载流程
java语言系统内置了众多类加载器#xff0c;从一定程度上讲#xff0c;只存在两种不同的类加载器#xff1a;一种是启动类加载器#xff0c;此类加载由C实现#xff0c;是JVM的一部分#xff1b;另一种就是所有其他的类加载器#xf…JAVA 双亲委派之一
JVM类加载流程
java语言系统内置了众多类加载器从一定程度上讲只存在两种不同的类加载器一种是启动类加载器此类加载由C实现是JVM的一部分另一种就是所有其他的类加载器这些类加载器均由java实现且全部继承自java.lang.ClassLoader
Bootstrap ClassLoader 启动类加载器最顶层的加载类由C实现负责加载%JAVA_HOME%/lib目录中或-Xbootclasspath中参数指定的路径中的并且是虚拟机识别的按名称类库Extention ClassLoader 扩展类加载器由启动类加载器加载实现为sun.misc.Launcher$ExtClassLoader负责加载目录%JRE_HOME%/lib/ext目录中或-Djava.ext.dirs中参数指定的路径中的jar包和class文件Application ClassLoader 应用类加载器也称为系统类加载器(System ClassLoader可由java.lang.ClassLoader.getSystemClassLoader()获取)实现为sun.misc.Launcher$AppClassLoader由启动类加载器加载负责加载当前应用classpath下的所有类
双亲委派模型
java语言系统有众多类加载器包括用户自定义类加载器各加载器之间的加载顺序如何首先从JVM入口应用sun.misc.Launcher聊起
Launcher
public Launcher() {ExtClassLoader localExtClassLoader;try {// 加载扩展类加载器localExtClassLoader ExtClassLoader.getExtClassLoader();} catch (IOException localIOException1) {throw new InternalError(Could not create extension class loader, localIOException1);}try {// 加载应用类加载器this.loader AppClassLoader.getAppClassLoader(localExtClassLoader);} catch (IOException localIOException2) {throw new InternalError(Could not create application class loader, localIOException2);}// 设置AppClassLoader为线程上下文类加载器Thread.currentThread().setContextClassLoader(this.loader);// ...static class ExtClassLoader extends java.net.URLClassLoaderstatic class AppClassLoader extends java.net.URLClassLoader
}Launcher初始化了ExtClassLoader和AppClassLoader并将AppClassLoader设置为线程上下文类加载器同时初始化AppClassLoader时传入了ExtClassLoader实例WHY? 这里要写一个大大的问号ExtClassLoader和AppClassLoader都继承自URLClassLoader而最终的父类则为ClassLoader。 在这里插入图片描述
查看源码可以得知初始化AppClassLoader时传入的ExtClassLoader实例最终设置为了AppClassLoader(ClassLoader)的parent属性parent属性的作用是什么
父类加载器 每个类都对应一个加载它的类加载器我们可以通过程序来验证
public class ClassLoaderDemo {public static void main(String[] args) {System.out.println(ClassLodarDemos ClassLoader is ClassLoaderDemo.class.getClassLoader());System.out.println(DNSNameServices ClassLoader is DNSNameService.class.getClassLoader());System.out.println(Strings ClassLoader is String.class.getClassLoader());}
}输出为
ClassLodarDemos ClassLoader is sun.misc.Launcher$AppClassLoader18b4aac2
DNSNameServices ClassLoader is sun.misc.Launcher$ExtClassLoader63c12fb0
Strings ClassLoader is nullPathClassLoader为我们自己创建的类其类加载器为AppClassLoader DNSNameService为%JRE_HOME%/lib/ext目录下的类其类加载器为ExtClassLoader String存在于rt.jar中但其类加载器为null这里是应为rt.jar由Bootstrap ClassLoader加载而Bootstrap ClassLoader是由C编写属于JVM的一部分
每个类加载器都有一个父类加载器(parent)同样通过程序验证
public class ClassLoaderDemo {public static void main(String[] args) {System.out.println(PathClassLoaders ClassLoader is PathClassLoader.class.getClassLoader());System.out.println(PathClassLoaders ClassLoader is PathClassLoader.class.getClassLoader().getParent());System.out.println(PathClassLoaders ClassLoader is PathClassLoader.class.getClassLoader().getParent().getParent());}
}输出
PathClassLoaders ClassLoader is sun.misc.Launcher$AppClassLoader18b4aac2
PathClassLoaders ClassLoader is sun.misc.Launcher$ExtClassLoader63c12fb0
PathClassLoaders ClassLoader is nullAppClassLoader的父类加载器为ExtClassLoader ExtClassLoader的父类加载器为null 源码解析
public ExtClassLoader(File[] var1) throws IOException {super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);}// 父类URLClassLoader方法
public URLClassLoader(URL[] urls, ClassLoader parent,URLStreamHandlerFactory factory)super方法中传参就null
null并不代表ExtClassLoader没有父类加载器而是Bootstrap ClassLoader
双亲委派 类加载器在加载类或者其他资源时使用的是如上图所示的双亲委派模型这种模型要求除了顶层的BootStrap ClassLoader外其余的类加载器都应当有自己的父类加载器父类加载器不是父类继承如果一个类加载器收到了类加载请求首先会把这个请求委派给父类加载器加载只有父类加载器无法完成类加载请求时子类加载器才会尝试自己去加载。
要理解双亲委派可以查看ClassLoader.loadClass方法
protected Class? loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 检查是否已经加载过Class? c findLoadedClass(name);if (c null) { // 没有被加载过long t0 System.nanoTime();// 首先委派给父类加载器加载try {if (parent ! null) {c parent.loadClass(name, false);} else {c findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c null) {// 如果父类加载器无法加载才尝试加载long t1 System.nanoTime();c findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}破坏双亲委派模型
双亲委派模型并不是一个强制性的约束模型在java的世界中大部分的类加载器都遵循这个模型但也有例外一个典型的例子便是JNDI服务。
JNDI存放于rt.jar由启动类加载器加载JNDI的目的是对资源进行集中管理和查找它需要调用由各厂商实现并部署在应用程序ClassPath下的JNDI接口实现的代码如此一来在双亲委派模型下启动类加载器根本无法加载这些代码
针对此问题java设计团队引入了线程上下文类加载器Thread Context ClassLoader这个类加载器可以通过Thread类的setContextClassLoader进行设置默认继承父线程类加载器 通过线程上下文类加载器JNDI服务通过此类加载器由父类加载器请求子类加载器完成类加载动作
破坏双亲委派模型的例子还有很多如tomcat服务、osgi、jigsaw等等是否破坏双亲委派模型并没有对与错只是不同场景下的具体应用而已
自定义ClassLoader
不论是AppClassLoader还是ExtClassLoader还是启动类加载器其加载类的路径都是固定的如果我们需要加载外部类或者资源如某路径下或网络上这样便需要自定义类加载器 自定义类加载器只需要继承ClassLoader类复写findClass方法在findClass方法中调用defineClass方法即可 一个ClassLoader创建时如果没有指定parent那么它的parent默认就是AppClassLoader 如果需要制定一个ClassLoader的父类加载器为启动类加载器只需要将其parent指定为null即可 首先编写一个测试用的类文件
package com.lvyuanj.standard.test;public class MyDefinedClass {public void say() {System.out.println(Im MyDefinedClass by this.getClass().getClassLoader());}
}将其编译放入D:\TestXf\classloader目录下接下来
编写MyDefinedClassLoader
package com.lvyuanj.standard.test;import java.net.URL;
import java.net.URLClassLoader;public class MyDefinedClassLoader extends URLClassLoader {public MyDefinedClassLoader(URL[] urls, ClassLoader parent) {super(urls, parent);}public MyDefinedClassLoader(URL[] urls) {super(urls);}
}
这里直接继承URLClassLoader类该类findClass的实现如下
protected Class? findClass(final String name) throws ClassNotFoundException {final Class? result;try {result AccessController.doPrivileged(new PrivilegedExceptionActionClass?() {public Class? run() throws ClassNotFoundException {// 类文件全路径String path name.replace(., /).concat(.class);// 指定资源目录下查找Resource res ucp.getResource(path, false);if (res ! null) {try {// 调用defineClass生成类return defineClass(name, res);} catch (IOException e) {throw new ClassNotFoundException(name, e);}} else {return null;}}}, acc);} catch (java.security.PrivilegedActionException pae) {throw (ClassNotFoundException) pae.getException();}if (result null) {throw new ClassNotFoundException(name);}return result;
}编写测试程序
package com.lvyuanj.standard.test;import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;public class MyDefinedClassLoaderDemo {public static void main(String[] args) throws Exception {URL path new File(D:\\TestXf\\classloader).toURI().toURL();URL[] urls new URL[1];urls[0] path;MyDefinedClassLoader myDefinedClassLoaderA new MyDefinedClassLoader(urls);Class? aClass myDefinedClassLoaderA.loadClass(com.lvyuanj.standard.test.MyDefinedClass);Method sayA aClass.getMethod(say);Object instanceA aClass.newInstance();sayA.invoke(instanceA);System.out.println(aClass.getClassLoader().toString());System.out.println(aClassaClass.hashCode());System.out.println();MyDefinedClassLoader myDefinedClassLoaderB new MyDefinedClassLoader(urls, myDefinedClassLoaderA);Class? bClass myDefinedClassLoaderB.loadClass(com.lvyuanj.standard.test.MyDefinedClass);Method sayB bClass.getMethod(say);Object instanceB bClass.newInstance();sayB.invoke(instanceB);System.out.println(myDefinedClassLoaderB);System.out.println(bClassbClass.hashCode());System.out.println();MyDefinedClassLoader myDefinedClassLoaderC new MyDefinedClassLoader(urls);Class? cClass myDefinedClassLoaderC.loadClass(com.lvyuanj.standard.test.MyDefinedClass);Method sayC cClass.getMethod(say);Object instanceC cClass.newInstance();sayC.invoke(instanceC);System.out.println(myDefinedClassLoaderC);System.out.println(cClasscClass.hashCode());System.out.println();System.out.println(aClass bClass : (aClass bClass));System.out.println(cClass bClass : (cClass bClass));}
}
输出为
Im MyDefinedClass by com.lvyuanj.standard.test.MyDefinedClassLoader619a5dff
com.lvyuanj.standard.test.MyDefinedClassLoader619a5dff
aClass2117255219Im MyDefinedClass by com.lvyuanj.standard.test.MyDefinedClassLoader619a5dff
com.lvyuanj.standard.test.MyDefinedClassLoader7ab2bfe1
bClass2117255219Im MyDefinedClass by com.lvyuanj.standard.test.MyDefinedClassLoader497470ed
com.lvyuanj.standard.test.MyDefinedClassLoader497470ed
cClass186276003aClass bClass :true
cClass bClass :false这里我们定义了3个ClassLoader类搜索路径均为D:\TestXf\classloader其中myDefinedClassLoaderB的父类加载器为myDefinedClassLoaderA aClass bClass cClass 分别由 myDefinedClassLoaderA myDefinedClassLoaderB myDefinedClassLoaderC 加载 instanceA instanceB instanceC 分别由 aClass bClass cClass 创建
从输出可以看出instanceA及instanceB所对应的class均由myDefinedClassLoaderA加载 虽然myDefinedClassLoaderA与myDefinedClassLoaderB为两个不同的类加载器但由于myDefinedClassLoaderB的父类加载器为myDefinedClassLoaderA从输出结果可以看出aClass与bClass完全相同包括内存地址这也说明aClass与bClass均由同一类加载器加载 而instanceC所对应的classcClass却是由myDefinedClassLoaderC加载
这个例子能更好的帮助理解双亲委派模型
**注意如果使用IDEA编辑器的话一定不要创建MyDefinedClass 这个类否则sun.misc.Launcher A p p C l a s s L o a d e r 默认的加载器。自定义 C l a s s L o a d e r 加载器继承 C l a s s L o a d e r 重写 f i n d C l a s s 指定路径加载的类一定不要放到本工程目录对应的包下面否则 f i n d C l a s s 都不会执行如果 l o a d C l a s s 包名 类名 此类会被 s u n . m i s c . L a u n c h e r AppClassLoader默认的加载器。 自定义ClassLoader加载器继承ClassLoader重写findClass 指定路径加载的类一定不要放到本工程目录对应的包下面否则findClass都不会执行如果loadClass包名类名此类会被sun.misc.Launcher AppClassLoader默认的加载器。自定义ClassLoader加载器继承ClassLoader重写findClass指定路径加载的类一定不要放到本工程目录对应的包下面否则findClass都不会执行如果loadClass包名类名此类会被sun.misc.LauncherAppClassLoader默认加载器加载是可以直接找到此类导致自定义的findClass不执行。 使用loadClass(“类名”), 找不到才会再执行findClass方法。正好次方法验证了类加载向父类查找建议需要加载的类使用IDEA 写好之后然后把文件copy到其它目录然后删除本工程下面的的文件 **
类的唯一性
对于任意一个类都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性每一个类加载器都拥有一个独立的类名称空间如上例虽然classA(classB)与classC的类路径相同但由于被不同的类加载器加载其却属于两个不同的类名称空间
手动打包jar
package com.lvyuanj.standard.model;public class StudentA {private String name;private Integer age;private String address;public StudentA() {System.out.println(---------------StudentA constructor-----------);}public String getName() {return name;}public void setName(String name) {this.name name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age age;}public String getAddress() {return address;}public void setAddress(String address) {this.address address;}public void print(){System.out.println(--------------------- excute print method);}
}package com.lvyuanj.standard.model;public class StuMain {public static void main(String[] args) {StudentA studentA new StudentA();studentA.print();}
}手动编码
执行 javac -encoding utf-8 -d classes com/lvyuanj/standard/model/*.java
打jar包
创建MANIFEST.MF文件
Manifest-Version: 1.0
Main-Class: com.lvyuanj.standard.model.StuMain
请务必在文件的最后一行至少加一个回车换行 Manifest-Version 版本号默认为1.0 Main-Class 入口的主类也就是main方法所在的类 Class-Path 引用外部类的路径
2.打包 在该文件夹下执行jar -cvfm stu.jar MANIFEST.MF com jar命令是jdk自带的可在%JAVA_HOME%\bin中找到 c 创建新档案 v 在标准输出中生成详细输出有输出信息 f 指定档案文件名 m 包含指定清单文件中的清单信息也就是指定我们的MANIFEST.MF 如果没有这个参数会生成一个默认的MANIFEST.MF文件 stu.jar 指定生成jar包的名称
MANIFEST.MF com commons-lang3-3.4.jar 要打包的文件 成功之后会在当前文件夹下生成一个stu.jar
运行 执行命令 java -jar stu.jar 输出结果
---------------StudentA constructor-----------
--------------------- excute print method验证ExtClassLoader加载
把以上打包好的stu.jar 包放到 jre/lib/ext 目录下面
public static void main(String args[]) {Class? classz Class.forName(com.lvyuanj.standard.model.StudentA);System.out.println(classload :classz.getClassLoader());
}结果
classload:sun.misc.Launcher$ExtClassLoader63c12fb0