Dart 和Java一样 能够在代码中写注解,也能够反射或者用其他方式获得注解内容,当然也能够基于注解 动态生成一些代码
同样目前比较厉害的第三方库 json_serializable 就是基于 source_gen 和 build_runner 实现 json 注解 完成 json字符串和对象互转的
首先需要一个独立的library项目来完成注解生成代码的builder,同时创建相关的注解类 ,在 pubspec.yaml 中 依赖 build_runner 和 source_gen
build_runner 需要 build.yaml声明依赖的builder构造器build.yaml 放在 pubspec.yaml 一起就好
简单描述下这个 build.yaml,申明了一个 api_generator构造器,会基于.dart文件生成 .g.part文件
这个Builder会在 package:request_generator/builder.dart 的 apiBuilder 方法返回
build.yaml如下
targets:
$default:
builders:
api_generator:
enabled: true
builders:
api_generator:
import: "package:request_generator/builder.dart"
builder_factories: ["apiBuilder"]
build_extensions: {".dart": ["api_generator.g.part"]}
auto_apply: all_packages
build_to: cache
applies_builders: ["source_gen|combining_builder"]
实现这个builder ,就是实现如下方法
Builder apiBuilder(BuilderOptions options) {
print("xxxx");
return SharedPartBuilder([ApiBuilderGenerator()], "api_generator");
}
SharedPartBuilder 是 source_gen提供的用于生成 .g.part的类,然后我们需要实现
里面用到的 ApiBuilderGenerator,这个类是继承自
source_gen提供的GeneratorForAnnotation 类, generateForAnnotatedElement 方法返回的字符串就会自动生成在文件中,简单的来说,这里用了注解类
Request,每个注解为 Request的Dart 类就会进入这个注解扫描生成器 GeneratorForAnnotation,转为 ClassElement对象,然后通过遍历ClassElement的 methods 取得每个方法 methodElement ,每个methodElement 里面有个metadata 集合,获取metadata集合,通过 computeConstantValue 可以获取对应的值,然后基于这些值做相应的文本拼接
class ApiBuilderGenerator extends GeneratorForAnnotation<Request> {
@override
generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) {
print("element is $element");
if (element is! ClassElement) {
throw InvalidGenerationSourceError(
"Request class is not ok for ${element.displayName}");
}
StringBuffer stringBuffer=StringBuffer("");
for (var methodElement in (element as ClassElement).methods) {
for (var annometadata in methodElement.metadata) {
final metadata = annometadata.computeConstantValue();
final metadatatype = annometadata.runtimeType;
print("metadatatype is $metadatatype");
print("metadata type is ${metadata.type.runtimeType}");
if (metadata.type.name == "ApiMethod") {
String method = metadata.getField("method").toStringValue();
String url = metadata.getField("url").toStringValue();
var headerfield = metadata.getField("head");
var head = {};
if (headerfield != null) {
print("headerfield:${headerfield.toMapValue()}");
head = headerfield.toMapValue().map((key, value) =>
MapEntry(key.toStringValue(), value.toStringValue()));
}
if (head == null) {
head = {};
}
head["Content-Type"] = "application/json";
print("genertor is ${method}");
print("bool is ${method == "get"}");
if ("get" == method) {
print("get");
stringBuffer.write( _genGetAnnotation(url, methodElement, head));
stringBuffer.writeln();
} else if ("post" == method) {
print("post");
stringBuffer.write( _genPostAnnotation(url, methodElement, head));
stringBuffer.writeln();
}
}
}
}
return stringBuffer.toString();
}
String _genGetAnnotation(String url, MethodElement element, Map head) {
return """
Future _\$get_${element.name}() {
${_getHeadMapString(head)}
return http.get("$url",headers:header);
}
""";
}
String _genPostAnnotation(String url, MethodElement element, Map head) {
return """
Future _\$post_${element.name}() {
${_getHeadMapString(head)}
return http.post("${url}",headers:header);
}
""";
}
String _getHeadMapString(Map head) {
return """
var header =${jsonEncode(head)};
""";
}
}
class Request {
const Request();
}
class ApiMethod{
final String url;
final String method;
final Map head;
const ApiMethod(this.url, this.method,{this.head});
}
class HttpRequestType {
static const String GET = "get";
static const String POST = "post";
}
按照业界规范,放到 lib/src目录中,同时在lib中创建一个dart文件,将这些类导出
library request_generator;
export 'src/request.dart';
最后就是在别的library或者project中使用这些注解了
具体如下
import 'package:request_generator/out_generator.dart';
import 'package:http/http.dart' as http;
part 'request_util.g.dart';
@Request()
class RequestUtil {
static RequestUtil sInstance = new RequestUtil._internal();
RequestUtil._internal();
factory RequestUtil() {
return sInstance;
}
@ApiMethod("https://swapi.co/api/starships",HttpRequestType.GET,head: {"token":"123"})
Future ships(){
return _$get_ships();
}
@ApiMethod("https://swapi.co/api/starships",HttpRequestType.POST,head: {"token":"123"})
Future upships(){
return _$post_upships();
}
}
最后执行命令生成 .g.part文件了
flutter packages pub run build_runner build