一个 Tomcat 程序中是可以运行多个 Web 应用的,如果这两个应用中出现了相同限定名的类,比如 Servlet 类,Tomcat 要保证这两个类都能加载并且它们应该是不同的类。
如果不打破双亲委派机制,当应用类加载器加载 Web 应用 1 中的 MyServlet 之后,Web 应用 2 中相同限定名的 MyServlet 类就无法被加载了。
Tomcat 使用了自定义类加载器来实现应用之间类的隔离,每个应用都会有一个独立的类加载器加载对应的类。
实际上,我们不应该打破双亲委派机制。因此,正确的去实现一个自定义类加载器的方式是重写 findClass 方法,这样不会破坏双亲委派机制
<aside> 💡 自定义类加载器如果不指定父类加载器,默认是 应用程序类加载器:
</aside>
<aside> 💡 两个自定义类加载器加载相同限定名的类不会冲突吗?
</aside>
<aside>
💡 总结:自定义类加载器并重写 loadClass
方法,就可以将双亲委派机制的代码去除
</aside>
JDBC 中使用 DriverManager 来管理项目中引入的不同数据库的驱动
DriverManager 类位于 rt.jar 包中,由启动类加载(可能是安全性考量)
DriverManager 的静态代码块调用 loadInitialDrivers() 方法
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
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);
}
}
}