DriverManager 在 gradle 自定义插件的任务中看不到依赖项 [英] DriverManager doesn't see dependency in gradle custom plugin's task

查看:26
本文介绍了DriverManager 在 gradle 自定义插件的任务中看不到依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一些 gradle 插件,现在我遇到了一个问题,即 DriverManager 没有看到在 buildscript 依赖项中定义的 JDBC 驱动程序:

I'm writing some gradle plugin and now I've faced with a problem that DriverManager doesn't see JDBC driver defined in buildscript dependency:

我有下一个 build.gradle 文件:

I have next build.gradle file:

buildscript {
   repositories {
      mavenCentral()
   }
   dependencies {
      classpath("com.h2database:h2:1.4.196")
   }
}
plugins {
   id "someplugin"
}
apply plugin: 'groovy'

当我调用在扩展 DefaultTask 的任务中定义的命令时

And when I invoke command defined in task that extends DefaultTask

DriverManager.getConnection("jdbc:h2:mem:", "sa", "")

我收到异常

No suitable driver found for jdbc:h2:mem:

当我对这些类进行单元测试时没有问题 - 只有当我调用调用 DriverManager.getConnection 的插件任务时才会发生这种情况.

There is no problem when I unit-test these classes - it happens only when I invoke plugin's task that invokes DriverManager.getConnection.

我在这里错过了什么?谢谢!

What am I missing here? Thanks!

推荐答案

嗯,有一个关于为什么 DriverManager 不像你使用它那样工作以及如何使它工作的答案,并且有一个回答如何正确使用 Groovy(这是 Gradle 脚本的基础)中的 SQL.我将从使用 Groovy 的 SQL 的正确方法开始:

Well, there is an answer on why DriverManager does not work like you use it and how to make it work and there is an answer on how to use SQL from Groovy (which is the base for Gradle scripts) properly. I'll start with the proper way to use SQL from Groovy:

正确使用来自 Gradle/Groovy 的 SQL:

很遗憾,将驱动添加到 buildscript 类路径不足以使用 Groovy Sql 类,您需要将驱动添加到正确的类加载器中,否则将无法正常工作.

Adding the driver to the buildscript classpath is not enough unfortunately to use the Groovy Sql class, you need to add the driver to the right classloader instead, otherwise it will not work properly.

除了将驱动程序添加到 JVMs ext 目录之外,您还可以像这样动态地执行此操作:

Besides adding the driver to the JVMs ext dir, you can do this dynamically like this:

configurations { jdbc }
jdbc 'com.h2database:h2:1.4.196'
def sqlClassLoader = Sql.classLoader
configurations.jdbc.each { sqlClassLoader.addURL it.toURI().toURL() }
Sql.withInstance('jdbc:h2:mem:', 'sa', '', 'org.h2.Driver') {
    it.execute 'your sql here'
}

正确使用DriverManager:

由于 Groovy 的动态性,您不能像以前那样正确使用 DriverManager.在您使用的方法中,DriverManager 尝试从调用堆栈中动态查找调用者类,然后使用该类的类加载器来查找数据库驱动程序.使用 Groovy,这是找到的一些动态代理类,因此在其类加载器中找不到数据库驱动程序.

You cannot use the DriverManager properly like you did, because of the dynamicness of Groovy. In the method you used, the DriverManager tries to dynamically find the caller class from the callstack and then uses the classloader of that class for finding the database driver. With Groovy, this is some dynamic proxy class that is found and thus the database driver is not found in its class loader.

如果你改为给 DriverManager 明确的调用者类,比如 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""]作为属性,getClass()),它可以正常工作.或者,它也可以不提供任何调用者类,例如 DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, null)这种情况下使用当前线程上下文类加载器,也可以.

If you instead give the DriverManager the proper caller class explicitly like DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, getClass()), it works properly. Alternatively it would also work to not give any caller class like DriverManager.getConnection("jdbc:h2:mem:", [user: "sa", password: ""] as Properties, null) in which case the current threads context class loader is used, which is also fine.

Gradle 中自动查找驱动程序的问题:

DriverManager 类被加载时,它会扫描系统属性jdbc.drivers 和所有提供java.sql.Driver 服务.它遍历那些找到的类并实例化它们.驱动程序本身通常会响应向 DriverManager 注册自己,此时他们可以这样做,以便以后可以像我上面建议的那样进行自动查找.

When the class DriverManager is loaded, it scans through the system property jdbc.drivers and all services that provide the java.sql.Driver service. It iterates through those found classes and instantiates them. The drivers themselves are usually responsive for registering themselves with the DriverManager which they can do at this point in time for being available for automatic lookup later on like I advised above.

现在的问题是,如果您正在使用 Gradle 守护程序(现在是默认设置),并在该守护程序中运行加载 DriverManager 的任何构建(例如在您之前的尝试中),那么这个类已经加载了.如果您稍后将 buildscript 依赖项添加到 H2(或在它不存在但 DriverManager 已加载之后运行它存在的构建),则该类已经加载并且不会查找现在在类路径中的驱动程序.

Now the problem is, if you are using the Gradle Daemon (which is the default by now), and run any build in that daemon that loads the DriverManager (e. g. in your former tries), then the class is already loaded. If you then later on add the buildscript dependency to H2 (or run a build where it is present after one where it not was present but DriverManager got loaded), then the class is already loaded and does not lookup drivers that are now in the classpath.

这里有一些没有明确命名驱动程序类的可能解决方法,从可能最差到可能最好的解决方案:

Here some possible workarounds without explicitly naming the driver class, from probably worst to probably best solution:

  • 为您的构建禁用守护程序,并确保没有人使用您的构建启用守护程序.这很难控制和强制执行,并且会降低构建性能.
  • 在调用 getConnection 之前使用私有方法 DriverManager.loadInitialDrivers() 以确保再次查找并自动添加 H2 驱动程序.更好,但使用私有方法.
  • 自己使用ServiceLoader 加载类路径中的所有Driver 类,使它们像ServiceLoader 一样自行注册到DriverManager.load(Driver.class).collect()getConnection 调用之前.可能是最优雅的解决方案.
  • Disable the daemon for your build and make sure noone using your build enables the daemon. This is hard to control and enforce and degrades build performance.
  • Use the private method DriverManager.loadInitialDrivers() before calling getConnection to make sure the lookup is done again and the H2 driver added automatically. Better but uses a private method.
  • Use the ServiceLoader yourself to load all Driver classes in the classpath to make them self register with the DriverManager like ServiceLoader.load(Driver.class).collect() before the getConnection call. Probably the most elegant solution.

这里有一些显式命名驱动程序类的可能解决方法:

Here some possible workarounds with explicitly naming the driver class:

  • 在使用 getConnection() 之前简单地加载类,使其自注册 org.h2.Driver.toString()
  • 在使用 getConnection() 之前简单地加载类,使其自注册 Class.forName 'org.h2.Driver'
  • Simply load the class before using getConnection() to make it self-register with org.h2.Driver.toString()
  • Simply load the class before using getConnection() to make it self-register with Class.forName 'org.h2.Driver'

这篇关于DriverManager 在 gradle 自定义插件的任务中看不到依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