Javaアセンブラー、メタプログラミング、JPA

このトピックでは、コード生成システムをその場で作成した最初の経験を共有したいと思います。 このコードは、前のトピックで説明たアイデアの一部を実装し、コード自体は1つの古いが稼働しているサイト管理システムで使用されます



問題の簡単な説明:



このタスクを完了するために、次が使用されました。





メタモデル


どこから始めますか。 すべては、オブジェクトメタモデルの構築から始まります(ビジネスモデルと混同しないでください)。 これまでのところ、次のモデルに焦点を合わせることが決定されました。

 -構成(複雑なタイプ)
 -タイプ
  -単純型(simpletypetype-文字列/ long / int / enum、enumクラス)
  -ComplexType(属性、一部のプロパティ)
 -属性(名前、タイプ、一部のプロパティ、一部のJPAプロパティ)


同時に、テスト用および一般的な美しさのために、XMLスキーマ(このイデオロギーが基礎とされた)からこの構成を構築できるクラスが作成されました。 たとえば、これから:

<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  1. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  2. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  3. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  4. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  5. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  6. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  7. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  8. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  9. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  10. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  11. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  12. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  13. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  14. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  15. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  16. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  17. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









  18. <? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .









<? xml version ="1.0" encoding ="utf-8" ? > < xs:schema xmlns:xs ="http://www.w3.org/2001/XMLSchema" xmlns:meta ="http://www.arptek.ru/core/meta" targetNamespace ="http://www.arptek.ru/core/meta/test" > < xs:complexType name ="Test" > < xs:annotation > < xs:appinfo > < meta:java.interface > ru.arptek.meta.SomeInterface </ meta:java.interface > </ xs:appinfo > </ xs:annotation > < xs:sequence > < xs:element name ="id" type ="xs:int" meta:jpa . Id ="true" /> < xs:element name ="string" type ="xs:string" /> < xs:element name ="boolean" type ="xs:boolean" /> < xs:element name ="integer" type ="xs:int" /> < xs:element name ="long" type ="xs:long" /> </ xs:sequence > </ xs:complexType > </ xs:schema > * This source code was highlighted with Source Code Highlighter .











ところで、名前空間「http:// www.arptek.ru/core/meta」で追加の属性と要素を使用することに注意してください。 そのため、追加の属性は常に別の名前空間に配置し、要素は一般にxs:注釈/ xs:appinfoに配置する必要があります。 この回路を構成に変換するコードは、 ここにあります



Beanクラスの生成


生成コードは3つのタスクを実行します。



コードを解析する前の注意事項:



アセンブリ言語の特定のJavaコードに対応する簡単な操作を実行する方法に関する知識が必要です。 たとえば、コマンド

  this.aを返します。 


次のコードに変換する必要があります。

     0 aload_0
     1 getfield classname.a
     4 lreturn


この場合、特定の操作コードは、引数を持つ型に依存します。 int / boolean / longの場合、異なる操作コードが使用されます。 しかし、良い点は、CGLibとASMはJavaマシン実装のこれらの機能を私たちから隠し、どのタイプが引数を持ち、どのオペレーションコードを使用するかを自分で制御することです。 たとえば、変数を定義した場合:

  ce.declare_field(Constants.ACC_PROTECTED、fieldName、type、null); 


その後、CGLib / ASMはそのタイプを記憶し、コマンドを使用してコードを生成します

     codeEmitter.load_this(); 
     codeEmitter.getfield(constantName); 
     codeEmitter.return_value(); 


生成されたコードには、設定した変数のタイプで動作するオペランドが正確に含まれます。 そのため、クラスフィールドの値を返すのが高くなりました。 より正確には、このコードは2つの部分に分割できます。 フィールド値をスタックにプッシュする:

     codeEmitter.load_this(); 
     codeEmitter.getfield(constantName); 


スタックのトップ値を返します:

     codeEmitter.return_value(); 


関数の最初の引数をスタックに追加します(何かにさらに割り当てるため)。

     codeEmitter.load_arg(0);


