消息关闭
    暂无新消息!
  • spring ioc中为什么使用classloader,而不是Class.forName

  • 这样使用有什么好处?

  • 这两者有什么本质上的区别?


1个回答

︿ 0

Class.forName 和 ClassLoader.loadClass 的区别

类加载

为了弄清楚 Class.forNameClassLoader.loadClass 的区别, 首先我们需要了解 JVM 中类加载的步骤.
类的加载可以分为如下几步

  • 加载: 通过类的全限定名获取到类的二进制流, 然后加载到 JVM 中

  • 验证: 确保Class 文件的字节流中包含的信息符合虚拟机的要求, 并且不会危害虚拟机的安全

  • 准备: 为类变量分配内存空间并设置类变初始值

  • 解析

  • 初始化: 根据用户指定的代码初始化字段和其他资源, 执行 static 块.

Class.forName

当我们通过:

Class.forName("com.test.MyObj")

来获取一个 Class 时, 那么其实相当于调用了 Class.forName(className, true, currentLoader), 这个方法的第二个参数表示是否需要初始化类. 我们设置为 true, 因此 Class.forName 获取到 Class 对象时, 会自动对类进行初始化的.
并且 Class.forName 加载类的 ClassLoader 和调用 Class.forName 所在的类的 ClassLoader 相同.

ClassLoader.loadClass

Class.forName 不同, 默认情况下 ClassLoader.loadClass 并不会初始化类, 即类加载的 初始化 步骤没有执行, 因此类中的静态代码块不会执行.
并且使用 ClassLoader.loadClass 时, 我们可以指定不同的 ClassLoader. 例如:

ClassLoader.getSystemClassLoader().loadClass("com.test.MyObj");

一个例子

public class MyObj {
    static {
        System.out.println("MyObj class init.");
    }
}
public class Test implements Cloneable, Serializable {
    public static void main(String[] args) throws Exception {
        Class.forName("com.test.MyObj");
        // ClassLoader.getSystemClassLoader().loadClass("com.test.MyObj");
    }
}

那么上面的代码中, Class.forName("com.test.MyObj") 的调用会触发 MyObj 的静态代码块的执行, 而 ClassLoader.getSystemClassLoader().loadClass("com.test.MyObj"); 并不会.

这样使用有什么好处?

我个人猜测, 应该和 Spring IoC 的 Lazy loading 有关, Spring IoC 为了加快初始化速度, 因此大量使用了延时加载技术. 而使用 classloader 不需要执行类中的初始化代码, 可以加快加载速度, 把类的初始化工作留到实际使用到这个类的时候.