设为首页收藏本站

安徽论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 83464|回复: 0

Dart:通过注解生成代码

[复制链接]

1

主题

0

回帖

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-1-31 13:02:47 | 显示全部楼层 |阅读模式
网站内容均来自网络,本站只提供信息平台,如有侵权请联系删除,谢谢!
引言

拒绝重复工作,追求效率和性能。基于Dart的注解处理库 source_gen ,我们来感受一下,如何使用自定义注解生成代码。
添加引用

source_gen  : 用于解析注解
build_runner : 用于生成代码
dependencies:  flutter:    sdk: flutter  source_gen:  build_runner:

  • 第一步:自定义annotation
新建 test_anotaion.dart 文件,自定义带入参的类 ParamMetadata 用作注解使用
//带入参的自定义注解class ParamMetadata {  final String name;  final int id;  const ParamMetadata(this.name, this.id);}重点: 注解类其构造方法要求必须使用const修饰

  • 第二步:使用注解
注解类定义好后,新建文件 model_class.dart,创建一个实体类,并使用 ParamMetadata 进行注解标识:
@ParamMetadata("annotationClass", 1)class ClassModel {  final String className;  final List members;  ClassModel(this.className, this.members);  void funPrint(String content) {    print("className = $className ; members = ${members.toString()}");  }}class Student {  final String name;  const Student(this.name);}

  • 第三步:解析注解,生成代码

  • 创建解析器需要继承 GeneratorForAnnotation 泛型T 为需要解析的注解类,也就是我们自定义的 ParamMetadata。
import 'package:source_gen/source_gen.dart';import 'package:test_anotation/anotation/test_anotation.dart';import 'package:analyzer/dart/element/element.dart' as e;import 'package:build/build.dart';import 'package:test_anotation/generator/temp_code.dart';class TestGenerator extends GeneratorForAnnotation {  @override  generateForAnnotatedElement(e.Element element, ConstantReader annotation, BuildStep buildStep) {    analyseBuildStep(buildStep);    analyseAnnotation(annotation);    return tempCode(element.name, analyseElement(element));  }  //分析元素  String analyseElement(e.Element element) {    print("ElementKind : ${element.kind.name} \n");    switch (element.kind) {      case e.ElementKind.CLASS:        //注释用在class上        return _analyseElementForClass(element as e.ClassElement);      case e.ElementKind.FUNCTION:        //注解用在方法上        return _analyseElementForMethod(element as e.FunctionElement);      default:        return "";    }  }  //注解用在class上  String _analyseElementForClass(e.ClassElement classElement) {    var fieldStr = "  class中拦截到的成员字段有:\n";    for (var e in classElement.fields) {      fieldStr += "   ${e.declaration}\n";    }    var methodStr = " class中拦截到的成员方法:\n";    for (var e in classElement.methods) {      methodStr += "   ${e.declaration}\n";    }    return fieldStr + "\n" + methodStr;  }  //注解用在方法上  String _analyseElementForMethod(e.FunctionElement methodElement) {    var result =        "方法名称 : ${methodElement.name}, 方法参数:${methodElement.parameters[0].declaration} \n";    return result;  }  //分析注解传参  void analyseAnnotation(ConstantReader annotation) {    print("analyseAnnotation \n");    print("params - name : ${annotation.read("name")}\n");    print("params - id : ${annotation.read("id")}\n");  }  //分析构建的输入输出信息  void analyseBuildStep(BuildStep buildStep) {    print("当前输入源: ${buildStep.inputId.toString()}\n");  }}//创建文件的模板tempCode(String className, String content) {  return """      class ${className}APT {        /**        $content        */      }      """;}重写 generateForAnnotatedElement 对 ParamMetadata 类型的注解进行拦截:

  • analyseElement(): 获取分析元素,区分元素类型分别处理,具体看代码注解。demo中把解析出来的内容封存成字符串,最终把文本结果显示在生成的代码文件上。
  • analyseBuildStep():获取构建的输入输出信息,例如demo中会打印
    当前输入源: test_anotation|lib/model/model_class.dart
  • analyseAnnotation():获取分析注解的传参,例如我们注解构造方法里的 name , id 传参字段
有小伙伴应该注意到了 import 引用中使用了 as 关键字,在使用 Element 时,直接使用别名进行引用,这是由于 Element 在不同的库里有重复命名,使用 as 关键字处理报错报红。
import 'package:analyzer/dart/element/element.dart' as e;...generateForAnnotatedElement(e.Element element, ConstantReader annotation, BuildStep buildStep) ...<hr>

  • 配置文件
我们知道Flutter中在禁用了dart:mirror,无法使用反射。所以我们只能通过命令在编译期进行触发操作,在执行命令前,我们还有一个步骤需要实现,创建配置文件:

  • 创建一个返回类型为 Builder 的全局方法:新建一个文件(名字随意),例如 test_build.dart,内容如下:
import 'package:source_gen/source_gen.dart';import 'package:build/build.dart' as build;import 'package:test_anotation/generator/annotation_class_generator.dart';build.Builder testBuilder(build.BuilderOptions options) => LibraryBuilder(TestGenerator());// TestGenerator 就是我们的解析器

  • 在项目根目录创建 build.yaml 文件,其意义在于 配置 Builder 的各项参数:
builders:  testBuilder:    import: "package:test_anotation/builder/test_builder.dart"    builder_factories: ["testBuilder"]    build_extensions: {".dart": [".g.part"]}    auto_apply: root_package    build_to: sourceimport:Builder 的全局方法所在文件的具体路径
builder_factories:Builder 的全局方法的方法名称
<hr>build Run 运行,(解析注解生成代码)

完成以上工作后,可直接在项目的命令行窗口,运行如下命令,开始解析注解,生成代码:
清理生成的文件:flutter packages pub run build_runner clean生成代码文件:flutter packages pub run build_runner build根据 model_class.dart 文件,同目录下生成文件 model_class.g.dart


根据解析器拦截逻辑,生成的代码为:
// GENERATED CODE - DO NOT MODIFY BY HAND// **************************************************************************// TestGenerator// **************************************************************************class ClassModelAPT {  /**          class中拦截到的成员字段有:   String* className   List* members class中拦截到的成员方法:   void funPrint(String* content)        */}总结提示:


  • 一个 Generator 只能解析一种类型的 annotation,如果有多种类型的 annotation 需要创建多个解析器。(配置文件中也需要同步添加对应内容)
  • 使用 source_gen 提供的默认处理器: GeneratorForAnnotation ,处理器只能处理 top-level级别的元素,例如直接在.dart 文件定义的Class、function、enums等等,但对于类内部Fields、functions 上使用的注解则无法拦截。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
免责声明
1. 本论坛所提供的信息均来自网络,本网站只提供平台服务,所有账号发表的言论与本网站无关。
2. 其他单位或个人在使用、转载或引用本文时,必须事先获得该帖子作者和本人的同意。
3. 本帖部分内容转载自其他媒体,但并不代表本人赞同其观点和对其真实性负责。
4. 如有侵权,请立即联系,本网站将及时删除相关内容。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表