しかし! 問題は、putfield操作中に、スタックの最上部に値が必要であり、その下にフィールド所有者オブジェクトへの参照があることです。 そのため、最初にオブジェクトへの参照を取得し、次に引数をスタックに追加してから、フィールドに値を割り当てます。

          codeEmitter.load_this(); 
          codeEmitter.load_arg(0); 
          codeEmitter.putfield(fieldName); 
          codeEmitter.return_value(); 


メソッド呼び出し。 これを行うには、スタックにオブジェクトと引数への参照が必要です(上記の引数)。

                 codeEmitter.invoke_virtual(parentClass、新しい署名(
                         getterName、fieldValueType、TYPES_EMPTY));


この場合、invoke_staticを使用して静的メソッドを呼び出し、invoke_interfaceを使用してインターフェイス(parentClass-interface)で定義されたメソッドを呼び出し、invoke_specialを使用してコンストラクターを呼び出します(メソッド名は「<init>」)。 ところで、オブジェクトの作成とコンストラクターの呼び出しは別の操作であり、何かによって非常にうまく分離できます。 ここでは、たとえば、LinkedHashMapを作成します。

        ローカルローカル= e.make_local(TYPE_LINKED_HASH_MAP);

         e.new_instance(TYPE_LINKED_HASH_MAP);
         e.dup();
         e.push(fieldClasses.size());
         e.push(10);
         e.push(1f);
         e.invoke_constructor(TYPE_LINKED_HASH_MAP、SIG_MAP_INIT_INT_FLOAT);
         e.store_local(ローカル);


マップを保存するローカル変数を準備します。 次に、ヒープ上に新しいオブジェクトを作成します(メモリを割り当てます)。 次に、dup()オペレーションを使用してスタックの一番上の引数を複製します-コンストラクターを呼び出した後、リンクの1つが残され、値を保存する必要があるためです。 また、コンストラクター呼び出しの引数をスタックに配置し(型を厳密に監視すると、autoconversion int-> floatは機能しません)、invoke_constructorを使用してコンストラクターを呼び出します(invoke_specialに変換されます)。 最後の行-結果をローカル変数に保存します。 その後、スタックは空になります。



