Java 动态加载JDBC驱动程序

Java 动态加载JDBC驱动程序,java,jdbc,classloader,Java,Jdbc,Classloader,我尝试用以下代码动态加载JDBC驱动程序: try{ URL[] url={new URL("file:libs/mysql-connector-java-5.1.21.jar")}; URLClassLoader loader = new URLClassLoader(url, System.class.getClassLoader()); loader.loadClass(drivername); Enumera

我尝试用以下代码动态加载JDBC驱动程序:

        try{
        URL[] url={new URL("file:libs/mysql-connector-java-5.1.21.jar")};
        URLClassLoader loader = new URLClassLoader(url, System.class.getClassLoader());
        loader.loadClass(drivername);
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()){
            Driver driver = drivers.nextElement();
            System.out.println("driver:"+driver);
        }
        Class.forName(drivername, true, loader);
        drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()){
            Driver driver = drivers.nextElement();
            System.out.println("driver:"+driver);
        }
        Connection connect = DriverManager.getConnection(jdbcurl, user,
                password);

        return connect;
    }
    catch (MalformedURLException e){
        e.printStackTrace();
        return null;
    }
第二个循环显示相同的驱动程序,但没有MySQL驱动程序

我的问题是为什么?我错过什么了吗

我了解到,如果驱动程序已加载,则每个驱动程序都会尝试通过drivermanager注册自己。在我的代码中,这应该是loader.loadClass(drivername)。我认为这段代码应该调用静态部分,例如:

static {
  try {
      java.sql.DriverManager.registerDriver(new Driver());
  } catch (SQLException E) {
      throw new RuntimeException("Can't register driver!");
  }
}

当然。

您不能这样做,因为不允许您使用调用代码无权访问的驱动程序(即,由不同类加载器加载的驱动程序):

调用方法
getConnection
时,
DriverManager
将尝试从初始化时加载的驱动程序和使用与当前小程序或应用程序相同的类加载器显式加载的驱动程序中找到合适的驱动程序

据我所知,唯一可能的解决方法是手动实例化
驱动程序
,而不是使用
驱动程序管理器
(假设它有一个无参数构造函数):


尽管我不确定这是一种正确的方法。

这是DriverManager和类加载器的一个已知问题,请参阅:

驱动程序定义(基本上是代理):

使用示例:

URL u = new URL("jar:file:/path/to/pgjdbc2.jar!/");
String classname = "org.postgresql.Driver";
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver)Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
DriverManager.getConnection("jdbc:postgresql://host/db", "user", "pw");

好吧,把
Constructor
作为
Class进行检查。newInstance
很讨厌。我能更详细地解释一下上面的注释是什么意思吗?或者是指向解释的链接?@JackJ Class.newInstance()自Java 9以来就被弃用了,这是有充分理由的。请参见Euw,这将动态加载的驱动程序置于一个太宽的范围内。最好只构造
驱动程序
并使用它,而不使用
驱动程序管理器
@TomHawtin tackline的肮脏性。如果您知道URL将用于什么RDBMS,这是一个好主意,但在某些情况下,您可能不知道。
Driver driver = Class.forName(drivername, true, loader).newInstance();
Connection connect = driver.connect(url, props);
class DriverShim implements Driver {
    private Driver driver;
    DriverShim(Driver d) { this.driver = d; }
    public boolean acceptsURL(String u) throws SQLException {
        return this.driver.acceptsURL(u);
    }
    public Connection connect(String u, Properties p) throws SQLException {
        return this.driver.connect(u, p);
    }
    // and so on....
URL u = new URL("jar:file:/path/to/pgjdbc2.jar!/");
String classname = "org.postgresql.Driver";
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver)Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
DriverManager.getConnection("jdbc:postgresql://host/db", "user", "pw");