2026 年 4 月 29 日 星期三
  • 登录
  • 注册
周天财经
广告
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金
没有结果
查看所有结果
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金
没有结果
查看所有结果
周天财经
没有结果
查看所有结果
首页 基金

xiuyuantech 博客 - 注解处理器 APT (Annotation Processing Tool)

2025 年 12 月 12 日
在 基金
阅读时间: 22 mins read
阅读:696
A A


xiuyuantech 博客: https://xiuyuantech.github.io/

Related articles

A 股三大指数震荡调整,资源、证券、猪肉等板块补涨,关注 A500ETF 易方达 (159361) 中长期配置价值

2026 年 4 月 29 日
Claude code 接入 OpenRouter 免费模型教程!

Claude code 接入 OpenRouter 免费模型教程!

2026 年 4 月 29 日

广告

注解处理器是一种处理注解的工具,确切的说它是 javac 的一个工具,它用来在编译时扫描和处理注解。
注解处理器以 Java 代码 (或者编译过的字节码) 作为输入, 生成.java 文件作为输出, 减少手动的代码输入。
简单来说就是在编译期, 通过注解生成.java 文件。比如我们经常用的轮子 Dagger2, ButterKnife,
EventBus 都在用,我们要紧跟潮流来看看 APT 技术的来龙去脉。

实现方式
创建 Android Module 命名为 app
创建 Java library Module 命名为 apt-annotation
创建 Java library Module 命名为 apt-processor 依赖 apt-annotation
创建 Android library Module 命名为 apt-library 依赖 apt-annotation、auto-service
可选 compileOnly files(org.gradle.internal.jvm.Jvm.current().getToolsJar())

注意不可都放在一个 Module 中,放在一个 Module 中不会生效!

功能主要分为三个部分

apt-annotation:存放自定义注解
apt-processor:注解处理器,根据 apt-annotation 中的注解,在编译期生成 xxx.java 代码
apt-library:工具类,实现业务逻辑的绑定。

结构图

apt-annotation (自定义注解)

@Retention(RetentionPolicy.CLASS):表示编译时注解
@Target(ElementType.FIELD):表示注解范围为类成员 (构造方法、方法、成员变量)

@Retention: 定义被保留的时间长短
RetentionPoicy.SOURCE、RetentionPoicy.CLASS、RetentionPoicy.RUNTIME
@Target: 定义所修饰的对象范围
TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、LOCAL_VARIABLE 等

apt-processor (注解处理器)

添加依赖

dependencies {
    implementation project(':apt-annotation')
    implementation 'com.google.auto.service:auto-service:1.0-rc4'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
    compileOnly files(org.gradle.internal.jvm.Jvm.current().getToolsJar()) //optional
}

自定义处理器 BindViewProcessor

