我们正在使用setAccessible(true)
在几个地方获得对私有 API 的反射访问。 JDK9 发布后,我们不能指望这一点。
特别是,SQL 对象中的DefaultMethodHandler
使用setAccessible
来访问私有成员的MethodHandle.Lookup
,以便我们可以调用默认(“超级”)方法。
看:
我已经在 JDK 9-ea+136 上测试了最新的 JDBI 快照,不幸的是,默认方法不起作用。
java.lang.RuntimeException: java.lang.IllegalAccessException: access to public member failed: org.jdbi.v3.sqlobject.TestSqlObject$Dao.doesTransactionAnnotationWork()boolean/invokeSpecial, from org.jdbi.v3.sqlobject.TestSqlObject$Dao/2 (unnamed module @34b7bfc0)
at org.jdbi.v3.sqlobject.DefaultMethodHandler.invoke(DefaultMethodHandler.java:63)
at org.jdbi.v3.sqlobject.TransactionDecorator.lambda$invoke$0(TransactionDecorator.java:54)
at org.jdbi.v3.core.transaction.LocalTransactionHandler.inTransaction(LocalTransactionHandler.java:173)
at org.jdbi.v3.core.Handle.inTransaction(Handle.java:478)
at org.jdbi.v3.sqlobject.TransactionDecorator.invoke(TransactionDecorator.java:57)
at org.jdbi.v3.sqlobject.SqlObjectFactory.lambda$createInvocationHandler$18(SqlObjectFactory.java:241)
at com.sun.proxy.$Proxy16.doesTransactionAnnotationWork(Unknown Source)
at org.jdbi.v3.sqlobject.TestSqlObject.testTransactionAnnotationWorksOnInterfaceDefaultMethod(TestSqlObject.java:115)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@9-ea/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@9-ea/NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@9-ea/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@9-ea/Method.java:535)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@9-ea/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@9-ea/NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@9-ea/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@9-ea/Method.java:535)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.IllegalAccessException: access to public member failed: org.jdbi.v3.sqlobject.TestSqlObject$Dao.doesTransactionAnnotationWork()boolean/invokeSpecial, from org.jdbi.v3.sqlobject.TestSqlObject$Dao/2 (unnamed module @34b7bfc0)
at java.lang.invoke.MemberName.makeAccessException(java.base@9-ea/MemberName.java:915)
at java.lang.invoke.MethodHandles$Lookup.checkAccess(java.base@9-ea/MethodHandles.java:1924)
at java.lang.invoke.MethodHandles$Lookup.checkMethod(java.base@9-ea/MethodHandles.java:1864)
at java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(java.base@9-ea/MethodHandles.java:2013)
at java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(java.base@9-ea/MethodHandles.java:2007)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(java.base@9-ea/MethodHandles.java:1539)
at org.jdbi.v3.sqlobject.DefaultMethodHandler.invoke(DefaultMethodHandler.java:51)
... 38 more
可悲的是,我也没有看到解决方法。
互联网上遇到同样问题的所有人都以这种黑客作为唯一的解决方案,这有点奇怪。 看起来 JDK 团队有点疏忽,没有注意“代理中的默认方法”用例。
嗯,让它与:
Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
field.setAccessible(true);
MethodHandles.Lookup lookup = (MethodHandles.Lookup) field.get(null);
Class<?> declaringClass = method.getDeclaringClass();
return lookup.unreflectSpecial(method, declaringClass)
.bindTo(target)
.invokeWithArguments(args);
不知道为什么这适用于 JDK9。
叹。 我们可能应该尽早跟进 openjdk 开发人员关于这个问题......
过去几天我一直在看 jigsaw-dev。 关于这个确切的用例已经有一些讨论,但据我所知,它更像是一个脚注,相对于关于限制setAccessible
的价值的激烈讨论。 似乎很多人对这一举动并不满意。
我正在从发布阻止程序中删除它。 上游修复似乎不太可能很快出现,JDK9 还没有发布。 最终修复不应影响公共 API。
我就此事联系了 jigsaw-dev 邮件列表。 http://jigsaw-dev.1059479.n5.nabble.com/Invoking-default-methods-from-a-Proxy-s-InvocationHandler-in-JDK9-td5714878.html。
我们会看看结果如何。
再次将其撞到 core-libs-dev 上。
这应该在最近的 JDK9 版本中为我们解决。 至少在理论上。
基本上,他们更改了安全检查,以便任何人都可以
unreflectSpecial 在默认方法上。
当JDK9登陆时,我们将不得不根据情况动态切换方法
运行 JDBI 的 Java 版本。
2017 年 3 月 1 日下午 2:36,“Steven Schlansker”通知@github.com
写道:
再次将其撞到 core-libs-dev 上。
—
您收到此消息是因为您创作了该线程。
直接回复本邮件,在GitHub上查看
https://github.com/jdbi/jdbi/issues/497#issuecomment-283478025或静音
线程
https://github.com/notifications/unsubscribe-auth/AACW5VBktrJYJatnsABFKay_-Ci5WD_Sks5rheTCgaJpZM4KCb6m
.
啊,是的。 从核心库开发:
您可以使用 MethodHandles.privateLookupIn(JDK 9 中的新功能)替换 Constructor hack 以获取具有目标类私有访问权限的 Lookup 对象。
曼迪
听着真让人耳目一新! 至少我们可以在JDK9中摆脱这种混乱。
对此有什么说法吗? 我什么时候可以在 JDK 9 中使用 JDBI?
我们对 Java 9 支持非常感兴趣,但坦率地说,我还没有开始研究它。 最后我看生态系统显然还需要时间来解决,但也许是时候提高这个优先级了。
这里有什么新东西吗? JDK 10 已经发布了几天...
jdk9没有明显变化,直接10比较好
听起来我们可能必须对代码进行分支并使用反射来利用 JDK9 中添加的新方法,而不会破坏 8 的兼容性。这里最快的方法是提交补丁或至少进行一些工作以找出最小的更改必要的:) 否则我会在这周的某个时候尝试看看这个。
希望达到 10 的大部分工作实际上是达到 9——我认为 10 没有任何大的变化我们期望破坏事物,而我们当然知道 9 破坏事物......
我有一个不需要双重构建的分支,但它需要先修复
好的,我不会开始破解它。
这应该在下一个 Jdbi 版本中得到支持——可能是 v3.2.0
最有用的评论
这应该在下一个 Jdbi 版本中得到支持——可能是 v3.2.0