One . SPI In the role
Come to the conclusion first
-
DriverManager.getConnection() Used internally SPI Mechanism , scanning mysql Of jar Bag
META-INF/services/
Get the full pathname and useClass.forName(cn, false, loader),c.newInstance()
Load target driver . -
On the other hand , It also solves why not use Class.forName() You can also break parental delegation , because getConnection It’s encapsulated inside
Class.forName(cn, false, loader);
-
false
Express Loading bytecode returns class object . But it doesn’t initialize , It doesn’t trigger static Code block -
c.newInstance
The object must be instantiated , Trigger static Code block
Preface
The following concepts extend to Effective java page 6 ( Chinese version )
-
SPI (Service Provider Interface), Interface used to expand engineering examples
-
about JDBC ,Connection It’s part of its service interface
1.1 The service provider is for JDK Interface Driver
Service Interface
JDK Provides standards , Specific database drivers are provided by major database manufacturers
1.2 Provider Registration API registerDriver(new Driver)
Provider Registration API
JDK For registration API, Registered API Would call new Driver()
Driver There must be a null parameter constructor , For support Class.forName().newInstance()
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
1.3 The user invokes the API getConnection(url)
Provider access API
Code used by developers
@CallerSensitive
public static Connection getConnection(String url)
throws SQLException {
java.util.Properties info = new java.util.Properties();
return (getConnection(url, info, Reflection.getCallerClass()));
}
Two . DriverManager join SPI
2.1. DriverManager getConnection The underlying source code
private static Connection getConnection(
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
// register SPI All the drivers in the directory
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
return (con);
}
}
}
}
}
}
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
Class<?> aClass
= Class.forName(driver.getClass().getName(), true, classLoader);
result = ( aClass == driver.getClass() ) ? true : false;
}
return result;
}
Most called Class.forName
2.2 DriverManager Use Class.forName
About getContextClassLoader, Click on this link , Pull to the bottom of the article
Thread.currentThread().getContextClassLoader(); // What you get is AppClassLoader
Loading the core class DriverManager after , You can use AppClassLoader load SPI Implementation class
Class.forName(driver.getClass().getName(), true, classLoader);
Class.forName It uses getContextClassLoader() Medium AppClassLoader load
2.3 DriverManager.getConnection Trigger static methods
For the first time getConnection(String url)
Will trigger DriverManager Of static Method
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
2.4 DriverManager.loadInitialDrivers()
ServiceLoader<Driver>
Namely SPI Is executing container
private static void loadInitialDrivers() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
while(driversIterator.hasNext()) {
driversIterator.next(); // next() The method is SPI The ultimate realization of
}
return null;
}
});
}
3、 … and . SPI Final realization
Physical support
public final class ServiceLoader<S>implements Iterable<S>{
private static final String PREFIX = "META-INF/services/";
}
ServiceLoader.nextService() and com.mysql.jdbc.Driver Static method of
ServiceLoader.nextService
Used to traverse all SPI(Iterable.next Encapsulates the method )
private S nextService() {
String cn = nextName;//cn yes SPI All implementers are limited to their names
nextName = null;
Class<?> c = null;
try {
// Loading bytecode returns class object . But it doesn't initialize , It doesn't trigger static Code block
c = Class.forName(cn, false, loader);
}
try {
// cast Not the main business logic . c.newInstance Triggered com.mysql.jdbc.Driver Static method of
// The static method completes Driver Instantiation and registration of
S p = service.cast(c.newInstance());
providers.put(cn, p);// Local cache ( All names are limited , Implementation class object )
return p;
}
}
Driver The static method of is triggered , Completion will SPI The implementation class is registered to DriverManager
Because it was c.newInstance
The trigger , there new Driver() Has been replaced by SPI Implementation class , Registration completed
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
Four . carding getConnection(url) The registration logic of
DriverManager.getConnection(url) // How to get the drive
-
Step one , Thread on , Parent delegation assigns core class loading ,DriverManager When not loaded
- Bootstrap ClassLoader load
- Launcher load AppClassLoader、ExtClassLoader load
- Load the ** Threads pass through setContextClassLoader(this.loader)** hold AppClassLoader Set into context
public class Launcher { static class AppClassLoader extends URLClassLoader { } static class ExtClassLoader extends URLClassLoader { } public Launcher() { try { this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } // hold AppClassLoader Class loader set as the context of this thread Thread.currentThread().setContextClassLoader(this.loader); } }
-
Step two , The thread continues to execute ,DriverManager Be loaded , Trigger loadInitialDrivers(), Direct trigger SPI Read tool class instantiation
static { loadInitialDrivers(); // Internal use is ServiceLoader<Driver> Of api println("JDBC DriverManager initialized"); }
// loadInitialDrivers(); Internal initialization ServiceLoader ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
-
Step three ,ServiceLoader< Driver> initialization , Take along AppClassLoader
public static <S> ServiceLoader<S> load(Class<S> service) { // What you get is 【 Step one 】 Set in the AppClassLoader 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); } // Set in AppClassLoader Assign a value to loader private ServiceLoader(Class<S> svc, ClassLoader cl) { loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; }
-
Step four ,ServiceLoader< Driver> Scan directory , initialization SPI Implementation class
public final class ServiceLoader<S>implements Iterable<S>{ // Specification catalog ,MySQL Driven Jar There must be this file under the package , Inside is SPI The full pathname of the implementation class private static final String PREFIX = "META-INF/services/"; private S nextService() { String cn = nextName;//SPI All implementers are limited to their names nextName = null; Class<?> c = null; try { // Loading bytecode returns class object . But it doesn't initialize , It doesn't trigger static Code block c = Class.forName(cn, false, loader); } try { // cast Not the main business logic . c.newInstance Triggered Driver Static method of S p = service.cast(c.newInstance()); providers.put(cn, p);// Local cache ( All names are limited , Implementation class object ) return p; } } }
-
Step five ,SPI Implementation class com.mysql.jdbc.Driver After the initialization , use static call DriverManager Registration drives itself , It’s registration com.mysql.jdbc.Driver complete
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
5、 … and . Sum up , One sentence summary
-
DriverManager.getConnection() Used internally SPI Mechanism , scanning mysql Of jar Bag
META-INF/services/
Get the full pathname and useClass.forName(cn, false, loader),c.newInstance()
Load target driver . -
On the other hand , It also solves why not use Class.forName() You can also break parental delegation , because getConnection It’s encapsulated inside
Class.forName(cn, false, loader);