Java 编码系列:注解处理器详解与面试题解析
引言
在上一篇文章中,我们详细探讨了 Java 注解的基本概念、自定义注解、元注解等技术。本文将继续深入探讨 Java 注解处理器(Annotation Processor),介绍如何编写注解处理器,并结合大厂的最佳实践和面试题详细解析其核心原理。注解处理器在编译时运行,可以根据注解生成新的源代码或修改现有代码,广泛应用于代码生成、依赖注入、编译时检查等场景。
1. 注解处理器概述
1.1 什么是注解处理器
注解处理器(Annotation Processor)是一种在编译时运行的工具,它可以读取、处理和响应注解。注解处理器的主要用途包括:
- 代码生成:根据注解生成新的源代码文件。
- 编译时检查:在编译时检查代码的正确性,提前发现潜在的错误。
- 配置生成:生成配置文件或其他资源文件。
1.2 注解处理器的工作流程
注解处理器的工作流程可以分为以下几个步骤:
- 扫描注解:编译器扫描源代码中的注解。
- 匹配处理器:编译器将找到的注解与注册的注解处理器进行匹配。
- 处理注解:注解处理器处理匹配的注解,生成新的源代码或资源文件。
- 重新编译:生成的新源代码被重新编译,整个过程可能会迭代多次,直到没有新的源代码生成为止。
2. 编写注解处理器
2.1 创建注解处理器类
注解处理器类需要实现 javax.annotation.processing.Processor
接口。通常,我们会继承 AbstractProcessor
类,它提供了一些默认实现,简化了注解处理器的编写。
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {for (TypeElement annotation : annotations) {Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : annotatedElements) {try (PrintWriter out = new PrintWriter(processingEnv.getFiler().createSourceFile(element.getSimpleName() + "Generated").openWriter())) {out.println("package " + element.getEnclosingElement().toString() + ";");out.println();out.println("public class " + element.getSimpleName() + "Generated {");out.println(" public void generatedMethod() {");out.println(" System.out.println(\"This is a generated method\");");out.println(" }");out.println("}");} catch (IOException e) {e.printStackTrace();}}}return true;}
}
2.2 注册注解处理器
为了使编译器能够找到并使用注解处理器,需要在项目的 META-INF/services
目录下创建一个名为 javax.annotation.processing.Processor
的文件,并在其中指定注解处理器的全限定类名。
com.example.MyAnnotationProcessor
3. 使用注解处理器
3.1 定义注解
首先,定义一个简单的注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
}
3.2 应用注解
在类上应用注解:
@MyAnnotation
public class MyClass {public void myMethod() {System.out.println("Method called");}
}
3.3 编译项目
使用 javac
命令编译项目时,注解处理器会自动运行,并生成新的源代码文件。
javac -processorpath path/to/processor.jar -d out src/com/example/MyClass.java
编译完成后,会在 out
目录下生成 MyClassGenerated.java
文件。
4. 大厂最佳实践
4.1 Google Auto
Google Auto 是一组用于生成常见类型代码的库,包括 AutoValue
、AutoService
和 AutoOneOf
。这些库使用注解处理器在编译时生成代码,提高了代码的质量和可维护性。
- AutoValue:用于生成不可变值对象。
- AutoService:用于生成
META-INF/services
文件。 - AutoOneOf:用于生成
OneOf
类型。
4.2 Lombok
Lombok 是一个流行的 Java 库,通过注解处理器在编译时生成常见的样板代码,如 getter
、setter
、toString
等。Lombok 的使用大大简化了代码编写,提高了开发效率。
- @Data:生成
getter
、setter
、toString
、equals
和hashCode
方法。 - @AllArgsConstructor:生成包含所有字段的构造函数。
- @NoArgsConstructor:生成无参构造函数。
4.3 Spring Boot
Spring Boot 广泛使用注解处理器来生成配置类和 Bean 定义。例如,@Configuration
注解用于标记配置类,@ComponentScan
注解用于扫描组件,@EnableAutoConfiguration
注解用于启用自动配置。
5. 面试题解析
5.1 注解处理器的基本概念
Q1: 什么是注解处理器?
- A1: 注解处理器是一种在编译时运行的工具,它可以读取、处理和响应注解。注解处理器的主要用途包括代码生成、编译时检查和配置生成。
Q2: 注解处理器的工作流程是什么?
- A2: 注解处理器的工作流程包括扫描注解、匹配处理器、处理注解和重新编译。编译器扫描源代码中的注解,将找到的注解与注册的注解处理器进行匹配,注解处理器处理匹配的注解,生成新的源代码或资源文件,生成的新源代码被重新编译,整个过程可能会迭代多次。
5.2 编写注解处理器
Q3: 如何创建注解处理器类?
- A3: 注解处理器类需要实现
javax.annotation.processing.Processor
接口,通常继承AbstractProcessor
类。需要重写process
方法,在该方法中处理注解并生成新的源代码或资源文件。
Q4: 如何注册注解处理器?
- A4: 在项目的
META-INF/services
目录下创建一个名为javax.annotation.processing.Processor
的文件,并在其中指定注解处理器的全限定类名。
5.3 使用注解处理器
Q5: 如何定义和应用注解?
- A5: 首先,定义一个注解,使用
@Retention
和@Target
元注解指定注解的保留策略和目标类型。然后,在类、方法或字段上应用注解。
Q6: 如何编译项目以运行注解处理器?
- A6: 使用
javac
命令编译项目时,通过-processorpath
参数指定注解处理器的路径,编译器会自动运行注解处理器并生成新的源代码文件。
5.4 大厂最佳实践
Q7: Google Auto 的主要用途是什么?
- A7: Google Auto 是一组用于生成常见类型代码的库,包括
AutoValue
、AutoService
和AutoOneOf
。这些库使用注解处理器在编译时生成代码,提高了代码的质量和可维护性。
Q8: Lombok 的主要用途是什么?
- A8: Lombok 是一个流行的 Java 库,通过注解处理器在编译时生成常见的样板代码,如
getter
、setter
、toString
等。Lombok 的使用大大简化了代码编写,提高了开发效率。
Q9: Spring Boot 中如何使用注解处理器?
- A9: Spring Boot 广泛使用注解处理器来生成配置类和 Bean 定义。例如,
@Configuration
注解用于标记配置类,@ComponentScan
注解用于扫描组件,@EnableAutoConfiguration
注解用于启用自动配置。
6. 示例代码
6.1 创建注解处理器类
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {for (TypeElement annotation : annotations) {Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : annotatedElements) {try (PrintWriter out = new PrintWriter(processingEnv.getFiler().createSourceFile(element.getSimpleName() + "Generated").openWriter())) {out.println("package " + element.getEnclosingElement().toString() + ";");out.println();out.println("public class " + element.getSimpleName() + "Generated {");out.println(" public void generatedMethod() {");out.println(" System.out.println(\"This is a generated method\");");out.println(" }");out.println("}");} catch (IOException e) {e.printStackTrace();}}}return true;}
}
6.2 注册注解处理器
在 META-INF/services/javax.annotation.processing.Processor
文件中添加:
com.example.MyAnnotationProcessor
6.3 定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
}
6.4 应用注解
@MyAnnotation
public class MyClass {public void myMethod() {System.out.println("Method called");}
}
6.5 编译项目
javac -processorpath path/to/processor.jar -d out src/com/example/MyClass.java
编译完成后,会在 out
目录下生成 MyClassGenerated.java
文件。
7. 总结
本文详细介绍了 Java 注解处理器的基本概念、编写方法、使用步骤,并结合大厂的最佳实践和面试题详细解析了其核心原理。注解处理器在编译时运行,可以根据注解生成新的源代码或修改现有代码,广泛应用于代码生成、依赖注入、编译时检查等场景。合理地使用注解处理器可以简化代码、提高开发效率、增强程序的可维护性。希望本文对你有所帮助,如果你有任何问题或建议,欢迎留言交流。
相关文章:
Java 编码系列:注解处理器详解与面试题解析
引言 在上一篇文章中,我们详细探讨了 Java 注解的基本概念、自定义注解、元注解等技术。本文将继续深入探讨 Java 注解处理器(Annotation Processor),介绍如何编写注解处理器,并结合大厂的最佳实践和面试题详细解析其…...

