类加载-学习

235 阅读4分钟

类加载器

类加载器父类并不是继承的关系,而是委派关系,是在创建的时候传入其父类。

Launcher

Launcher类是java的入口,在启动java应用的时候会首先创建Launcher类,由BootStrapClassLoader负责加载。

/**
 * BootStrapClassLoader会加载Launcher类;
 * 可以说 ExtClassLoader、AppClassLoader 是由BootStrapClassLoader加载的;
 */
public class Launcher {
  
  // 静态属性是创建Launcher,所以会执行Launcher()构造器
  private static Launcher launcher = new Launcher();
​
  public Launcher() {
      Launcher.ExtClassLoader var1;
      try {
        // 创建ext类加载器,父类为null(BootStrapClassLoader),ClassLoader#loadClass父类为空时调用     #findBootstrapClassOrNull
          var1 = Launcher.ExtClassLoader.getExtClassLoader();
      } catch (IOException var10) {
          throw new InternalError("Could not create extension class loader", var10);
      }
​
      try {
         // 创建app类加载器,父类为刚创建的ExtClassLoader
          this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
      } catch (IOException var9) {
          throw new InternalError("Could not create application class loader", var9);
      }
      // 
      Thread.currentThread().setContextClassLoader(this.loader);
​
      。。。
​
  }
}

BootStrapLoader

加载的内容:javahome/jre/lib下的部分jar;

ExtClassLoader

Launcher中的内部类,继承URLClassLoader -> SecureClassLoader ->ClassLoader;

加载的内容:javahome/jre/lib/ext下的部分jar

AppClassLoader

系统类加载器与应用加载器;

Launcher中的内部类,继承URLClassLoader -> SecureClassLoader ->ClassLoader;

加载的内容:

System.getProperty("java.class.path")
​
1. 加载 javahome/jre/lib下的jar
2. 加载 javahome/jre/lib/ext下的jar
3. 项目对应的/target/classes
4. 加载.m2下的jar包,项目引入的三方jar
#loadClass
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46);
    if (var3 != -1) {
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3));
        }
    }
    // 默认是false,除非设置启动参数lookupCacheEnabled为true
    if (this.ucp.knownToNotExist(var1)) {
        Class var5 = this.findLoadedClass(var1);
        if (var5 != null) {
            if (var2) {
                this.resolveClass(var5);
            }
​
            return var5;
        } else {
            throw new ClassNotFoundException(var1);
        }
    } else {
        return super.loadClass(var1, var2);
    }
}

URLClassLoader

  • 可以指定一串路径,然后再些路径下面寻找将要加载的类。可以动态加载程序外的类
  • 是ExtClassLoader、AppClassLoader的父类

实际使用demo:cloud.tencent.com/developer/a…

ContentClassLoader

Thread.currentThread().getContextClassLoader()的意义: 父Classloader可以使用当前线程Thread.currentthread().getContextLoader()中指定的classloader中加载的类。颠覆了父ClassLoader不能使用子Classloader或者是其它没有直接父子关系的Classloader中加载的类这种情况。这个就是Context Class Loader的意义。

原文链接:blog.csdn.net/qbg19881206…

ClassLoader

  • java类加载器的基类
#构造器
ClassLoader构造器:
​
// 无参构造函数默认是AppClassLoader,子类super()即可调用
protected ClassLoader() {
    this(checkCreateClassLoader(), getSystemClassLoader());
}
​
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}
​
private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
    if (ParallelLoaders.isRegistered(this.getClass())) {
        parallelLockMap = new ConcurrentHashMap<>();
        package2certs = new ConcurrentHashMap<>();
        assertionLock = new Object();
    } else {
        // no finer-grained lock; lock on the classloader instance
        parallelLockMap = null;
        package2certs = new Hashtable<>();
        assertionLock = this;
    }
}
​
​
#loadClass
/**
 *
 * 1. 先对className加锁,synchronized
 * 2. 查看是否已经加载过
 * 3. 递归调用父类#loadClass  
 * 4. 调用子类#findClass
 */
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
       // 加锁
        synchronized (getClassLoadingLock(name)) {
            // 查看class是否已经加载过
            Class<?> c = findLoadedClass(name);
           // 没加载过
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 有父类:调用父类#loadClass,没有父类:调用BootStrap
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }
                // 父类未也未找到class,调用#findClass
                if (c == null) {
             
                    long t1 = System.nanoTime();
                    c = findClass(name);
​
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
           // 解析class文件,就是将符号引用替换为直接引用的过程
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
 }
​
​
/**
 *
 * 1. 不推荐使用String、Integer作为锁对象,有缓存区的存在。
 * 2. 1.7之后,加锁的时候如果加载器具备并行能力,那么就不再对类加载器进行加锁,而是找到加载类文件对应的Object锁进行加锁操作。  
 */
protected Object getClassLoadingLock(String className) {
   // 默认使用当前类加载器做为锁对象
    Object lock = this;
    // 判断平行的锁Map是否存在,存在则获取className对应的锁对象
    if (parallelLockMap != null) {
        Object newLock = new Object();
        lock = parallelLockMap.putIfAbsent(className, newLock);
        if (lock == null) {
            lock = newLock;
        }
    }
    return lock;
}
#findClass
// 父类未加载到class时,子类再进行查找,子类未实现时抛出ClassNotFoundException异常
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}
​

URLClassLoader#findClass

protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                      // 将.替换成/,拼接.class,再进行加载
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                              // 将字节码转成Class对象
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }
#defineClass

将字节码转成Class对象

protected final Class<?> defineClass(String name, byte[] b, int off, int len,
  ProtectionDomain protectionDomain) throws ClassFormatError{
    protectionDomain = preDefineClass(name, protectionDomain);
    String source = defineClassSourceLocation(protectionDomain);
    // 调用native方法生成class对象
    Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
    postDefineClass(c, protectionDomain);
    return c;
}
​

ContentClassLoader

www.jianshu.com/p/a4dc75565…

其它知识

  • 父类没有无参构造器,子类需要在构造器中通过#super调用父类的有参数构造器

  • new子类,并不会new一个父类,只会执行父类的构造器,实例化父类的属性

    比如: Aab的父类,A中有name的属性。
    创建a时,name设置为a
    创建b时,name设置为b
    这时候打印a.name为:ab.nameb,并不会相互影响
    
  • jdk包,rt.jar是java继承类库,其中包括java、javax、sun等类包

    • java:java SE的标准库,我们程序中可以调用
    • javax:标准库的扩展
    • sun:是sun的hotspot虚拟机中java.* 和javax.*的实现类。因为包含在rt中,所以我们也可以调用。

问题

打破了双亲委派的例子

blog.csdn.net/qq_38182963…

jdbc 如何利用ContentClassLoader
Launcher启动类都做了什么