クラスの生成:





  1. ClassWriter cw = 新しい DebuggingClassWriter(
  2. ClassWriter.COMPUTE_FRAMES);
  3. ClassEmitter ce = 新しい ClassEmitter(cw);
  4. Type [] interfaces = getInterfacesTypes(complexType);
  5. ce.begin_class(Constants.V1_6、Constants.ACC_PUBLIC、className、
  6. Type.getType(complexType.getSuperClass())、インターフェース、
  7. Constants.SOURCE_FILE);
  8. if (complexType.isJpaEntity()){
  9. ce.visitAnnotation(ANNOTATION_DESCRIPTOR_ENTITY、 true
  10. .visitEnd();
  11. }
  12. if (StringUtils.isNotEmpty(complexType.getJpaTableName())){
  13. final AnnotationVisitor av = ce.visitAnnotation(
  14. ANNOTATION_DESCRIPTOR_TABLE、 true );
  15. av.visit( "name" 、complexType.getJpaTableName());
  16. av.visitEnd();
  17. }
  18. {
  19. if (options.generateFieldAccessors){
  20. final String fieldsMapFieldName = "$ fields $" ;
  21. ce.declare_field(Constants.ACC_PRIVATE
  22. | Constants.ACC_FINAL、fieldsMapFieldName、
  23. TYPE_MAP、 null );
  24. generateConstructorWithFieldsInit(mainClass、ce、
  25. fieldClasses、fieldsMapFieldName);
  26. generateGetFieldsMethod(ce、fieldsMapFieldName);
  27. generateGetFieldMethod(ce、fieldsMapFieldName);
  28. } else {
  29. EmitUtils.null_constructor(ce);
  30. }
  31. for (属性属性:complexType.getAttributes()){
  32. processAttribute(ce、属性);
  33. }
  34. }
  35. ce.end_class();
  36. {
  37. バイト [] bs = cw.toByteArray();
  38. log.info( "生成されたクラス '" + className + "' of" + bs.length
  39. + "bytes" );
  40. result.put(className、bs);
  41. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


最初と最後の行は、チェーンClassEmitter-> ClassWriter-> ByteArrayOutputStream-> bytesを編成します。 クラスコードを生成するときは、ClassEmitterを使用し、CodeEmitterを使用するメソッド、およびアノテーションがない場合は、AnnotationVisitorを使用します(visitEnd()で忘れずに閉じることも重要です)。



クラスを作成するには、そのクラスの名前、バージョン、親クラス、インターフェースなどを選択します。通常のクラスを別の言語で記述しているかのように選択します。 ただし、通常のJavaコードとは異なり、このようなコードはコンパイルできますが、お金を稼ぐことはできません-何かを忘れたり、正しく実行しなかったためです。 たとえば、プロシージャからリターンコードを追加するのを忘れた場合...クラスは生成されますが、機能しません。 つまり、コンパイル段階でそのようなコードのエラーを追跡することは不可能であり、テストケースを実行する必要があります。 すでにアセンブラの認識の準備ができている人はすでにそれについて知っていますが:)



行11から21は、JPAクラスにエンティティテーブルの注釈を追加します。 時間を大幅に節約できる重要なポイントは、visitAnnotationメソッドが文字列を受け入れますが、アノテーションクラスの名前ではなく、内部Java形式の説明を受け入れることです。 たとえば、javax.persistence.Entityの場合、「Ljavax / persistence / Entity;」になります。 ただし、Typeクラスを使用して簡単に取得できます(ほぼすべての行で使用されます)。

1 プライベート スタティック 最終文字列ANNOTATION_DESCRIPTOR_ENTITY = Type.getType(

2エンティティ。 class ).getDescriptor();



36行目は、単純なコンストラクターを追加するために必要です(別のケースでは少し後で)。 ループ39-41では、新しいフィールドを追加し、各属性のメソッドとデフォルトの定数値を取得/設定します。





  1. if (attribute.getType()instanceof EmptyType)
  2. 帰る
  3. String fieldName = attribute.getFieldName();
  4. 文字列getterName = attribute.getGetterName();
  5. 文字列setterName = attribute.getSetterName();
  6. final String javaMethodNamePart = WordUtils.capitalize(
  7. attribute.getAttrName()、 new char [] { '_' })。replace( "_""" );
  8. final Type type = getTypeFromXmlSchemaTypeName(attribute.getType());
  9. if (fieldName == null
  10. fieldName = "m $" + StringUtils.uncapitalize(javaMethodNamePart);
  11. if (getterName == null
  12. getterName = "get" + javaMethodNamePart;
  13. if (setterName == null
  14. setterName = "set" + javaMethodNamePart;
  15. 最後のブール値jpaId = attribute.isJpaId();
  16. final String strDefaultValue;
  17. if (!jpaId){
  18. strDefaultValue = attribute.getStrDefaultValue();
  19. } else {
  20. strDefaultValue = null ;
  21. }
  22. if (strDefaultValue == null ){
  23. ce.declare_field(Constants.ACC_PRIVATE、fieldName、type、 null );
  24. {
  25. //ゲッターを作成します
  26. CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
  27. 新しい署名(getterName、type、TYPES_EMPTY)、
  28. TYPES_EMPTY);
  29. continueAttributeJpaColumnName(属性、codeEmitter);
  30. continueAttributeJpaLob(属性、codeEmitter);
  31. continueAttributeJpaId(属性、codeEmitter);
  32. codeEmitter.load_this();
  33. codeEmitter.getfield(fieldName);
  34. codeEmitter.return_value();
  35. codeEmitter.end_method();
  36. }
  37. {
  38. // nullセーフセッターを作成します
  39. CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
  40. 新しい署名(setterName、Type.VOID_TYPE、
  41. 新しいタイプ[] {type})、TYPES_EMPTY);
  42. codeEmitter.load_this();
  43. codeEmitter.load_arg(0);
  44. codeEmitter.putfield(fieldName);
  45. codeEmitter.return_value();
  46. codeEmitter.end_method();
  47. }
  48. } else {
  49. final Object defaultValue = toValue(type、strDefaultValue);
  50. final String constantName = "DEFAULT_" + fieldName;
  51. ce.declare_field(Constants.ACC_PROTECTED | Constants.ACC_FINAL
  52. | Constants.ACC_STATIC、constantName、type、 null );
  53. {
  54. CodeEmitter staticHook = ce.getStaticHook();
  55. EmitUtils.push_object(staticHook、defaultValue);
  56. staticHook.putfield(constantName);
  57. }
  58. ce.declare_field(Constants.ACC_PROTECTED、fieldName、type、 null );
  59. {
  60. //ゲッターを作成します
  61. 最終署名署名= 新しい署名(getterName、type、
  62. TYPES_EMPTY);
  63. 最終的なCodeEmitter codeEmitter = ce.begin_method(
  64. Constants.ACC_PUBLIC、署名、TYPES_EMPTY);
  65. continueAttributeJpaColumnName(属性、codeEmitter);
  66. continueAttributeJpaLob(属性、codeEmitter);
  67. codeEmitter.load_this();
  68. codeEmitter.getfield(fieldName);
  69. ラベルifNull = codeEmitter.make_label();
  70. codeEmitter.ifnull(ifNull);
  71. {
  72. codeEmitter.load_this();
  73. codeEmitter.getfield(fieldName);
  74. codeEmitter.return_value();
  75. }
  76. codeEmitter.mark(ifNull);
  77. {
  78. codeEmitter.load_this();
  79. codeEmitter.getfield(constantName);
  80. codeEmitter.return_value();
  81. }
  82. codeEmitter.end_method();
  83. }
  84. {
  85. //セッターを作成します
  86. 最終署名署名= 新しい署名(setterName、
  87. Type.VOID_TYPE、 新しいタイプ[] {type});
  88. 最終的なCodeEmitter codeEmitter = ce.begin_method(
  89. Constants.ACC_PUBLIC、署名、TYPES_EMPTY);
  90. codeEmitter.load_this();
  91. codeEmitter.load_arg(0);
  92. codeEmitter.putfield(fieldName);
  93. codeEmitter.return_value();
  94. codeEmitter.end_method();
  95. }
  96. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


フィールドが「空」の場合、チェック1-2が必要です。 このようなフィールドを使用すると、同じテーブルに保存されないが、たとえば独自の状態を管理する属性をクラスに含めることができます。 行4〜18では、変数が初期化され、内部変数とメソッドの将来の名前が定義されます。 コンピューターによって生成されるメソッドと変数には、「$」記号を使用するのが一般的ですが、ユーザーコードでは使用しないことをお勧めします。 もちろん、get / setメソッドはHibernateとビジネスコードから呼び出されるため、この記号はありません。



JPA IDにはデフォルト値が使用されないため、念のため、23〜27行目で設定されていないことを確認してください。 デフォルト値が設定されていて、設定されていない場合のコード生成は異なります。 存在しない場合は、もちろん、より単純で簡単です。フィールド自体は30行目でクラスに追加され、32-46行目でgetメソッドが設定され、48-58行目で追加されます。



デフォルト値のケースには以下が必要です。





フィールドセットアクセス


上記のコードは、(願わくば)「bean」(get / setフィールドとメソッドを持つクラス)を正常に生成しました。 ただし、getField(name)とgetFields()の2つのメソッドを生成する必要があります。 これらのメソッドは、コレクションを介してオブジェクトのフィールドへのアクセスを許可する必要がありますが、ネイティブメソッドを呼び出してシステムの速度を低下させないためには、同時にリフレクションを使用しないでください。

これを行うには、オブジェクトの各属性に対して、2つのメソッドgetValue()およびsetValue()を含むクラスを追加で生成します。これらのメソッドは、これらの呼び出しをメインクラスに委任します。 このようなクラスの生成は非常に簡単ですが、クラスコンストラクターが空ではなく、単一のパラメーター(親オブジェクトへのリンク)を受け入れるという点で興味深いものです。





  1. final String holderFieldName = "$ parent $" ;
  2. ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL、
  3. HolderFieldName、parentClass、 null );
  4. {
  5. CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、
  6. 新しい署名( "<init>" 、Type.VOID_TYPE、
  7. 新しいタイプ[] {parentClass})、 null );
  8. e.load_this();
  9. e.super_invoke_constructor();
  10. e.load_this();
  11. e.load_arg(0);
  12. e.putfield(holderFieldName);
  13. e.return_value();
  14. e.end_method();
  15. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




リンク自体は$ parent $変数に保存されます。 get / setメソッドは非常に簡単に行われます:





  1. {
  2. //ゲッターを作成します
  3. CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
  4. 新しい署名( "getValue" 、fieldValueType、TYPES_EMPTY)、
  5. TYPES_EMPTY);
  6. codeEmitter.load_this();
  7. codeEmitter.getfield(holderFieldName);
  8. codeEmitter.invoke_virtual(parentClass、 新しい署名(
  9. getterName、fieldValueType、TYPES_EMPTY));
  10. codeEmitter.return_value();
  11. codeEmitter.end_method();
  12. }
  13. {
  14. // nullセーフセッターを作成します
  15. CodeEmitter codeEmitter = ce.begin_method(Constants.ACC_PUBLIC、
  16. 新しい署名( "setValue" 、Type.VOID_TYPE、
  17. 新しいタイプ[] {fieldValueType})、TYPES_EMPTY);
  18. codeEmitter.load_this();
  19. codeEmitter.getfield(holderFieldName);
  20. codeEmitter.load_arg(0);
  21. codeEmitter.invoke_virtual(parentClass、 新しい署名(
  22. setterName、Type.VOID_TYPE、
  23. 新しいタイプ[] {fieldValueType}));
  24. codeEmitter.return_value();
  25. codeEmitter.end_method();
  26. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




各属性のこれらのクラスはループで生成され、Map <Attribute、String = class name>に保存されます。





  1. ClassWriter cw = 新しい DebuggingClassWriter(
  2. ClassWriter.COMPUTE_FRAMES);
  3. ClassEmitter ce = 新しい ClassEmitter(cw);
  4. 文字列fieldClassName = processAttributeField(className、
  5. mainClass、ce、属性);
  6. fieldClasses.put(属性、ce.getClassType());
*このソースコードは、 ソースコードハイライターで強調表示されました。




なんで? 後で親クラスのコンストラクターで子オブジェクトのインスタンスを作成し、それらを内部フィールド$ fields $に配置するには:





  1. public void generateConstructorWithFieldsInit(最終タイプmainClass、
  2. ClassEmitter ce、Map <Attribute、Type> fieldClasses、
  3. final String fieldsMapFieldName){
  4. CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、TypeUtils
  5. .parseConstructor( "" )、 null );
  6. e.load_this();
  7. e.super_invoke_constructor();
  8. ローカルローカル= e.make_local(TYPE_LINKED_HASH_MAP);
  9. e.new_instance(TYPE_LINKED_HASH_MAP);
  10. e.dup();
  11. e.push(fieldClasses.size());
  12. e.push(1f);
  13. e.invoke_constructor(TYPE_LINKED_HASH_MAP、SIG_MAP_INIT_INT_FLOAT);
  14. e.store_local(ローカル);
  15. for (エントリ<属性、タイプ> fieldTypes:fieldClasses.entrySet()){
  16. e.load_local(ローカル);
  17. e.push(fieldTypes.getKey()。getAttrName());
  18. // = new <...> Field(this);
  19. e.new_instance(fieldTypes.getValue());
  20. e.dup();
  21. e.load_this();
  22. e.invoke_constructor(fieldTypes.getValue()、 新しい署名( "<init>"
  23. Type.VOID_TYPE、 新しいタイプ[] {m​​ainClass}));
  24. // $ fields $ .put(fieldName、field);
  25. e.invoke_virtual(TYPE_LINKED_HASH_MAP、SIG_MAP_PUT);
  26. }
  27. e.load_this();
  28. e.load_local(ローカル);
  29. e.invoke_static(TYPE_COLLECTIONS、SIG_COLLECTIONS_UNMODIFIABLEMAP);
  30. e.putfield(fieldsMapFieldName);
  31. e.return_value();
  32. e.end_method();
  33. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




最初の行では、LinkedHashMapを作成します(同時に、Genericを使用してパラメーター化を行います-アセンブラーにはほとんどありません)。 次に、子Fieldsオブジェクトを入力し、念のため、Collections.unmodifiableMapでラップして、最終フィールドに保存します。 ちなみに、最後のメソッドはinvoke_staticを使用して呼び出され、コンストラクターでもreturn_value操作で完了しますが、仕様(4〜5行目)ではVOIDを返します。



最後に、getField(name)メソッドとgetFields()メソッドを作成します。これらのメソッドにTransientアノテーションを忘れずに提供し、JPAがこれらをオブジェクトフィールドと見なさないようにします。





  1. public void generateGetFieldMethod(ClassEmitter ce、
  2. final String fieldsMapFieldName){
  3. CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、
  4. SIG_FIELDSHOLDER_GETFIELD、 null );
  5. e.visitAnnotation(ANNOTATION_DESCRIPTOR_TRANSIENT、 true ).visitEnd();
  6. e.load_this();
  7. e.getfield(fieldsMapFieldName);
  8. e.load_arg(0);
  9. e.checkcast(TYPE_OBJECT);
  10. e.invoke_interface(TYPE_MAP、SIG_MAP_GET);
  11. e.checkcast(TYPE_FIELD);
  12. e.return_value();
  13. e.end_method();
  14. }
  15. public void generateGetFieldsMethod(ClassEmitter ce、
  16. final String fieldsMapFieldName){
  17. CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC、
  18. SIG_FIELDSHOLDER_GETFIELDS、 null );
  19. e.visitAnnotation(ANNOTATION_DESCRIPTOR_TRANSIENT、 true ).visitEnd();
  20. e.load_this();
  21. e.getfield(fieldsMapFieldName);
  22. e.return_value();
  23. e.end_method();
  24. }
