首先我们来看张图,自己就不画了,网上找了张图。
(内存模型就先不看了,1。8之后已经不是这个模型了)
我们java的程序在加载到jvm之前,经历过的事情上面在类加载器之前我就不解释了,在.class文件到类加载器之间,还有一些过程。看下图
加载:io读取磁盘上面的字节码文件,在对象被使用到的时候加载到内存
验证:验证内存中读取字节码文件内容是否正确(里面是很多0-9和字母组成,class文件不要随意去更改)
准备:这个阶段给静态成员分配地址,给默认值(基本类型都有默认值,如果成员中有对象的话,此时为null,即使是你代码中new了)
解析:符号(类名,方法名,包名)这个静态链接过程怎么去解释呢,(这个理解了往后补充)
初始化:把静态变量赋值为我们给的值
我们再看类加载器:
1:启动类加载器:加载jre的lib下的核心库类(rt.jar,charsets.jar等)
2:拓展类加载器:加载jre的lib下的ext拓展文件中的jar包类
3:应用程序类加载器:加载classpath下的类(自己的类)
4:自定义类加载器:加载用户指定的类
先来看下自定义加载器,我们如何去定义一个类加载器:
public class Test { public void helle () { System.out.println("helle_classloader"); } }
定义一个Test类
package com.ghsy.user.test; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } /** * 重写findClass方法 * @param name 是我们这个类的全路径 * @return * @throws ClassNotFoundException */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class log = null; // 获取该class文件字节码数组 byte[] classData = getData(); if (classData != null) { // 将class的字节码数组转换成Class类的实例 log = defineClass(name, classData, 0, classData.length); } return log; } /** * 将class文件转化为字节码数组 * @return */ private byte[] getData() { File file = new File(classPath); if (file.exists()){ FileInputStream in = null; ByteArrayOutputStream out = null; try { in = new FileInputStream(file); out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int size = 0; while ((size = in.read(buffer)) != -1) { out.write(buffer, 0, size); } } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } return out.toByteArray(); }else{ return null; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { MyClassLoader classLoader = new MyClassLoader("C:/test/Test.class"); Class clazz = classLoader.findClass("com.ghsy.user.test.Test"); Object object = clazz.newInstance(); clazz.getMethod("helle",null).invoke(object, null); System.out.println(clazz.getClassLoader().getClass().getName()); } }
自定义一个类加载器MyClassLoader。
我们把target下classes里面的Test.class拷贝到C盘test目录中
控制台输出:
D:workjavajdk1.8.0_60injava.exe "-javaagent:D:workideaIntelliJ IDEA 2018.2.5libidea_rt.jar=53557:D:workideaIntelliJ IDEA 2018.2.5in" -Dfile.encoding=UTF-8 -classpath D:workjavajdk1.8.0_60jrelibcharsets.jar;D:workjavajdk1.8.0_60jrelibdeploy.jar;D:workjavajdk1.8.0_60jrelibextaccess-bridge-64.jar;D:workjavajdk1.8.0_60jrelibextcldrdata.jar;D:workjavajdk1.8.0_60jrelibextdnsns.jar;D:workjavajdk1.8.0_60jrelibextjaccess.jar;D:workjavajdk1.8.0_60jrelibextjfxrt.jar;D:workjavajdk1.8.0_60jrelibextlocaledata.jar;D:workjavajdk1.8.0_60jrelibext
ashorn.jar;D:workjavajdk1.8.0_60jrelibextsunec.jar;D:workjavajdk1.8.0_60jrelibextsunjce_provider.jar;D:workjavajdk1.8.0_60jrelibextsunmscapi.jar;D:workjavajdk1.8.0_60jrelibextsunpkcs11.jar;D:workjavajdk1.8.0_60jrelibextzipfs.jar;D:workjavajdk1.8.0_60jrelibjavaws.jar;D:workjavajdk1.8.0_60jrelibjce.jar;D:workjavajdk1.8.0_60jrelibjfr.jar;D:workjavajdk1.8.0_60jrelibjfxswt.jar;D:workjavajdk1.8.0_60jrelibjsse.jar;D:workjavajdk1.8.0_60jrelibmanagement-agent.jar;D:workjavajdk1.8.0_60jrelibplugin.jar;D:workjavajdk1.8.0_60jrelib
esources.jar;D:workjavajdk1.8.0_60jrelib
t.jar;D:workmycodecloud-parentapp-user argetclasses;D:workjar-resourceorgspringframeworkootspring-boot-starter2.2.1.RELEASEspring-boot-starter-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot2.2.1.RELEASEspring-boot-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-context5.2.1.RELEASEspring-context-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot-autoconfigure2.2.1.RELEASEspring-boot-autoconfigure-2.2.1.RELEASE.jar;D:workjar-resourcejakartaannotationjakarta.annotation-api1.3.5jakarta.annotation-api-1.3.5.jar;D:workjar-resourceorgspringframeworkspring-core5.2.1.RELEASEspring-core-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-jcl5.2.1.RELEASEspring-jcl-5.2.1.RELEASE.jar;D:workjar-resourceorgyamlsnakeyaml1.25snakeyaml-1.25.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter-netflix-eureka-client2.2.3.RELEASEspring-cloud-starter-netflix-eureka-client-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter2.2.3.RELEASEspring-cloud-starter-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-context2.2.3.RELEASEspring-cloud-context-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworksecurityspring-security-crypto5.2.1.RELEASEspring-security-crypto-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-commons2.2.3.RELEASEspring-cloud-commons-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworksecurityspring-security-rsa1.0.9.RELEASEspring-security-rsa-1.0.9.RELEASE.jar;D:workjar-resourceorgouncycastlecpkix-jdk15on1.64cpkix-jdk15on-1.64.jar;D:workjar-resourceorgouncycastlecprov-jdk15on1.64cprov-jdk15on-1.64.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-netflix-hystrix2.2.3.RELEASEspring-cloud-netflix-hystrix-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-aop2.2.1.RELEASEspring-boot-starter-aop-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-netflix-eureka-client2.2.3.RELEASEspring-cloud-netflix-eureka-client-2.2.3.RELEASE.jar;D:workjar-resourcecom
etflixeurekaeureka-client1.9.21eureka-client-1.9.21.jar;D:workjar-resourceorgcodehausjettisonjettison1.3.7jettison-1.3.7.jar;D:workjar-resourcestaxstax-api1.0.1stax-api-1.0.1.jar;D:workjar-resourcecom
etflix
etflix-commons
etflix-eventbus .3.0
etflix-eventbus-0.3.0.jar;D:workjar-resourcecom
etflix
etflix-commons
etflix-infix .3.0
etflix-infix-0.3.0.jar;D:workjar-resourcecommons-jxpathcommons-jxpath1.3commons-jxpath-1.3.jar;D:workjar-resourcejoda-timejoda-time2.10.5joda-time-2.10.5.jar;D:workjar-resourceorgantlrantlr-runtime3.4antlr-runtime-3.4.jar;D:workjar-resourceorgantlrstringtemplate3.2.1stringtemplate-3.2.1.jar;D:workjar-resourceantlrantlr2.7.7antlr-2.7.7.jar;D:workjar-resourcecomgooglecodegsongson2.8.6gson-2.8.6.jar;D:workjar-resourceorgapachecommonscommons-math2.2commons-math-2.2.jar;D:workjar-resourcecom
etflixarchaiusarchaius-core .7.6archaius-core-0.7.6.jar;D:workjar-resourcejavaxws
sjsr311-api1.1.1jsr311-api-1.1.1.jar;D:workjar-resourcecom
etflixservoservo-core .12.21servo-core-0.12.21.jar;D:workjar-resourcecomsunjerseyjersey-core1.19.1jersey-core-1.19.1.jar;D:workjar-resourcecomsunjerseyjersey-client1.19.1jersey-client-1.19.1.jar;D:workjar-resourcecomsunjerseycontribsjersey-apache-client41.19.1jersey-apache-client4-1.19.1.jar;D:workjar-resourceorgapachehttpcomponentshttpclient4.5.10httpclient-4.5.10.jar;D:workjar-resourceorgapachehttpcomponentshttpcore4.4.12httpcore-4.4.12.jar;D:workjar-resourcecommons-codeccommons-codec1.13commons-codec-1.13.jar;D:workjar-resourcecommons-configurationcommons-configuration1.10commons-configuration-1.10.jar;D:workjar-resourcecommons-langcommons-lang2.6commons-lang-2.6.jar;D:workjar-resourcecomgoogleinjectguice4.1.0guice-4.1.0.jar;D:workjar-resourcejavaxinjectjavax.inject1javax.inject-1.jar;D:workjar-resourceaopallianceaopalliance1.0aopalliance-1.0.jar;D:workjar-resourcecomfasterxmljacksoncorejackson-annotations2.10.0jackson-annotations-2.10.0.jar;D:workjar-resourcecomfasterxmljacksoncorejackson-core2.10.0jackson-core-2.10.0.jar;D:workjar-resourcecomfasterxmljacksoncorejackson-databind2.10.0jackson-databind-2.10.0.jar;D:workjar-resourcecomfasterxmlwoodstoxwoodstox-core5.3.0woodstox-core-5.3.0.jar;D:workjar-resourceorgcodehauswoodstoxstax2-api4.2stax2-api-4.2.jar;D:workjar-resourcecom
etflixeurekaeureka-core1.9.21eureka-core-1.9.21.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter-netflix-archaius2.2.3.RELEASEspring-cloud-starter-netflix-archaius-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-netflix-archaius2.2.3.RELEASEspring-cloud-netflix-archaius-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter-netflix-ribbon2.2.3.RELEASEspring-cloud-starter-netflix-ribbon-2.2.3.RELEASE.jar;D:workjar-resourcecom
etflix
ibbon
ibbon2.3.0
ibbon-2.3.0.jar;D:workjar-resourcecom
etflix
ibbon
ibbon-transport2.3.0
ibbon-transport-2.3.0.jar;D:workjar-resourceio
eactivex
xnetty-contexts .4.9
xnetty-contexts-0.4.9.jar;D:workjar-resourceio
eactivex
xnetty-servo .4.9
xnetty-servo-0.4.9.jar;D:workjar-resourceio
eactivex
xnetty .4.9
xnetty-0.4.9.jar;D:workjar-resourcecom
etflix
ibbon
ibbon-core2.3.0
ibbon-core-2.3.0.jar;D:workjar-resourcecom
etflix
ibbon
ibbon-httpclient2.3.0
ibbon-httpclient-2.3.0.jar;D:workjar-resourcecommons-collectionscommons-collections3.2.2commons-collections-3.2.2.jar;D:workjar-resourcecom
etflix
etflix-commons
etflix-commons-util .3.0
etflix-commons-util-0.3.0.jar;D:workjar-resourcecom
etflix
ibbon
ibbon-loadbalancer2.3.0
ibbon-loadbalancer-2.3.0.jar;D:workjar-resourcecom
etflix
etflix-commons
etflix-statistics .1.1
etflix-statistics-0.1.1.jar;D:workjar-resourceio
eactivex
xjava1.3.8
xjava-1.3.8.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter-loadbalancer2.2.3.RELEASEspring-cloud-starter-loadbalancer-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-loadbalancer2.2.3.RELEASEspring-cloud-loadbalancer-2.2.3.RELEASE.jar;D:workjar-resourceioprojectreactor
eactor-core3.3.0.RELEASE
eactor-core-3.3.0.RELEASE.jar;D:workjar-resourceioprojectreactoraddons
eactor-extra3.3.0.RELEASE
eactor-extra-3.3.0.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-cache2.2.1.RELEASEspring-boot-starter-cache-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-context-support5.2.1.RELEASEspring-context-support-5.2.1.RELEASE.jar;D:workjar-resourcecomstoyanrevictor1.0.0evictor-1.0.0.jar;D:workjar-resourcecom
etflix
ibbon
ibbon-eureka2.3.0
ibbon-eureka-2.3.0.jar;D:workjar-resourceorgslf4jslf4j-api1.7.29slf4j-api-1.7.29.jar;D:workjar-resourcecom houghtworksxstreamxstream1.4.11.1xstream-1.4.11.1.jar;D:workjar-resourcexmlpullxmlpull1.1.3.1xmlpull-1.1.3.1.jar;D:workjar-resourcexpp3xpp3_min1.1.4cxpp3_min-1.1.4c.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-actuator2.2.1.RELEASEspring-boot-starter-actuator-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot-actuator-autoconfigure2.2.1.RELEASEspring-boot-actuator-autoconfigure-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot-actuator2.2.1.RELEASEspring-boot-actuator-2.2.1.RELEASE.jar;D:workjar-resourcecomfasterxmljacksondatatypejackson-datatype-jsr3102.10.0jackson-datatype-jsr310-2.10.0.jar;D:workjar-resourceiomicrometermicrometer-core1.3.1micrometer-core-1.3.1.jar;D:workjar-resourceorghdrhistogramHdrHistogram2.1.11HdrHistogram-2.1.11.jar;D:workjar-resourceorglatencyutilsLatencyUtils2.0.3LatencyUtils-2.0.3.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter-netflix-hystrix2.2.3.RELEASEspring-cloud-starter-netflix-hystrix-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-netflix-ribbon2.2.3.RELEASEspring-cloud-netflix-ribbon-2.2.3.RELEASE.jar;D:workjar-resourcecom
etflixhystrixhystrix-core1.5.18hystrix-core-1.5.18.jar;D:workjar-resourcecom
etflixhystrixhystrix-serialization1.5.18hystrix-serialization-1.5.18.jar;D:workjar-resourcecomfasterxmljacksonmodulejackson-module-afterburner2.10.0jackson-module-afterburner-2.10.0.jar;D:workjar-resourcecom
etflixhystrixhystrix-metrics-event-stream1.5.18hystrix-metrics-event-stream-1.5.18.jar;D:workjar-resourcecom
etflixhystrixhystrix-javanica1.5.18hystrix-javanica-1.5.18.jar;D:workjar-resourceorgapachecommonscommons-lang33.9commons-lang3-3.9.jar;D:workjar-resourceorgow2asmasm5.0.4asm-5.0.4.jar;D:workjar-resourceorgaspectjaspectjweaver1.9.4aspectjweaver-1.9.4.jar;D:workjar-resourcecomgoogleguavaguava29.0-androidguava-29.0-android.jar;D:workjar-resourcecomgoogleguavafailureaccess1.0.1failureaccess-1.0.1.jar;D:workjar-resourcecomgoogleguavalistenablefuture9999.0-empty-to-avoid-conflict-with-guavalistenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;D:workjar-resourceorgcheckerframeworkchecker-compat-qual2.5.5checker-compat-qual-2.5.5.jar;D:workjar-resourcecomgoogleerrorproneerror_prone_annotations2.3.4error_prone_annotations-2.3.4.jar;D:workjar-resourcecomgooglej2objcj2objc-annotations1.3j2objc-annotations-1.3.jar;D:workjar-resourceio
eactivex
xjava-reactive-streams1.2.1
xjava-reactive-streams-1.2.1.jar;D:workjar-resourceorg
eactivestreams
eactive-streams1.0.3
eactive-streams-1.0.3.jar;D:workjar-resourceorgprojectlomboklombok1.18.10lombok-1.18.10.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-log4j22.2.1.RELEASEspring-boot-starter-log4j2-2.2.1.RELEASE.jar;D:workjar-resourceorgapachelogginglog4jlog4j-slf4j-impl2.12.1log4j-slf4j-impl-2.12.1.jar;D:workjar-resourceorgapachelogginglog4jlog4j-api2.12.1log4j-api-2.12.1.jar;D:workjar-resourceorgapachelogginglog4jlog4j-core2.12.1log4j-core-2.12.1.jar;D:workjar-resourceorgapachelogginglog4jlog4j-jul2.12.1log4j-jul-2.12.1.jar;D:workjar-resourceorgslf4jjul-to-slf4j1.7.29jul-to-slf4j-1.7.29.jar;D:workmycodecloud-parentapp-user-domain argetclasses;D:workjar-resourceorgmybatisspringootmybatis-spring-boot-starter1.3.2mybatis-spring-boot-starter-1.3.2.jar;D:workjar-resourceorgmybatisspringootmybatis-spring-boot-autoconfigure1.3.2mybatis-spring-boot-autoconfigure-1.3.2.jar;D:workjar-resourceorgmybatismybatis3.4.6mybatis-3.4.6.jar;D:workjar-resourceorgmybatismybatis-spring1.3.2mybatis-spring-1.3.2.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-jdbc2.2.1.RELEASEspring-boot-starter-jdbc-2.2.1.RELEASE.jar;D:workjar-resourcecomzaxxerHikariCP3.4.1HikariCP-3.4.1.jar;D:workjar-resourceorgspringframeworkspring-jdbc5.2.1.RELEASEspring-jdbc-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-tx5.2.1.RELEASEspring-tx-5.2.1.RELEASE.jar;D:workjar-resourceorgmybatisgeneratormybatis-generator-core1.3.7mybatis-generator-core-1.3.7.jar;D:workjar-resourcecomalibabadruid-spring-boot-starter1.1.10druid-spring-boot-starter-1.1.10.jar;D:workjar-resourcecomalibabadruid1.1.10druid-1.1.10.jar;D:workjar-resourcemysqlmysql-connector-java8.0.18mysql-connector-java-8.0.18.jar;D:workmycodecloud-parentcloud-fegin argetclasses;D:workjar-resourceorgspringframeworkcloudspring-cloud-starter-openfeign2.2.3.RELEASEspring-cloud-starter-openfeign-2.2.3.RELEASE.jar;D:workjar-resourceorgspringframeworkcloudspring-cloud-openfeign-core2.2.3.RELEASEspring-cloud-openfeign-core-2.2.3.RELEASE.jar;D:workjar-resourceiogithubopenfeignformfeign-form-spring3.8.0feign-form-spring-3.8.0.jar;D:workjar-resourceiogithubopenfeignformfeign-form3.8.0feign-form-3.8.0.jar;D:workjar-resourcecommons-fileuploadcommons-fileupload1.4commons-fileupload-1.4.jar;D:workjar-resourcecommons-iocommons-io2.2commons-io-2.2.jar;D:workjar-resourceiogithubopenfeignfeign-core10.10.1feign-core-10.10.1.jar;D:workjar-resourceiogithubopenfeignfeign-slf4j10.10.1feign-slf4j-10.10.1.jar;D:workjar-resourceiogithubopenfeignfeign-hystrix10.10.1feign-hystrix-10.10.1.jar;D:workjar-resourceiogithubopenfeignfeign-okhttp10.10.1feign-okhttp-10.10.1.jar;D:workjar-resourcecomsquareupokhttp3okhttp3.14.4okhttp-3.14.4.jar;D:workjar-resourcecomsquareupokiookio1.17.2okio-1.17.2.jar;D:workmycodecloud-parentapp-common argetclasses;D:workjar-resourceorgspringframeworkootspring-boot-starter-web2.2.1.RELEASEspring-boot-starter-web-2.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-json2.2.1.RELEASEspring-boot-starter-json-2.2.1.RELEASE.jar;D:workjar-resourcecomfasterxmljacksondatatypejackson-datatype-jdk82.10.0jackson-datatype-jdk8-2.10.0.jar;D:workjar-resourcecomfasterxmljacksonmodulejackson-module-parameter-names2.10.0jackson-module-parameter-names-2.10.0.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-tomcat2.2.1.RELEASEspring-boot-starter-tomcat-2.2.1.RELEASE.jar;D:workjar-resourceorgapache omcatembed omcat-embed-core9.0.27 omcat-embed-core-9.0.27.jar;D:workjar-resourceorgapache omcatembed omcat-embed-el9.0.27 omcat-embed-el-9.0.27.jar;D:workjar-resourceorgapache omcatembed omcat-embed-websocket9.0.27 omcat-embed-websocket-9.0.27.jar;D:workjar-resourceorgspringframeworkootspring-boot-starter-validation2.2.1.RELEASEspring-boot-starter-validation-2.2.1.RELEASE.jar;D:workjar-resourcejakartavalidationjakarta.validation-api2.0.1jakarta.validation-api-2.0.1.jar;D:workjar-resourceorghibernatevalidatorhibernate-validator6.0.18.Finalhibernate-validator-6.0.18.Final.jar;D:workjar-resourceorgjbossloggingjboss-logging3.4.1.Finaljboss-logging-3.4.1.Final.jar;D:workjar-resourcecomfasterxmlclassmate1.5.1classmate-1.5.1.jar;D:workjar-resourceorgspringframeworkspring-web5.2.1.RELEASEspring-web-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-beans5.2.1.RELEASEspring-beans-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-webmvc5.2.1.RELEASEspring-webmvc-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-aop5.2.1.RELEASEspring-aop-5.2.1.RELEASE.jar;D:workjar-resourceorgspringframeworkspring-expression5.2.1.RELEASEspring-expression-5.2.1.RELEASE.jar com.ghsy.user.test.MyClassLoader
helle_classloader
com.ghsy.user.test.MyClassLoader
Process finished with exit code 0
这个时候ClassLoader是我们自己定义的ClassLoader,不再是由AppClassLoader去加载了。
问题1:如果自定义一个类加载器去加载?
继承ClassLoader,重写里面的findClass方法。
在看下这些类加载器之间的关系:
自己画的一个草图,上面的null,网上由其他资料说是BootstrapClassLoader,我在jdk中并未找到明确的类(无法作证),这个启动类加载器为底层c语言实现(navtice),不需要去特意关注。
这里提一个经常提到的概念:类加载器的双亲委派机制,下面我们来看源码(ClassLoader中的loadclass方法)
我们先来求证一件事情,上图所对应的类加载器关系是不是这样呢?我们看AppClassLoader的源码:
//代码【1】
static class AppClassLoader extends URLClassLoader { final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
//这个方法返回的是一个AppClassLoader public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException { //传入的为ClassLoader实现类(这里为ExtClassLoader,下面会解释) final String var1 = System.getProperty("java.class.path"); final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1); return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() { public Launcher.AppClassLoader run() { URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2); return new Launcher.AppClassLoader(var1x, var0); } }); } AppClassLoader(URL[] var1, ClassLoader var2) { super(var1, var2, Launcher.factory); //调用父类的构造函数(看第代码【3】) this.ucp.initLookupCache(this); } public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException { int var3 = var1.lastIndexOf(46); if (var3 != -1) { SecurityManager var4 = System.getSecurityManager(); if (var4 != null) { var4.checkPackageAccess(var1.substring(0, var3)); } } if (this.ucp.knownToNotExist(var1)) { Class var5 = this.findLoadedClass(var1); if (var5 != null) { if (var2) { this.resolveClass(var5); } return var5; } else { throw new ClassNotFoundException(var1); } } else { return super.loadClass(var1, var2); } } protected PermissionCollection getPermissions(CodeSource var1) { PermissionCollection var2 = super.getPermissions(var1); var2.add(new RuntimePermission("exitVM")); return var2; } private void appendToClassPathForInstrumentation(String var1) { assert Thread.holdsLock(this); super.addURL(Launcher.getFileURL(new File(var1))); } private static AccessControlContext getContext(File[] var0) throws MalformedURLException { PathPermissions var1 = new PathPermissions(var0); ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1); AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2}); return var3; } static { ClassLoader.registerAsParallelCapable(); } }
//这里为LaunCh的构造方法源码(AppClassLoader和ExtClassLoader都为LaunCh类的内部类)
//代码【2】
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader(); //获取了一个ExtClassLoader
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); //传入的是ExtClassLoader(由此证明上面AppClassLoader的getAppClassLoader方法传入的是ExtClassLoader)
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
;
} catch (InstantiationException var6) {
;
} catch (ClassNotFoundException var7) {
;
} catch (ClassCastException var8) {
;
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
//URLClassLoader(AppClassLoader和ExtClassLoader的父类)的构造方法 在点进源码中去看,最终发现URLClassLoader是ClassLoader子类
//代码【3】
public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls, factory);
acc = AccessController.getContext();
}
//SecureClassLoader(URLClassLoader父类)中的构造方法,继续调用父类的构造方法,我们再去ClassLoader中看
//代码【4】
protected SecureClassLoader(ClassLoader parent) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
initialized = true;
}
//ClassLoader
//代码【5】
private ClassLoader(Void unused, ClassLoader parent) { //这里可以看到,AppClassLoader的Parent其实已经是个ExtClssLoader的实例,
//同理,ExtClassLoader都为URLClassLoader子类,这里就做过多赘述
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
//再来看下ExtClassLoader的构造函数和getExtDirs方法,由此可证(ExtClassLoader加载的是ext扩展包中的类)
//代码【6】
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory); //构造函数传入的ClassLoader是null,说明一开始执行的main函数输出String的类加载器为null是正确的,
//网上其他BootStrapClassLoader人云亦云
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs");
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
int var3 = var2.countTokens();
var1 = new File[var3];
for(int var4 = 0; var4 < var3; ++var4) {
var1[var4] = new File(var2.nextToken());
}
} else {
var1 = new File[0];
}
return var1;
}
//最后我们来看下loadclass方发,什么情况下去调用的启动类加载器(null)
代码【7】
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name); //这个位置是ExtClassLoader加载器所进入到的判断(具体为啥往上翻----代码【6】,因为ExtClassLoader构造函数里面parent传入的就是null)
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
//再来看findBootstrapClassOrNull方法
//代码【8】
/**
* Returns a class loaded by the bootstrap class loader; //返回由启动类加载器在载的Class,如果启动类无法加载,则返回null
* or return null if not found.
*/
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
//findBootstrapClass方法为native方法(由c语言实现,不可见)次方法为加载jdk核心jar类的方法
// return null if not found
private native Class<?> findBootstrapClass(String name);
问题1:自定义类加载器,为什么默认的parent会是AppClassLoader呢?我们来看ClassLoader的默认构造函数:
//默认构造函数 protected ClassLoader() { this(checkCreateClassLoader(), getSystemClassLoader()); } //带有参数的构造函数,上面默认的构造函数调用了可以看到,这时候已经传入了parent,我们再来看getSystemClassLoader方法 private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent; if (ParallelLoaders.isRegistered(this.getClass())) { parallelLockMap = new ConcurrentHashMap<>(); package2certs = new ConcurrentHashMap<>(); domains = Collections.synchronizedSet(new HashSet<ProtectionDomain>()); assertionLock = new Object(); } else { // no finer-grained lock; lock on the classloader instance parallelLockMap = null; package2certs = new Hashtable<>(); domains = new HashSet<>(); assertionLock = this; } }
//getSystemClassLoader方法,调用了initSystemClassLoader方法,并且返回了scl(内部变量ClassLoader),接着来看initSystemClassLoader方法
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
//initSystemClassLoader方法
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); //返回了Launcher对象(包含了内部类AppClassLoader和ExtClassLoader),这个方法不过多解释,看一下就知道返回了一个LaunCher内部变量LaunCher
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader(); //我们再看这个getClassLoader方法,返回了内部成员CLassLoader,这个是AppClassLoader(具体为什么请看上面的Launcher类的构造函数,源码第49行)
try {
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// wrap the exception
throw new Error(oops);
}
}
}
sclSet = true;
}
}
至此,默认的parent赋值为AppClassLoader,上面画的个个类加载器关系全部串联起来!!!!!!
答案在上面代码中,因为默认的构造函数给parent初始化为AppClassLoader了。
问题2:类加载器的双亲委派是怎么样实现的?
我们还是结合源码来说:下面看ClassLoader的源码(为什么只看ClassLoader源码不在赘述,因为AppClassLoader和ExtClassLoader都执行的是ClassLoader中的laodClass方法)
protected Class<?> loadClass(String name, boolean resolve) //这是一个套娃过程,等会来张图解释 throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); //校验传入的类名在该类加载器中是否已经加载过了,如果加载过了,直接返回。(如果是父级加载器,也是需要判断是否加载过传入的类) if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { //判断父级类加载器是否为空,不为空则由父级去加载此类(往上委托-----关键来了,父级也是调用次方法,再由父级去执行次方法,到ExtClassLoader这层) c = parent.loadClass(name, false); //这里开始套娃,一个个去调用父加载器,直到启动类加载器(这是个类似于递归调用) } else { c = findBootstrapClassOrNull(name); //会到这里说明已经是启动类加载器了,这个时候调用本地native方法去尝试在jre核心库区加载,如果加载不到,将返回null,在打回到ExtClassLoader,一层一层往下回,直到最底层类加载器找到类为止 } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { //这个位置是父级加载器没有找到类,尝试自己去加载类,这就是为什么我们自定义类加载器需要去重写这个findCLass方法 // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
这就是双亲委派机制
问题3:为什么要使用双亲委派这个机制
1:防止类重复加载(保证相同类只被加载一次),上面源码中已经说明。
2:防止jdk核心jar包类被篡改,增加程序安全(这个看问题4)
问题4:老生常谈的,我自定义一个java.lang.String会不会被加载到jvm里面去
下面有代码来证明吧
抛出异常了,String找不到main方法,明明不是有main方法吗?
类加载机制是一个双亲委派过程,最开始是由启动类加载器去加载,启动类加载器在rt.jar中找到了java.lang.String,所以就加载了rt.jar下的String,我们自定义的String并没有被加载到jvm中。
那么问题来了,我们自定义一个加载器能行吗?
把上面的MyClassLoader改造一下,我们去rt.jar中找到String.class,放入到我们的test中看看呢?
直接就抛出安全异常了,jvm不允许我们去加载以java开头包名下面的类!!!
问题5:如何打破双亲委派机制
这个问题我就不放代码和源码了,回头往上看讲双亲委派的那个地方,用的是loadclass方法,我们只需要自定义类加载器去继承ClassLoader类,重写里面的loadclass方法,
不要去调用parent中的laodclass即可
问题6:什么情况下去打破双亲委派机制?
举个例子说明,我们的tomcat容器,里面由两个应用,一个是spring4版本,一个是spring5版本。如果这个时候还是双亲委派机制,那么加载了其中一个应用的spring版本,
另一个应用spring将不会再去加载(为啥?双亲委派机制会先去校验是否已经被加载过),那么可想而知的后果是非常严重的。后面博客会讲述tomcat相关的类加载
生而为人,我很抱歉