【问题标题】:How does Class.forName() work?Class.forName() 是如何工作的?
【发布时间】:2011-05-11 06:38:13
【问题描述】:

我刚刚了解了java.sql package。它使用Class.forName() 动态加载扩展DriverManager 的驱动程序。 然后我们使用DriverManager.getConnection()方法获得连接。

那么整个事情是如何运作的呢?
DriverManager 类如何知道如何在不使用实际驱动程序的类名的情况下获取连接。

我们也可以将 Class.forName() 用于自定义应用程序...如果用一个例子来解释,我会很高兴。

【问题讨论】:

标签: java jdbc


【解决方案1】:

Class.forName 只是加载一个类,包括运行它的静态初始化器,如下所示:

class Foo {
    static {
        System.out.println("Foo initializing");
    }
}

public class Test {
    public static void main(String [] args) throws Exception {
        Class.forName("Foo");
    }
}

您所讨论的所有其余过程都是特定于 JDBC 的。驱动程序——它实现了Driver,它没有扩展DriverManager——只是使用DriverManager.registerDriver注册一个适当的实例。然后,当DriverManager 需要为特定的连接字符串查找驱动程序时,它会依次在每个已注册的驱动程序上调用connect,直到一个成功并返回一个非空连接。

请注意,这种注册驱动程序的方式相当过时 - 查看DriverManager 的文档,了解获取数据源的更现代方式。

【讨论】:

  • 我的查询是从第一行解决的,您回答先生。
  • @Jon 很好的例子,但是Class.forName("com.mysql.jdbc.Driver");com.mysql.jdbc.Driver 类在哪里,因为我尝试import com.mysql.jdbc.Driver 仍然无法正常工作
  • @UnKnown:它在 MySQL jar 文件中。但是你不需要导入它——你通常让 JDBC 直接初始化它,你只在代码中引用 JDBC 类。
  • @PhilipRego:我刚刚下载了mysql-connector-java-5.1.42-bin.jarcom.mysql.jdc.Driver 课程就在里面。
  • DriverManager不调用acceptsURL,只是调用connect,最先成功(不返回null或抛出异常)的是返回的连接。
【解决方案2】:

当我们使用 new 操作符创建一个类的实例时,它会做两件事

  1. 将类加载到内存中,如果没有加载 - 这意味着从 .class 文件创建类的内存表示,以便可以从中创建实例。这包括初始化静态变量(解析该类)
  2. 创建该类的实例并存储对变量的引用。

Class.forName 只做第一件事。 它将类加载到内存中,并将该引用作为 Class 的实例返回。如果我们想创建一个实例,我们可以调用该类的 newInstance 方法。这将调用默认构造函数(无参数构造函数)。 请注意,如果默认构造函数不可访问,则 newInstance 方法将抛出 IllegalAccessException。如果该类是一个抽象类或接口,或者它没有默认构造函数,那么它将抛出一个InstantiationException。如果在解析该类期间出现任何异常,它将抛出 ExceptionInInitializerError

如果没有定义默认构造函数,那么我们必须使用反射API调用defiend构造函数。

但是 Class.forName 的主要优点是,它可以接受类名作为字符串参数。所以我们可以动态传递类名。但是如果我们使用new操作符创建一个类的实例,类名就不能动态改变了。

Class.forName() 反过来会调用调用者 ClassLoader 的 loadClass 方法(调用 Class.forName 的类的 ClassLoder)。

默认情况下,Class.forName() 解析该类。这意味着,初始化该类中的所有静态变量。 同样可以使用Class.forName(String name,boolean initialize,ClassLoader loader)的重载方法来改变

使用Class.forName()加载jdbc驱动的主要原因是驱动可以动态变化。 在静态块中,所有驱动程序将创建自己的一个实例,并使用 DriverManager.registerDriver() 方法向 DriverManager 注册该类。由于Class.forName(String className) 默认解析类,它将初始化静态初始化程序。 所以当我们打电话给Class.forName("com.sun.jdbc.odbc.JdbcOdbcDriver")时, Driver 类将被加载、实例化并注册到 DriverManager

因此,如果您使用 new Operator,则必须执行以下操作。
代码:

Driver drv = new com.sun.jdbc.odbc.JdbcOdbcDriver();
DriverManager.registerDriver(drv);

【讨论】:

  • 这是一个很棒的描述!感谢您提到 newInstance,我很困惑是使用构造函数实例化该类还是仅实例化静态成员。所以基本上使用 forName 我们只实例化静态成员,然后代码中某处的管理器正在使用 newInstance 并开始使用它,对吗?
【解决方案3】:

Class.forName(..) 加载并初始化目标类。这反过来意味着调用了静态初始化程序块(在static { .. } 中定义的代码。

例如,如果您查看 MySQL 的驱动程序,则在该静态块中驱动程序正在注册自己:DriverManager.registerDriver(new Driver());

如果您可以“负担”对 MySQL 驱动程序的编译时依赖性,则可以省略 Class.forName(..) 并自行注册驱动程序。

也就是说,使用Class.forName(..) 来初始化应用程序中的类很少有相关性,因为编译时依赖在那里不是问题。

还要注意Class.forName(..) is no longer required 用于自版本 4 以来的 JDBC。通过使用 service provider 机制,您可以指示驱动程序管理器通过系统属性加载什么。

【讨论】:

    【解决方案4】:

    Class.forName() 之所以在 SQL 示例中经常被提及,是因为没有魔法可以告诉 JDBC DriverManager如何 将提供的 JDBC URL 映射到真正的驱动程序。

    例如“mysql”应该映射到给定的 MySQL 类,“thin”映射到 Oracle 类,“as400”映射到 DB2/400 类。

    通过显式加载类,这允许类中向 DriverManager 注册自身的代码运行。

    现在出现了魔术钩子,允许 JVM 自动发现驱动程序(如果足够新的话),所以调用是多余的,但出于习惯,许多人仍然使用它。

    【讨论】:

      猜你喜欢
      • 2015-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-01
      • 2011-06-06
      • 1970-01-01
      • 2018-02-19
      相关资源
      最近更新 更多