Java 8 では、呼び出し元が非パブリック・プロキシー・インスタンスと同じランタイム・パッケージ内にない場合に、プロキシー・インスタンスを作成するために Proxy.getProxyClass メソッドと Constructor.newInstance メソッドを使用するコードの振る舞いが変更されました。
Java 7 でこれらのメソッドを使用すると、プロキシー・クラスが作成されます。Java 8 でこれらのメソッドを使用すると、IllegalAccessException が発生して失敗します。
アナライザーは使用されているインターフェースやそれらの可視性を常に判断できるわけではないため、この規則により、Constructor.newInstance(InvocationHandler) のすべての呼び出しにフラグを立てます。ただし、それらがアクセス可能と定義されている場合を除きます。この規則は、Constructor.newInstance(InvocationHandler) メソッドの起動に対して、同じメソッド内に以下のメソッドへの呼び出しが前にある場合には、フラグを立てません。
Proxy.getProxyClass(ClassLoader, Class...) メソッドConstructor.setAccessible(true) メソッド
フラグが立てられたコードを調べて、getProxyClass メソッドが他のランタイム・パッケージから呼び出されたか、またプロキシー・インターフェースのいずれかが非パブリックかどうかを確認します。例えば、public キーワードを持たないパッケージ・レベルのインターフェースは非パブリックです。
Java 8 でプロキシー・クラスを作成するには、以下のいずれかの手法を使用します。
Constructor.setAccessible(true) を呼び出して、アクセス可能なフラグを設定する。Proxy.newProxyInstance 便利メソッドを使用する。
セキュリティー・マネージャーが存在する場合、SecurityException を回避するには、いずれの解決方法にも ReflectPermission("newProxyInPackage.{package name}") 権限が必要です。
以下の例では、proxyClass メソッドが非パブリック・インターフェースをインスタンス化する場合に、コードを変更する方法を示します。
|
public Object instantiateClass(Class<?> proxyClass,
InvocationHandler handler) throws Exception { Object o=null; o = proxyClass.getConstructor(InvocationHandler.class).newInstance(handler); return o; } |
|
public Object instantiateClass(Class<?> proxyClass,
InvocationHandler handler) throws Exception { Object o=null; o = proxyClass.getConstructor(InvocationHandler.class).newInstance(handler); Constructor c = proxyClass.getConstructor(InvocationHandler.class); c.setAccessible(true); o = c.newInstance(handler); return o; } |
|
public Object instantiateClass(Class<?> proxyClass,
InvocationHandler handler) throws Exception { Object o=null; o = Proxy.newProxyInstance(proxyClass.getClassLoader(), proxyClass.getInterfaces(), handler); return o; } |
詳しくは、以下のクラスに関する Java 資料を参照してください。