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日14:36、「StevenSchlansker」 [email protected]
書きました:
それをcore-libs-devに再度バンプしました。
—
スレッドを作成したため、これを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/jdbi/jdbi/issues/497#issuecomment-283478025 、またはミュート
スレッド
https://github.com/notifications/unsubscribe-auth/AACW5VBktrJYJatnsABFKay_-Ci5WD_Sks5rheTCgaJpZM4KCb6m
。
ああ、そうだ。 core-libs-devから:
MethodHandles.privateLookupIn(JDK 9の新機能)を使用してコンストラクターハックを置き換え、ターゲットクラスのプライベートアクセスを持つルックアップオブジェクトを取得できます。
マンディ
聞いてさわやかです! 少なくとも、JDK9ではこの混乱を取り除くことができます。
こんにちは私はJDK9を使用してインターフェースのデフォルトメソッドを呼び出す私のプロジェクトOWNERで同様の問題を解決しました。おそらくそれがあなたを助けることができるかもしれない私のユーティリティクラスを見てください。
これについて何か一言? JDK9でJDBIを使用できるようになるのはいつですか。
私たちはJava9のサポートに非常に興味を持っていますが、率直に言って、私はまだそれを調べ始めていません。 最後に私は生態系がまだ落ち着くのにまだ時間が必要であることを明らかに見ました、しかし多分これの優先順位を上げる時です。
ここで何か新しいことはありますか? JDK10は数日から出ています...
jdk9に明らかな変更はありません。直接、10に移動することをお勧めします。
8の互換性を損なうことなく、JDK9で追加された新しいメソッドを利用するには、コードを分岐してリフレクションを使用する必要があるようです。必要です:)それ以外の場合は、今週中にこれを見てみます。
そしてうまくいけば、10に到達するための作業の大部分は実際には9に到達することです-私たちは物事を壊すと予想している10に大きな変化はないと思いますが、私たちは確かに9が物事を壊したことを知っています...
デュアルビルドを必要としないブランチがありますが、最初に修正が必要です
OK、それではハッキングを開始しません。
これは、次のJdbiリリース(おそらくv3.2.0)でサポートされるはずです。
最も参考になるコメント
これは、次のJdbiリリース(おそらくv3.2.0)でサポートされるはずです。