C语言 | Leetcode C语言题解之第441题排列硬币
题目: 题解: class Solution { public:int arrangeCoins(int n) {return (int) ((sqrt((long long) 8 * n 1) - 1) / 2);} };...

Linux noVNC远程桌面(xfce)部署
一、安装 VNC 服务器和桌面环境 Notebook实验 常用vnc服务 VNC (Virtual Network Computing) 是一种远程桌面协议,可以让你通过网络访问服务器的图形界面。 TurboVNC:专为图形密集型应用设计,尤其适合 3D 可视化和高分辨率图像的远程传输…...
【网络安全】身份认证
1. 身份认证 1.1 定义 身份认证(Authentication)是确认用户身份的过程,确保只有授权的用户才能访问系统或资源。它通常涉及验证用户提供的凭证,如密码、生物特征或其他识别标志。 1.2 重要性 身份认证是信息安全的第一道防线&…...

LeetCode - #124 二叉树中的最大路径和(Top 100)
文章目录 前言1. 描述2. 示例3. 答案关于我们前言 本题为 LeetCode 前 100 高频题 我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新到 123 期…...

Java:插入排序
目录 排序的概念 插入排序 直接插入排序 哈希排序 排序的概念 排序:所谓的排序,就是使一串记录,按照某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个…...

