Avro 模式中的多态性和继承

Posted

技术标签:

【中文标题】Avro 模式中的多态性和继承【英文标题】:Polymorphism and inheritance in Avro schemas 【发布时间】:2014-01-18 19:26:45 【问题描述】:

是否可以编写一个 Avro 模式/IDL 来生成一个扩展基类或实现接口的 Java 类? 生成的 Java 类似乎扩展了org.apache.avro.specific.SpecificRecordBase。所以,工具可能是要走的路。但是,我不知道这是否可能。

我看到了一些示例,建议在每个特定架构中定义一个显式的“类型”字段,更多的是关联而不是继承语义。

我在我的工厂类和代码的其他部分中大量使用我的基类,并使用<T extends BaseObject> 等泛型。目前,我使用支持继承的 JSON Schema 生成代码。

另一个问题:你可以使用 IDL 来定义没有协议定义的记录吗?我认为答案是否定的,因为编译器抱怨缺少协议关键字。

帮助表示赞赏!谢谢。

【问题讨论】:

【参考方案1】:

我找到了解决这个问题的更好方法。查看 Avro 中的 Schema 生成源,我发现类生成逻辑在内部使用 Velocity 模式来生成类。

我修改了record.vm 模板来实现我的特定接口。有一种方法可以使用 maven 构建插件中的 templateDirectory 配置来指定速度目录的位置。

我也改用SpecificDatumWriter 而不是reflectDatumWriter

<plugin>
  <groupId>org.apache.avro</groupId>
  <artifactId>avro-maven-plugin</artifactId>
   <version>$avro.version</version>
   <executions>
    <execution>
      <phase>generate-sources</phase>
      <goals>
        <goal>schema</goal>
      </goals>
      <configuration>
         <sourceDirectory>$basedir/src/main/resources/avro/schema</sourceDirectory>
         <outputDirectory>$basedir/target/java-gen</outputDirectory>
         <fieldVisibility>private</fieldVisibility>
         <stringType>String</stringType>
         <templateDirectory>$basedir/src/main/resources/avro/velocity-templates/</templateDirectory>
       </configuration>
    </execution>
  </executions>
</plugin>

【讨论】:

仍在模式级别寻找一些继承解决方案(非常确定没有),但我很高兴您记录了自己的发现。谢谢! templates目录下有一个文件record.vm,这是要生成的类的模板,如果需要可以修改模板。在这里你可以包含一些自定义继承,或者创建你自己的模板。【参考方案2】:

我发现这个问题有类似的问题。在我的情况下,我只需要强加 marker interface 并且只强加 some 类型(以便稍后区分特定类)。感谢您的回答,我更深入地研究了record.vm 模板的结构。我发现可以在 .avsc 定义 JSON 中定义 "javaAnnotation": "my.full.AnnotationName" 键。然后将@my.full.AnnotationName 添加到生成的类中。

诚然,这个解决方案最终不是建立在标记界面上的,尽管对我来说已经足够好并且保持模板不变是很大的优势。

【讨论】:

【参考方案3】:

我决定使用ReflectData API 在运行时从类生成模式,然后使用ReflectDatumWriter 进行序列化。 使用反射会更慢。但是,看起来架构是在内部缓存的。如果我发现性能问题,我会报告。

Schema schema = ReflectData.AllowNull.get().getSchema(sourceObject.getClass());
ReflectDatumWriter<T> reflectDatumWriter = new ReflectDatumWriter<>(schema);

DataFileWriter<T> writer = new DataFileWriter<>(reflectDatumWriter);
try 
    writer.setCodec(CodecFactory.snappyCodec());
    writer.create(schema, new File("data.avro"));
    writer.append(sourceObject);
    writer.close();

catch (IOException e) 
    // log exception

【讨论】:

【参考方案4】:

如果我在这里写下我已经为这种情况创建了 maven 插件,我希望对其他人有所帮助 - https://github.com/tunguski/interfacer。

它会检查自动生成的类并检查它们是否符合在特定包中的类路径中找到的接口。如果是,则将接口添加到类中。它适用于通用接口,至少在我必须处理的基本示例中。

该插件不是 avro 特定的,用作生成的代码后处理器,因此也可以在其他情况下使用。

<!-- 
  post process avro generated sources and add interfaces from package
  pl.matsuo.interfacer.showcase to every generated class that has 
  all methods from specific interface
 -->
<plugin>
    <groupId>pl.matsuo.interfacer</groupId>
    <artifactId>interfacer-maven-plugin</artifactId>
    <version>0.0.6</version>
    <executions>
        <execution>
            <configuration>
                <interfacesDirectory>$project.basedir/src/main/java</interfacesDirectory>
                <interfacePackage>pl.matsuo.interfacer.showcase</interfacePackage>
            </configuration>
            <goals>
                <goal>add-interfaces</goal>
            </goals>
        </execution>
    </executions>
</plugin>
// src/main/java manually defined interface
public interface HasName 
  String getName();


// target/generated-sources/avro
public class Person 

  String name;

  public String getName() 
    return name;
  
  // [...]


public class Company 

  String name;

  public String getName() 
    return name;
  
  // [...]


// after this plugin run

// target/generated-sources/avro
public class Person implements HasName 

  String name;

  public String getName() 
    return name;
  
  // [...]


public class Company implements HasName 

  String name;

  public String getName() 
    return name;
  
  // [...]

【讨论】:

你成就了我的一天。【参考方案5】:

我遵循https://www.infoq.com/articles/ApacheAvro/ 来实现继承。这也说明了多态性(我需要)。

一点。 在声明的同时 "name": "user", "type": com.navteq.avro.FacebookUser , 确保双引号,例如"name": "user", "type": "com.navteq.avro.FacebookUser" ,

如果我不这样做,那么我会收到如下错误

&gt; org.apache.avro.SchemaParseException: org.codehaus.jackson.JsonParseException: Unexpected character ('c' (code 99)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')

【讨论】:

以上是关于Avro 模式中的多态性和继承的主要内容,如果未能解决你的问题,请参考以下文章

Java中类的继承,属性和方法的四种修饰符的作用范围,final关键字,java的三大特点中的2个:封装和多态,以及多态的一个设计模式,模板方法模式(template method)

JS面向对象三大特征:封装、继承、多态

C++多态:从虚表指针到设计模式

大话设计模式封装 继承 多态

了解 C++ 中的继承和多态性

设计模式之美——封装,继承,多态的意义