www.jianshu.com/p/60dbd8009…
Source In-depth Understanding of the Java VIRTUAL Machine
The first “break” of the parent delegation model actually happened before the parent delegation model came into being — before JDK1.2 was released. Because the parent delegate model was introduced after JDK1.2, and the class loaders and abstract java.lang.ClassLoader were introduced in JDK1.0, Java designers had to make some compromises when introducing the parent delegate model in the face of the existing implementation code for user-defined class loaders. For forward compatibility, a new proceted method, findClass(), was added in java.lang.classLoader after JDK1.2. Prior to this, users could inherit java.lang.classLoader only to override the loadClass() method. This is because the virtual class calls the loader’s private method loadClassInternal() during class loading, and the only logic for this method is to call its own loadClass(). After JDK1.2, users are no longer encouraged to override the loadClass() method. Instead, they should write their own class loading logic into the findClass() method. In the loadClass() logic, if the parent class loader fails to load, the user will call its own findClass() method to complete the load. This ensures that newly written classloaders conform to the parental delegation model. Parents delegate second “destruction” of the model is the defects of the model itself and, as a result of parents delegation model well solves the base class reunification of each class loader (the base class loading by the top loader) and base class is called the “foundation”, because they always are called as code calls the API. But what if the base class calls the user’s code again? This is not impossible, a typical example is JNDI services, whose code is loaded by the bootloader (put into rt.jar in JDK1.3), but JNDI is all about centralized management and lookup of resources, What if it calls the code of the JNDI Interface Provider (SPI) deployed in the application’s classpath by an independent vendor implementation, but the bootloader cannot “know” some of the code? To solve this dilemma, the Java design team introduced a less elegant design: the Thread Context ClassLoader. This classloader can be set through the setContextClassLoader() method of the java.lang.Thread class. If it has not been set when the Thread is created, it will inherit one from the parent Thread; The class loader is the application class loader by default if it is not set at the global scope of the application. With thread context class loader, JNDI service using the thread context class loader for the need of SPI code, which is the parent class loader requested subclasses loader to finish class loading action, this kind of behavior is actually created parents delegation model hierarchy to reverse the use of class loaders, has violated the parents delegation model, But there is nothing you can do about it. All load actions in Java that involve SPIs, such as JNDI,JDBC,JCE,JAXB, and JBI, take this approach.
The first one is easier and I won’t talk about it here.
For the second, how do you implement it in JDBC
First, understand why JDBC needs to break the parental delegation pattern. The reason is that in native JDBC, the Driver itself is just an interface, and there is no concrete implementation. The concrete implementation is implemented by different database types. For example, mysql-connector- for MySQL
// when the callerCL is empty, it means that the ClassLoader is starting the ClassLoader, but the start ClassLoader does not recognize classes outside rt.jar. This assigns callerCL to thread.currentThread ().getContextClassLoader(); That is, the application launch class
private static Connection getConnection( String url, java.util.Properties info, Class<? >caller) throws SQLException {
/*
* When callerCl is null, we should check the application's * (which is invoking this class indirectly) * classloader, So that the JDBC driver class outside rt.jar * can be loaded from here. CurrentThread ().getContextClassLoader(); ClassLoader = callerCL = Caller! = null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); // Walk through the loaded registeredDrivers attempting to make a connection. // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; for(DriverInfo aDriver : // If the caller does not have permission to load the driver then // skip it if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con ! = null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } // if we got here nobody could connect. if (reason ! = null) { println("getConnection failed: " + reason); throw reason; } println("getConnection: no suitable driver found for "+ url); throw new SQLException("No suitable driver found for "+ url, "08001"); } private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) { boolean result = false; if(driver ! = null) { Class
aClass = null; AClass = class.forname (driver.getClass().getName(), true, classLoader); } catch (Exception ex) { result = false; } result = ( aClass == driver.getClass() ) ? true : false; } return result; }Copy the code
I hope my understanding can help you. If there is any mistake, please correct it.