bilibili-深入理解JVM 虚拟机 学习笔记
JVM学习笔记 1
JVM学习笔记 2
JVM学习笔记 3
JVM学习笔记 4
JVM学习笔记 5
JVM学习笔记 6
JVM学习笔记 7
JVM学习笔记 8
P27_线程上下文类加载器分析与实现(27)
当前类加载器
每个类都会使用自己的类加载器(加载当前类的类加载器)去加载自己所依赖
的(且还未加载
的)类。
线程上下文类加载器
(context class loader)是从 JDK 1.2 开始引入的。
类 java.lang.Thread 中的方法 getContextClassLoader() 和 setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器。
如果没有通过 setContextClassLoader(ClassLoader cl) 方法进行设置的话,线程将继承其父线程
的上下文类加载器。
Java 应用运行的初始线程的上下文类加载器是系统类加载器
。在线程中运行的代码可以通过此类加载器来加载类和资源。
父 ClassLoader 可以使用 线程上下文类加载器
加载的类(Thread.currentThread().getContextClassLoader()),这就改变了父加载器不能使用子加载器或没有父子关系的类加载器
所加载的类的情况,即改变了双亲委托模型。
常见的SPI(Service Provider Interface):
- JDBC (Java Database Connectivity )
https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/
https://docs.oracle.com/javase/tutorial/jdbc/basics/index.html
- JCE (Java Cryptography Extension) s
Java加密扩展是Java平台的正式发布的标准扩展,是Java加密体系结构的一部分。JCE提供了用于加密,密钥生成和密钥协商以及消息认证码算法的框架和实现。JCE补充了Java平台,该平台已经包含消息摘要和数字签名的接口和实现。安装特定于所使用的Java平台版本,可下载Java 6,Java 7和Java 8。
https://www.oracle.com/java/technologies/javase-jce8-downloads.html
- JNDI (Java Naming and Directory Interface)
Java命名和目录接口,是Java的一个目录服务应用程序接口,它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。
https://docs.oracle.com/javase/jndi/tutorial/getStarted/overview/index.html
https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
- JBI (Java Business Integration)
Java Business Integration 是在Java Community Process 下开发的规范,用于实现面向服务的体系结构的方法。JCP参考是用于JBI 1.0的JSR 208和用于JBI 2.0的JSR 312。提交者于2010年12月17日将JSR 312从JCP投票过程中删除,但未获得接受。
- JAXP (Java API for XML Processing)
JAXP 是Java XML程序设计的应用程序接口之一,它提供解析和验证XML文档的能力。JAXP是在Java社区进程下开发的,包括JSR 5 和 JSR 63 两个规范。
https://docs.oracle.com/javase/tutorial/jaxp/index.html
https://docs.oracle.com/javase/8/docs/technotes/guides/xml/jaxp/index.html
P28_线程上下文类加载器本质剖析与实做(28)
Tomcat Servlet ClassLoader 不是双亲委托模型
Spring WebAppClassLoader 学习点
P29_ServiceLoader在SPI中的重要作用分析(29)
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
package new_package.jvm.p29;
import java.sql.*;
import java.util.Iterator;
import java.util.ServiceLoader;
public class ServiceLoaderTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
ServiceLoader<Driver> serviceLoader = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
Driver driver = iterator.next();
System.out.println(driver.getClass());
System.out.println(driver.getClass().getClassLoader());
System.out.println("---");
}
System.out.println();
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(ServiceLoader.class.getClassLoader());
}
}
class com.mysql.jdbc.Driver
sun.misc.Launcher$AppClassLoader@18b4aac2
---
class com.mysql.fabric.jdbc.FabricMySQLDriver
sun.misc.Launcher$AppClassLoader@18b4aac2
---
sun.misc.Launcher$AppClassLoader@18b4aac2
null
java.util.ServiceLoader
A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services
. The file’s name is the fully-qualified binary name
of the service’s type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line. Space and tab characters surrounding each name, as well as blank lines, are ignored. The comment character is ‘#’ (’\u0023’, NUMBER SIGN); on each line all characters following the first comment character are ignored. The file must be encoded in UTF-8.
private static final String PREFIX = "META-INF/services/";
// Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
// Private inner class implementing fully-lazy provider lookup
//
private class LazyIterator
implements Iterator<S>
{
// ...
}
private class LazyIterator
implements Iterator<S>
{
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
P30_线程上下文类加载器实战分析与难点剖析(30)
java.sql.DriverManager
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
Class<?> aClass = null;
try {
aClass = Class.forName(driver.getClass().getName(), true, classLoader);
} catch (Exception ex) {
result = false;
}
// 防止命名空间不一致的问题
result = ( aClass == driver.getClass() ) ? true : false;
}
return result;
}
SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。