*このソースコードは、 ソースコードハイライターで強調表示されました。




受信したクラスをロードする


各クラスのバイト[]を生成した後、それらをメモリにロードする必要があります。 これは、ClassLoaderを定義することで実行されます(または、URLClassLoaderを使用してディスクに保存し、そこからロードできます)。





  1. 保護された Map <String、Class <?>> loadClasses(
  2. 最終マップ<String、 バイト []>生成){
  3. 最終スレッドcurrentThread = Thread.currentThread();
  4. final ClassLoader classLoader = currentThread.getContextClassLoader();
  5. final Map <String、Class <?>> classes = new ClassLoader(classLoader){
  6. 保護された Map <String、Class <?>> loadClasses(){
  7. Map <String、Class <?>> result = new LinkedHashMap <String、Class <?>>(
  8. generated.size());
  9. for (エントリ<String、 バイト []>エントリ:generated.entrySet()){
  10. final String className = entry.getKey();
  11. 最終バイト [] byteCode = entry.getValue();
  12. log.info( "Defining class" + className + "..." );
  13. final Class <?> cls = defineClass(className、byteCode、0、
  14. byteCode.length);
  15. result.put(cls.getName()、cls);
  16. }
  17. 結果を返す ;
  18. }
  19. } .loadClasses();
  20. クラスを返す ;
  21. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


ところで、このClassLoaderを忘れてはいけません-現在のClassLoaderがパラメーターとして渡されたクラスをロードできない場合は動作しないため、Hibernateでこれらのクラスをロードするために後で必要になります。



実用化


さらなる研究のために興味深いかもしれないいくつかのリンク:



結果として





All Articles