@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {

    private Messager messager;
    private Trees trees;
    private TreeMaker maker;
    private Name.Table names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
        trees = Trees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        maker = TreeMaker.instance(context);
        names = Names.instance(context).table;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (!roundEnv.processingOver()) {
            Set<? extends Element> elements = roundEnv.getRootElements();
            for (Element each : elements) {
                if (each.getKind() == ElementKind.CLASS) {
                    JCTree tree = (JCTree) trees.getTree(each);
                    if (tree != null) {
                        if (tree instanceof JCTree.JCClassDecl) {
                            if (((JCTree.JCClassDecl) tree).name.toString().equals(
                                    "WebAppInterface")) {
                                tree.accept(new Insert(messager, maker, names));
                                break;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    class Insert extends TreeTranslator {
        private Messager messager;
        private TreeMaker maker;
        private Name.Table names;

        public Insert(Messager messager, TreeMaker maker, Name.Table names) {
            this.messager = messager;
            this.maker = maker;
            this.names = names;
        }

        @Override
        public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
            super.visitMethodDef(jcMethodDecl);
            boolean isVisited = false;
            if (jcMethodDecl.mods != null && jcMethodDecl.mods.annotations != null) {
                List<JCTree.JCAnnotation> ans = jcMethodDecl.mods.annotations;
                for (JCTree.JCAnnotation annotation : ans) {
                    if (annotation.annotationType.toString().equals("JavascriptInterface")) {
                        isVisited = true;
                        break;
                    }
                }
            }
            if (isVisited) {
                JCTree.JCExpression invokeMethod = maker.Apply(List.nil(),
                        maker.Select(maker.Ident(names.fromString("mWebActivity")),
                                names.fromString(
                                        "getCurrentUrl")), List.nil());
                JCTree.JCVariableDecl statement = makeVarDef(maker.Modifiers(0), "surl",
                        memberAccess("java.lang.String"), invokeMethod);
                JCTree.JCExpression invokeMethod1 = maker.Apply(List.of(memberAccess("java.lang" +
                                ".String"), memberAccess("java.lang.String")),
                        memberAccess("com.dxdxmm.app.component.web.WebBridgeHelperKt" +
                                ".isHostVerify"), List.of(maker.Ident(names.fromString("surl")),
                                maker.Literal(jcMethodDecl.name.toString())));
                JCTree.JCVariableDecl statement1 = makeVarDef(maker.Modifiers(0), "verify",
                        maker.TypeIdent(TypeTag.BOOLEAN), invokeMethod1);
                JCTree.JCIf statement2 = maker.If(maker.Ident(names.fromString("verify")),
                        jcMethodDecl.body, maker.Skip());
                JCTree.JCReturn statement3 = maker.Return(maker.Literal(false));
                JCTree.JCReturn statement4 = maker.Return(maker.Literal(""));

                ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
                if (!jcMethodDecl.name.toString().equals("<init>")) {
                    statements.append(statement);
                    statements.append(statement1);
                    statements.append(statement2);
                    if (jcMethodDecl.restype != null) {
                        if (jcMethodDecl.restype.toString().equals("String")) {
                            statements.append(statement4);
                        } else if (jcMethodDecl.restype.toString().equals("boolean")) {
                            statements.append(statement3);
                        }
                    }
                }
                JCTree.JCBlock body = maker.Block(0, statements.toList());
                result = maker.MethodDef(
                        jcMethodDecl.getModifiers(),
                        names.fromString(jcMethodDecl.name.toString()),
                        jcMethodDecl.restype,
                        jcMethodDecl.typarams,
                        jcMethodDecl.params,
                        jcMethodDecl.thrown,
                        body,
                        jcMethodDecl.defaultValue
                );
                note(result.toString());
            }
        }

        private JCTree.JCVariableDecl makeVarDef(JCTree.JCModifiers modifiers, String name,
                                                 JCTree.JCExpression vartype,
                                                 JCTree.JCExpression init) {
            return maker.VarDef(
                    modifiers,
                    names.fromString(name),
                    vartype,
                    init
            );
        }

        private JCTree.JCExpression memberAccess(String components) {
            String[] componentArray = components.split("\\.");
            JCTree.JCExpression expr = maker.Ident(names.fromString(componentArray[0]));
            for (int i = 1; i < componentArray.length; i++) {
                expr = maker.Select(expr, names.fromString(componentArray[i]));
            }
            return expr;
        }
    }

    private void note(String msg) {
        messager.printMessage(Diagnostic.Kind.NOTE, msg);
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(WebModule.class.getCanonicalName());
        return supportTypes;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

init:初始化。可以得到 ProcessingEnviroment,ProcessingEnviroment 提供很多有用的工具类 Elements, Types 和 Filer
getSupportedAnnotationTypes:指定这个注解处理器是注册给哪个注解的,这里说明是注解 BindView
getSupportedSourceVersion:指定使用的 Java 版本,通常这里返回 SourceVersion.latestSupported()
process:可以在这里写扫描、评估和处理注解的代码,生成 Java 文件

Messager : 日志打印类,有利于分析
Trees : 抽象语法树
TreeMaker : 抽象语法树操作类
Name.Table : 命名表, 可根据名称找到对应的方法,变量,类
TreeTranslator : 抽象语法树转换器, 可修改方法,变量,类

其他类不熟悉的自行查询,这里就不一一介绍了。

完成了 Processor 的部分,基本就大功告成了。

apt-library 工具类 可选

在 App Module 的 build.gradle 中添加依赖

dependencies {
    implementation project(':apt-annotation')
}

创建注解工具类 BindViewTools

public class BindViewTools {

    public static void bind(Activity activity) {

        Class clazz = activity.getClass();
        try {
            Class bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
            Method method = bindViewClass.getMethod("bind", activity.getClass());
            method.invoke(bindViewClass.newInstance(), activity);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
```java
通过 javapoet 生成代码:

添加依赖
```java
dependencies {
    implementation 'com.squareup:javapoet:1.10.0'
}

新建 ClassCreatorProxy

public class ClassCreatorProxy {
    //省略部分代码...

    /**
     * 创建 Java 代码
     * @return
     */
    public TypeSpec generateJavaCode2() {
        TypeSpec bindingClass = TypeSpec.classBuilder(mBindingClassName)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(generateMethods2())
                .build();
        return bindingClass;

    }

    /**
     * 加入 Method
     */
    private MethodSpec generateMethods2() {
        ClassName host = ClassName.bestGuess(mTypeElement.getQualifiedName().toString());
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
                .addModifiers(Modifier.PUBLIC)
                .returns(void.class)
                .addParameter(host, "host");

        for (int id : mVariableElementMap.keySet()) {
            VariableElement element = mVariableElementMap.get(id);
            String name = element.getSimpleName().toString();
            String type = element.asType().toString();
            methodBuilder.addCode("host." + name + " = " + "(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));");
        }
        return methodBuilder.build();
    }


    public String getPackageName() {
        return mPackageName;
    }
}

使用自定义注解

使用工具类时:

public class TestActivity extends AppCompatActivity {

    @BindView(R.id.test)
    TextView textView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        BindViewTools.bind(this);
    }

}

不使用工具类, 只修改某个方法时:

@BindView()
public class TestActivity extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
    }

    @JavascriptInterface
    public void test(){

    }
}

业务咨询:https://soloist.pages.dev

相关 文章

A 股三大指数震荡调整,资源、证券、猪肉等板块补涨,关注 A500ETF 易方达 (159361) 中长期配置价值

来自 周天财经
2026 年 4 月 29 日
0

4 月 28 日,A 股三大指数走势分化,全市场...

Claude code 接入 OpenRouter 免费模型教程!

Claude code 接入 OpenRouter 免费模型教程!

来自 周天财经
2026 年 4 月 29 日
0

介绍一种零成本使用 Claude Cod...

市场震荡调整,超 20 只基金单日跌逾 3%

来自 周天财经
2026 年 4 月 29 日
0

每经记者|肖芮冬    每经编辑|赵云 ...

港股震荡调整,关注恒生科技 ETF 易方达 (513010)、港股通互联网 ETF 易方达 (513040) 布局机会

来自 周天财经
2026 年 4 月 28 日
0

本周,中证港股通消费主题指数下跌 0.5%...

中国汽车产业智能化转型全面加速,智能汽车 ETF 易方达 (516590) 标的指数连续三周上涨

来自 周天财经
2026 年 4 月 28 日
0

本周,中证智能电动汽车指数上涨 1.1%,...

加载更多
广告
  • 热门
  • 评论
  • 最新
神马经典投研: 集资讯、策略、研报一站式期货投研工具

神马经典投研: 集资讯、策略、研报一站式期货投研工具

2025 年 11 月 7 日
「我们也深陷残酷价格战」,德资巨头中国区高管警告

「我们也深陷残酷价格战」,德资巨头中国区高管警告

2025 年 8 月 4 日
一周产业基金|上海市人工智能CVC基金发布;湖北百亿人形机器人母基金来了

一周产业基金|上海市人工智能 CVC 基金发布;湖北百亿人形机器人母基金来了

2025 年 8 月 4 日
「硬科技」指数携手上涨,半导体设备ETF易方达(159558)、芯片ETF易方达(516350)等产品助力布局板块龙头

基民懵了!这个火爆的板块年内涨超 37%,主力却借道 ETF 狂抛逾 400 亿元

2025 年 9 月 20 日
Lesson 1: Basics Of Photography With Natural Lighting

The Single Most Important Thing You Need To Know About Success

4
Lesson 1: Basics Of Photography With Natural Lighting

Lesson 1: Basics Of Photography With Natural Lighting

3
Lesson 1: Basics Of Photography With Natural Lighting

5 Ways Animals Will Help You Get More Business

2
Lesson 1: Basics Of Photography With Natural Lighting

New Cryptocurrency That Will Kill Of Bitcoin

2

A 股三大指数震荡调整,资源、证券、猪肉等板块补涨,关注 A500ETF 易方达 (159361) 中长期配置价值

2026 年 4 月 29 日

中科宇航火箭超级工厂投产 商业液体火箭产能竞速升温

2026 年 4 月 29 日

突发!国际油价大涨,美股光通信股盘前调整——道达投资手记

2026 年 4 月 29 日
2025年A股十大股价回调公司

原油最新动态:盘点最新价格走势

2026 年 4 月 29 日
  • 隐私政策
  • 联系我们
  • 关于周天
  • 登录
  • 注册
投诉建议:+86 13326565461

© 2025 广州小舟天传媒有限公司 by 周天财经 - 粤 ICP 备 2025452169 号-1

没有结果
查看所有结果
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金

© 2025 广州小舟天传媒有限公司 by 周天财经 - 粤 ICP 备 2025452169 号-1

欢迎回来!

在下面登录您的帐户

忘记密码? 注册

创建新帐户!

填写以下表格进行注册

所有项目需要填写。 登录

重置您的密码

请输入您的用户名或电子邮件地址以重置密码。

登录

用户登录

还没有账号?立即注册

用户注册

已有账号?立即登录