Как динамически загрузить драйвер JDBC во время выполнения, начиная с Java 9? - PullRequest
0 голосов
/ 16 января 2019

Я сейчас перевожу свой код Java 8 на Java 11 и наткнулся на проблему. Я ищу файлы jar в каталоге и добавляю их в путь к классам, чтобы использовать их в качестве драйверов JDBC.

После этого я могу легко использовать DriverManager.getConnection(jdbcString); для подключения к любой базе данных, которую я предварительно загрузил драйвер.

Я использовал для загрузки драйверов, используя этот бит кода, который больше не работает, поскольку SystemClassLoader больше не является URLClassLoader.

Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
method.setAccessible(true);
method.invoke(ClassLoader.getSystemClassLoader(), new Object[] { jdbcDriver.toURI().toURL() });

Так что, посмотрев вокруг на альтернативы, я нашел такой ответ на SO: https://stackoverflow.com/a/14479658/10511969

К сожалению, для этого подхода мне понадобится имя класса драйверов, то есть "org.postgresql.Driver", которого я не знаю.

Разве просто нет способа сделать это больше, или я что-то упустил?

1 Ответ

0 голосов
/ 16 января 2019

Незнание класса водителя кажется странным ограничением.

Я бы выбрал специальный загрузчик классов, который после инициализации класса (я думаю, вы можете сделать это) вызывает DriverManager.getDrivers и регистрирует любые новые драйверы, которые он находит. (У меня нет времени на написание кода.)

Хакерской альтернативой было бы загрузить весь ваш код (кроме начальной загрузки) в URLClassLoader и addURL к этому.

Редактировать: Итак, я написал некоторый код.

Он создает загрузчик классов для драйверов, который также содержит класс «scout», который пересылает DriverManager.drivers (который является непослушным методом, чувствительным к вызывающим абонентам (новый!)). Поддельный драйвер в загрузчике класса приложения перенаправляет попытки подключения к любым динамически загружаемым драйверам во время запроса.

У меня нет драйверов для JDBC 4.0 или более поздней версии, на которых можно было бы это протестировать. Возможно, вы захотите изменить URL - вам понадобится класс Scout и банка с драйверами.

import java.lang.reflect.*;
import java.net.*;
import java.sql.*;
import java.util.*;
import java.util.logging.*;
import java.util.stream.*;

class FakeJDBCDriver {
    public static void main(String[] args) throws Exception {
        URLClassLoader loader = URLClassLoader.newInstance(
            new URL[] { new java.io.File("dynamic").toURI().toURL() },
            FakeJDBCDriver.class.getClassLoader()
        );
        Class<?> scout = loader.loadClass("Scout");
        Method driversMethod = scout.getMethod("drivers");
        DriverManager.registerDriver(new Driver() {
            public int getMajorVersion() {
                return 0;
            }
            public int getMinorVersion() {
                return 0;
            }
            public Logger getParentLogger() throws SQLFeatureNotSupportedException {
                throw new SQLFeatureNotSupportedException();
            }
            public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
                return new DriverPropertyInfo[] { };
            }
            public boolean jdbcCompliant() {
                return false;
            }
            public boolean acceptsURL(String url) throws SQLException {
                if (url == null) {
                    throw new SQLException();
                }
                for (Iterator<Driver> iter=drivers(); iter.hasNext(); ) {
                    Driver driver = iter.next();
                    if (
                        driver.getClass().getClassLoader() == loader &&
                        driver.acceptsURL(url)
                    ) {
                        return true;
                    }
                }
                return false;
            }
            public Connection connect(String url, Properties info) throws SQLException {
                if (url == null) {
                    throw new SQLException();
                }
                for (Iterator<Driver> iter=drivers(); iter.hasNext(); ) {
                    Driver driver = iter.next();
                    if (
                        driver.getClass().getClassLoader() == loader &&
                        driver.acceptsURL(url)
                    ) {
                        Connection connection = driver.connect(url, info);
                        if (connection != null) {
                            return connection;
                        }
                    }
                }
                return null;
            }
            private Iterator<Driver> drivers() {
                try {
                    return ((Stream<Driver>)driversMethod.invoke(null)).iterator();
                } catch (IllegalAccessException exc) {
                    throw new Error(exc);
                } catch (InvocationTargetException exc) {
                    Throwable cause = exc.getTargetException();
                    if (cause instanceof Error) {
                        throw (Error)cause;
                    } else if (cause instanceof RuntimeException) {
                        throw (RuntimeException)cause;
                    } else {
                        throw new Error(exc);
                    }
                }
            }
        });

        // This the driver I'm trying to access, but isn't even in a jar.
        Class.forName("MyDriver", true, loader);

        // Just some nonsense to smoke test.
        System.err.println(DriverManager.drivers().collect(Collectors.toList()));
        System.err.println(DriverManager.getConnection("jdbc:mydriver"));
    }
}

В каталоге dynamic (относительно текущего рабочего каталога):

import java.sql.*;

public interface Scout {
    public static java.util.stream.Stream<Driver> drivers() {
        return DriverManager.drivers();
    }
}

Я бы всегда предлагал избегать установки загрузчика класса контекста потока на что-либо кроме загрузчика, который все отрицает, или, возможно, null.

Модули вполне могут позволить вам аккуратно загружать драйверы, но я не смотрел.

...