This article has participated in the weekend learning program, click the link to see more details :juejin.cn/post/696572…
If the profile
-
Thread-context classloaders, which are the classloaders owned by the currentThread, are available via thread.currentthread ().
-
Thread Context classloaders can be set using the setContextClassLoader() method of java.lang.Thread. If not specified when the Thread is created, it inherits by default from the parent Thread (system ClassLoader).
-
The main context classloader for the main method is sun.misc.Launcher$AppClassLoader.
The parent delegate model of the class loader was described earlier. This model is implemented through the parent property of the class loader, which is handed over by default to the uppermost boot class loader to try to load.
But if you don’t want to follow the parent delegate model class loading mechanism, you can override the loadClass() method of the ClassLoader in addition to the well-known custom ClassLoader. Another option is to use a thread-context class loader to tweak the parent delegate mechanism to reverse load
public class ThreadClassLoaderTest {
public static void main(String[] agrs) throws ClassNotFoundException {
ClassLoader loader = ThreadClassLoaderTest.class.getClassLoader();
System.out.println(loader); // The default is the application class loader
// Get the context classloader:
ClassLoader loader2 = Thread.currentThread().getContextClassLoader();
System.out.println(loader2);// It is also the application class loader by default
// Set to custom class loaders:
Thread.currentThread().setContextClassLoader(
new ClassLoaderTest("d:/"));
System.out.println(Thread.currentThread().getContextClassLoader());
// Use custom class loaders to load:
Class c = Thread.currentThread().getContextClassLoader().loadClass("HelloWorld");
System.out.println(c.getClassLoader());// Thread context class loader
ClassLoader loader3 = String.class.getClassLoader();
System.out.println(loader3);// Start class loader = null}}Copy the code
Test results:
sun.misc.Launcher$AppClassLoader@41dee0d7
sun.misc.Launcher$AppClassLoader@41dee0d7
ClassLoaderTest@516a4aef
ClassLoaderTest@516a4aef
null
Copy the code
Java – SPI mechanisms
Before introducing thread-context class loading, let’s take a look at Java’s SPI mechanism, because SPI is a typical example of our thread-context class loading implementation. Let’s take JDBC’s SPI mechanism as a practical example.
JDBC loading case analysis
Here’s a simple example of how mysql gets a database connection:
public class JDBCTest {
Public static void main(String[] agrs) {try {// Register Driver class.forname (“com.mysql.jdbc.Driver”); String url = “jdbc:mysql://localhost:3306/testdb”; / / through Java library access to the database Connection Connection conn = Java SQL. DriverManager. GetConnection (url, “root”, “123456”); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }}}
Mysql register driver and get connection. In this process, Java implements reverse class loading through a threaded text class loader.
- Class. ForName (” com.mysql.jdbc.driver “);
- The underlying implementation: RegisterDriver () registers the driver instance in the java.sqL.DriverManager class, Driveradd com.mysql.jdbc.driver to the static variable CopyOnWriteArrayList set of java.sql.DriverManager.
. Com. Mysql. JDBC Driver package:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!"); }}}Copy the code
- Register the database driver with java.sql.DriverManager. First, look at the static methods of DriverManager. To be clear, java.sql.DriverManager resides in the rt.jar package directory, where all classes are loaded by the Bootstrap startup class loader.
Java. SQL. DriverManager package:
static {
// Initialize the Driver implementation class:
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers(a) {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run(a) {
return System.getProperty("jdbc.drivers"); }}); }catch (Exception ex) {
drivers = null;
}
// Load the JDBC driver implementation class via SPI:
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
// Use SPI to read classes in meta-INF /services:
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) { driversIterator.next(); }}catch(Throwable t) {}
return null; }}); println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: "+ ex); }}}Copy the code
- Spi concrete implementation:
In the following code, the class loading operation of the java.sql.Driver interface implementation class is accomplished through SPI mode.
Java. SQL. DriverManager package:
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
// Use SPI to read class name in meta-INF /services file:
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) { driversIterator.next(); }}catch(Throwable t) {}
return null; }});Copy the code
After obtaining the ServiceLoader object, iterate through all implementation Class names in the META-INF/services folder before loading the class.forname (“”) Class. Class loading is done in driversiterator.next ().
Java. Util. ServiceLoader package:
public static <S> ServiceLoader<S> load(Class<S> service) {
// Get the thread context class loader:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// Generate a ServiceLoader object:
return ServiceLoader.load(service, cl);
}
public static <S> ServiceLoader<S> load(Class service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = svc;
loader = cl;
reload();
}
Copy the code
When the ServiceLoader object is retrieved, the class loader in the thread context at that time is retrieved, and this load is assigned to a Loader member variable in the ServiceLoader class. In the subsequent class loading process, this type of loading is used to complete. This step breaks the parent delegate model and implements reverse class loading.
try{
while(driversIterator.hasNext()) { driversIterator.next(); }}catch(Throwable t) {}
Copy the code
The driversiterator.next () method internally calls the Class c = class.forname (cn, false, loader) method for Class loading. The loader passed is the thread context classloader, and the CN passed is the implementation class in the meta-INF /services file.