2.8 Oracle DatabaseのClass.forName()
JLSでは、Class.forName()
について次のように記述されています。
クラスの完全修飾名を指定すると、このメソッドはクラスを検索、ロードおよびリンクしようとします。正常に実行されると、このクラスのClass
オブジェクトへの参照を戻します。正しく実行されなかった場合は、ClassNotFoundException
のインスタンスがスローされます。
クラスの参照は、参照側のクラスにかわって常にClassLoader
のインスタンスを介して実行されます。Java Development Kit(JDK)実装とOracle JVM実装の違いは、クラスの検索方法です。
-
JDKは、
ClassLoader
の1つのインスタンスを使用して、環境変数CLASSPATH
に指定されている一連のディレクトリ・ツリー・ルートを検索します。 -
Oracle JVMは、クラスの検索方法を指定する複数のリゾルバを定義します。各クラスにはリゾルバが対応付けられており、各クラスは異なるリゾルバを保持できます。
Class.forName()
をコールするメソッドを実行すると、現在実行中のクラス(this
)のリゾルバを使用してクラスが検索されます。
予定にないリゾルバを使用してクラスを検索しようとすると、間違った結果となります。たとえば、スキーマX
のクラスX
がスキーマY
のクラスY
に対して、クラスZ
を参照するように要求する場合は、クラスX
のリゾルバを使用しようとするとエラーになります。参照を実行しているのはクラスY
であるため、クラスZ
の検索にはクラスY
に対応付けられたリゾルバが使用されます。つまり、クラスが別のスキーマに入っているときに、別のクラスの異なるリゾルバを指定すると(異なるスキーマにクラスが存在する場合はデフォルトでこの状態になります)、クラスを検索できない場合があります。
このようなリゾルバの問題は、次の方法で解決できます。
-
Class
オブジェクト自体を指定してクラス名の参照を回避します。 -
Class.forName()
メソッドに対してClassLoader
インスタンスを指定します。 -
classForNameAndSchema()
メソッドに対してクラスとそのクラスが常駐するスキーマを指定します。 -
ClassForName.lookupClass()
に対してスキーマとクラス名を指定します。 -
スキーマ名とクラス名でオブジェクトをシリアライズします。
ノート:
システム・クラスがClass.forName()
を起動すると、予期しない動作が発生することがあります。目的のクラスがSYS
またはPUBLIC
に常駐している場合のみ、そのクラスが検索されます。クラスがSYS
またはPUBLIC
のいずれにも常駐していない場合は、そのクラスに対してPUBLIC
シノニムを宣言できます。
この項の内容は次のとおりです。
関連トピック
2.8.1 Class.forName()へのClassLoaderの指定
Oracle Databaseは、スキーマ内でのクラスの検索にリゾルバを使用します。各クラスには指定のリゾルバが対応付けられており、各クラスには異なるリゾルバを対応付けることができます。したがって、クラスの検索は対応付けられたリゾルバの定義によって決まります。ClassLoader
インスタンスは、指定されたクラスに基づいて検索に使用するリゾルバを判断します。Class.forName()
にClassLoader
インスタンスを指定すると、クラスは、そのクラスのリゾルバに定義されているスキーマ内で検索されます。このClass.forName()
の改良型に対する構文は次のとおりです。
Class.forName (String name, boolean initialize, ClassLoader loader);
次の例は、現行のクラス・インスタンスまたはコール側のクラス・インスタンスのクラス・ローダーを指定する方法を示します。
例2-1 現行のクラスからのリゾルバの取得
Class.getClassLoader()
メソッドを使用することで、任意のインスタンスのクラス・ローダーを取得できます。次の例は、インスタンスx
で表されたクラスのクラス・ローダーを取得します。
Class c1 = Class.forName (x.whatClass(), true, x.getClass().getClassLoader());
例2-2 コール側クラスからのリゾルバの取得
oracle.aurora.vm.OracleRuntime.getCallerClass()
メソッドを使用することで、実行中のメソッドをコールしたインスタンスのクラスを取得できます。クラスを取得した後は、戻されたクラスのClass.getClassLoader()
メソッドをコールします。次の例では、workForCaller()
メソッドをコールしたインスタンスのクラスを取得します。次に、そのクラス・ローダーを取得し、Class.forName()
メソッドに渡します。結果として、クラスの検索に使用されたリゾルバがコール側クラスのリゾルバとなります。
void workForCaller() { ClassLoader c1=oracle.aurora.vm.OracleRuntime.getCallerClass().getClassLoader(); ... Class c=Class.forName(name, true, c1); ... }
2.8.2 classForNameAndSchema()へのクラス名とスキーマ名の指定
検索するスキーマを特定できるリゾルバを指定することで、クラスを検索する位置の問題を解決できます。または、クラスがロードされているスキーマを指定できます。クラスのロード先スキーマが判明している場合は、Oracle Databaseが提供するDbmsJava
クラスにある、classForNameAndSchema()
メソッドを使用できます。このメソッドには、クラス名とそのクラスが常駐するスキーマ名の両方を指定し、このメソッドによって、指定されたスキーマでのクラスの位置が特定されます。
例2-3 スキーマ名とクラス名の指定
次の例は、save()
メソッドを使用してスキーマ名とクラス名を保存する方法を示しています。クラス名とスキーマ名が取得され、DbmsJava.classForNameAndSchema
メソッドを使用してそのクラスの位置が特定されます。
import oracle.aurora.rdbms.ClassHandle; import oracle.aurora.rdbms.Schema; import oracle.aurora.rdbms.DbmsJava; void save (Class c1) { ClassHandle handle = ClassHandle.lookup(c1); Schema schema = handle.schema(); writeName (schema.getName()); writeName (c1.getName()); } Class restore() { String schemaName = readName(); String className = readName(); return DbmsJava.classForNameAndSchema (schemaName, className); }
2.8.3 lookupClass()へのクラス名とスキーマ名の指定
スキーマ名とクラス名が入った1つのString
値をoracle.aurora.util.ClassForName.lookupClass()
メソッドに指定できます。このメソッドをコールすると、指定されたスキーマでそのクラスの位置が特定されます。文字列は次の形式で指定する必要があります。
"<schema>:<class>"
たとえば、HR
スキーマでcom.package.myclass
の位置を特定するには、次のコードを使用します。
oracle.aurora.util.ClassForName.lookupClass("HR:com.package.myclass");
ノート:
スキーマ名には大文字を使用してください。この場合、スキーマ名は大/小文字が区別されます。
2.8.4 シリアライズでのクラス名とスキーマ名の指定
クラスのシリアライズを解除すると、処理の一部として、名前に基づいてクラスが検索されます。検索が正常に行われるためには、シリアライズされたオブジェクトにクラス名とスキーマ名の両方が含まれている必要があります。
Oracle Databaseでは、オブジェクトをシリアライズまたはシリアライズを解除する際に、次のクラスを使用できます。
-
oracle.aurora.rdbms.DbmsObjectOutputStream
このクラスは
java.io.ObjectOutputStream
を拡張し、適切な場所にスキーマ名を追加します。 -
oracle.aurora.rdbms.DbmsObjectInputStream
このクラスは
java.io.ObjectInputStream
を拡張し、DbmsObjectOutputStream
によって書き込まれたストリームを読み取ります。このクラスはあらゆる環境で使用できます。Oracle Databaseで使用した場合は、スキーマ名が読み取られ、クラスを検索する際に使用されます。クライアントで使用した場合、スキーマ名は無視されます。
2.8.5 Class.forNameの例
次に、クラスを参照するいくつかのメソッドの例を示します。
import oracle.aurora.vm.OracleRuntime; import oracle.aurora.rdbms.Schema; import oracle.aurora.rdbms.DbmsJava; public class ForName { private Class from; /* Supply an explicit class to the constructor */ public ForName(Class from) { this.from = from; } /* Use the class of the code containing the "new ForName()" */ public ForName() { from = OracleRuntime.getCallerClass(); } /* lookup relative to Class supplied to constructor */ public Class lookupWithClassLoader(String name) throws ClassNotFoundException { /* A ClassLoader uses the resolver associated with the class*/ return Class.forName(name, true, from.getClassLoader()); } /* In case the schema containing the class is known */ static Class lookupWithSchema(String name, String schema) { Schema s = Schema.lookup(schema); return DbmsJava.classForNameAndSchema(name, s); } }
この例では、次のメソッドを使用してクラスを検索します。
-
インスタンスのクラスのリゾルバを使用するには、
lookupWithClassLoader()
をコールします。このメソッドによって、from
変数のClass.forName()
メソッドにクラス・ローダーが指定されます。from
変数に指定されたクラス・ローダーは、このクラスにデフォルト設定されます。 -
特定のクラスのリゾルバを使用するには、明示したクラス名の後に
lookupWithClassLoader()
を指定してForName()
をコールします。ForName()
メソッドによって、from
変数が指定のクラスに設定されます。lookupWithClassLoader()
メソッドは指定されたクラスのクラス・ローダーを使用します。 -
コール側クラスのリゾルバを使用するには、最初に、パラメータを指定せずに
ForName()
メソッドをコールします。これにより、from
変数がコール側クラスに設定されます。次に、lookupWithClassLoader()
メソッドをコールし、コール側クラスのリゾルバを使用してクラスを検索します。 -
スキーマを指定してクラスを検索するには、
lookupWithSchema()
メソッドをコールします。このメソッドは、classForNameAndSchema()
メソッドにクラス名とスキーマ名を渡します。