How FAR ARE WE FROM AGI?(ICLR AGI Workshop 2024)概览
关注B站可以观看更多实战教学视频:hallo128的个人空间 How FAR ARE WE FROM AGI?官网 How FAR ARE WE FROM AGI?(ICLR AGI Workshop 2024) 该研讨会将于2024年5月11日在奥地利维也纳以混合模式举行,作为 ICLR 2024年会议的一部…...
leetcode刷题day33|动态规划Part02(62.不同路径、63. 不同路径 II、 343.整数拆分、96.不同的二叉搜索树)
62.不同路径 机器人从(0 , 0) 位置出发,到(m - 1, n - 1)终点。 动规五部曲 1、确定dp数组(dp table)以及下标的含义 dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路…...

基于Python大数据的B站热门视频的数据分析及可视化系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…...
matlab-批处理图像质量变化并形成折线图 (PSNR)
%修改路径就能用,图片分辨率要一致 %clc;clear all;close all;tic;%清理内存 file_pathE:\test\resources\image\;% 批量图像所在的文件夹下 file_save_pathE:\test\resources\SaveImage\;% 要存储的地址 img_path_listdir(strcat(file_path,*.jpg));% 获取批量bm…...
[Doc][Ros2]ros2中Qos(Quality of Service,服务质量)介绍
在 ROS 2 中,QoS(Quality of Service,服务质量)是用于控制节点之间消息传递的可靠性、历史存储和数据持久性等方面的机制。通过 QoS 设置,用户可以更细粒度地控制消息传递的行为,确保在不同网络环境或应用场景中满足特定的通信需求。 几个常用的包: QoSProfile: 含义…...

SpringBoot日志集成-LogBack
Log4J:最早的Java日志框架之一,由Apache基金会发起,提供灵活而强大的日志记录机制JDK自带的日志框架:java.util.logging.Logg,是JDK1.4之后提供的日志API,已淘汰logback: logback一个开源的日志…...

Google BigTable架构详解
文章目录 什么是BigTable?架构图一、整体架构二、数据存储与索引存储模型 三、数据拆分与存储四、元数据管理五、读写流程 其他内容概览负载平衡其他存储和数据库选项 什么是BigTable? Bigtable是Google开发的一个高性能、可扩展的分布式存储系统,用于管理大规模…...

【python】如何切换ipynb的kernel至指定conda环境
需求介绍 打开(若无新建环境) 环境 conda env list conda activate cvml conda install ipykernel python -m ipykernel install --name cvml 以上完成后,打开jupyter 创建一个python文件 在kernel——>change kernel——>python[conda env:cvml] 参考资料…...
Linux【基础指令汇总】
目录 Linux命令的特点 1、文件管理 ls命令 cp命令 mkdir命令 mv命令 pwd命令 2、文档编辑 cat命令 echo命令 rm命令 tail命令 rmdir命令 3、系统管理 rpm命令 find命令 startx命令 uname命令 vmstat命令 4、磁盘管理 df命令 fdisk命令 lsblk命令 hdpar…...
SpringCloud-EurekaClient
创建Module pom.xml <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency> spring:application:name: provider # 应用程序的名称,…...
配置Scrapy项目
配置Scrapy项目是一个涉及多个步骤的过程,在上一篇博客中已经写了安装Scrapy、创建Scrapy项目的步骤。 接下来应该定义Item类、编写爬虫程序以及配置settings.py文件等。以下是一个详细的配置Scrapy项目的步骤: 一、定义Item类 在项目目录下…...

航顺芯片HK32MCU受邀出席汽车芯片国产化与技术创新闭门研讨会
[中国,北京,2024年9月21日]近日,深圳市航顺芯片技术研发有限公司(以下简称“航顺芯片”)产品总监郑增忠受邀出席由中国设备管理协会新能源汽车产业发展促进中心主办的“汽车芯片国产化与技术创新闭门研讨会”。 会上航…...
【深度学习】(6)--图像数据增强
文章目录 图像数据增强一、作用二、增强方法三、代码体现四、增强体现 总结 图像数据增强 数据增强(Data Augmentation),也称为数据增广,是一种在机器学习和深度学习中常用的技术,它通过对现有数据进行各种变换和处理…...
Vscode 远程切换Python虚拟环境
在VSCode中远程切换Python虚拟环境是一个涉及多个步骤的过程,包括安装必要的扩展、连接到远程服务器、创建或激活虚拟环境,并在VSCode中选择相应的Python解释器。以下是一个详细的步骤指南,包括代码示例,旨在帮助我们完成这一过程…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...