学java,感触最深的就是各种机制的思想真的是那种可以直接在实际工作中运用的。
1 classLoader前言
可能是因为公司业务或者使用场景的原因,很少直接用classLoader去load一个class创建实例,更不用说自己去定义一个classLoader去实现类的加载,但从网上各种资料来看,这个机制真的很有用,举个例子,比如搞一个网络动态授权,授权验证算法类放在服务端,每次启动都要动态拿到这个class的字节流,然后解密,然后计算授权正确性,这个就完全可以通过我们自定义的classLoader来实现。
2 classLoader的划分(这个地方的赘述只是自己的总结记录)
一般来说有三种:bootstrap classloader、extension classloader和application classloader,其中extension没有父类,application的父类是extension。我们不能直接使用bootstrap classloader来加载我们的类。
任何事务的划分必须有规则,这里的规则就是待加载class的类型。
我的理解是bootstrap主要是加载jvm的一些核心类,因为我们的java程序运行在jvm上所以bootstrap classloader不是为普通的java程序准备的。
extension classloader是加载jre/ext下的class。
application classloader是加载java应用的class,主要针对classpath。
这个地方对比一些extension和application,相同点都是针对不同位置的class,那么其实我们在开发java应用的时候可能会依赖很多库,一般来说我们都是通过classpath的方式来指定这些依赖的位置,那么为什么我们不用extension?个人理解有两个原因:
(1) ext下放置的应该是针对所有应用的,且不易发生版本变更的扩展。但我们不同的应用依赖可能不同,如果放在ext下会导致ext下大量的jar导致class 加载效率变低,另外我们依赖的第三方的版本往往不会一成不变,但是我们的java环境一般来说会相对稳定。
(2) 双亲委派机制的影响。不知道这是不是一个鸡生蛋,蛋生鸡的问题,反正如果我们从双亲委派的机制来说,application classloader在加载class之前会先让extension尝试加载,只有extension加载失败才会由application继续加载,所以如果我们的一个自定义class和系统class重名,又都放在ext下且由同一个classloader加载,应该会导致冲突。
3 默认情况下如何正确的拿到classLoader
其实我们在实际工作中接触的大部分都是application classloader,获取application classloader一般通过getSystemClassLoader的凡是,但是对于application来说又分很多种类,比如针对web的,getSystemClassLoader返回的只能是AppClassLoader,而对于web或者其他java应用来说有可能我们自己指定了或者应用本身默认的classLoader并不是AppClassLoader,所以就会造成不一致的问题。所以我们应该通过Thread.getContextClassLoader来获取默认的classLoader,确保整个程序上下文中使用的classLoader一致。
4 双亲委派
原理很清楚,class总是由父先尝试load,如果load失败才由子继续load。对于一个class来说除了类型是一种区分标记,对应的classLoader也是,不同的classLoader加载同一种类型得到的class对象也是不同的。
对于我们使用者来说大部分情况下尽可能的遵循双亲委派的原则,除非我们的特殊需求父加载器不能满足,但即使是这样我们也不要尝试去破坏load的整体流程,还是应该先由父去加载,正常找不到的情况下我们通过重写findClass的方式实现class的自定义加载,这样可以尽可能的避免程序的恶意篡改。