一.好处

保证了Java程序的稳定运行,可以避免类的重复加载。保证了Java的核心API不被篡改。

二.源码

  1. public Class<?> loadClass(String name) throws ClassNotFoundException {
  2. return loadClass(name, false);
  3. }
  4. protected Class<?> loadClass(String name, boolean resolve)
  5. throws ClassNotFoundException
  6. {
  7. synchronized (getClassLoadingLock(name)) {
  8. // 检查是否已经加载过
  9. Class<?> c = findLoadedClass(name);
  10. if (c == null) {
  11. try {
  12. // 存在父加载器,交给父加载器加载
  13. if (parent != null) {
  14. c = parent.loadClass(name, false);
  15. } else {
  16. // Bootstrap类加载器加载
  17. c = findBootstrapClassOrNull(name);
  18. }
  19. } catch (ClassNotFoundException e) {
  20. }
  21. // 父加载器无法加载,自己加载
  22. if (c == null) {
  23. c = findClass(name);
  24. }
  25. }
  26. return c;
  27. }
  28. }

三.如何打破双亲委派机制

1.自定义类加载器,重写loadClass方法。
2.线程上下文类加载器。

  1. public class Test {
  2. public static void main(String[] args) throws SQLException {
  3. Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
  4. Statement statement = connection.createStatement();
  5. ResultSet resultSet = statement.executeQuery("select * from t1");
  6. while(resultSet.next()) {
  7. System.out.println(resultSet.getString("name"));
  8. }
  9. }
  10. }

不用写Class.forName加载驱动类。
DriverManager.getConnection触发DriverManager的加载,类加载器是启动类加载器。

  1. public class DriverManager {
  2. static {
  3. loadInitialDrivers();
  4. }
  5. private static void loadInitialDrivers() {
  6. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  7. public Void run() {
  8. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
  9. Iterator<Driver> driversIterator = loadedDrivers.iterator();
  10. try{
  11. while(driversIterator.hasNext()) {
  12. // 加载驱动类
  13. driversIterator.next();
  14. }
  15. } catch(Throwable t) {
  16. }
  17. return null;
  18. }
  19. });
  20. }
  21. }

ServiceLoader.load

  1. public static <S> ServiceLoader<S> load(Class<S> service) {
  2. // AppClassLoader
  3. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  4. return ServiceLoader.load(service, cl);
  5. }
  6. public static <S> ServiceLoader<S> load(Class<S> service,
  7. ClassLoader loader)
  8. {
  9. return new ServiceLoader<>(service, loader);
  10. }

加载驱动类

  1. private S nextService() {
  2. Class<?> c = null;
  3. try {
  4. // cn:com.mysql.cj.jdbc.Driver(META-INF/services/java.sql.Driver)
  5. // loader:AppClassLoader
  6. c = Class.forName(cn, false, loader);
  7. } catch (ClassNotFoundException x) {
  8. }
  9. }

四.tomcat类加载器

Tomcat允许同时运行多个Web程序,每个Web程序依赖的类必须是相互隔离的,因为可能会依赖同一个第三方类库的不同版本。使用WebappClassLoader加载应用程序中的Class,只对当前webapp可见,对Tomcat和其他Web应用程序不可见。
截屏2022-01-21 下午5.53.58.png
Common类加载器,负责加载Tomcat和Web应用都复用的类。
Catalina类加载器,负责加载Tomcat专用的类,而这些被加载的类在Web应用中将不可见。
Shared类加载器,负责加载Tomcat下所有的Web应用程序都复用的类,而这些被加载的类在Tomcat中将不可见。
commonLoader,catalinaLoader,sharedLoader 现在是同一个对象(URLClassLoader)。