把注解当“便利贴”——运行时自动识别字段

📅 2026/7/27 0:24:14
把注解当“便利贴”——运行时自动识别字段
一、这次又遇到了什么麻烦每个订单对象有几十个字段程序员每次写导出代码都要手动列出“商品名对应A列、价格对应B列”太容易出错而且以后字段一改到处都要改。我们能不能在订单类的字段上直接写好“这是哪一列、什么标题”让程序自己去读这些配置这时候就该注解Annotation出场了。二、注解到底是什么你可以把注解理解成贴在代码上的便利贴。它对程序的运行逻辑没有直接影响但编译器、框架、或者我们自己写的工具可以去读这些“便利贴”上的信息然后决定怎么做。Java内置了三个最常用的便利贴我们项目里其实已经用了// 1. Override —— 告诉编译器“我真的是在重写你帮我检查一下方法签名对不对” Override public void export(ListOrder orders, String filePath) { ... } // 2. Deprecated —— 告诉调用者“这个方法过时了别用了” Deprecated public void oldExportMethod() { ... } // 3. SuppressWarnings —— 告诉编译器“我知道这里有问题别给我报警告了” SuppressWarnings(unchecked) public void someMethod() { ... }现在我们要自己定制一个便利贴让导出工具能读懂。三、第一步自定义一个注解 ExcelColumn这个注解就用来标记字段对应的Excel列标题。import java.lang.annotation.*; // Target 指定这个注解可以贴在哪些地方这里只贴在字段上 Target(ElementType.FIELD) // Retention 指定注解保留到什么时候RUNTIME 表示程序运行时还能通过反射读到它 Retention(RetentionPolicy.RUNTIME) public interface ExcelColumn { // 列标题默认值空字符串 String title() default ; // 列序号从0开始默认-1表示按字段顺序 int order() default -1; }Target决定便利贴能贴哪儿方法、字段、类等。Retention决定便利贴存活多久RUNTIME是存活到运行时这样我们才能用反射读。四、第二步把注解贴在订单类的字段上import java.math.BigDecimal; import java.util.Date; public class Order { ExcelColumn(title 订单编号, order 0) private String orderId; ExcelColumn(title 商品名称, order 1) private String productName; ExcelColumn(title 单价, order 2) private BigDecimal price; ExcelColumn(title 数量, order 3) private int quantity; ExcelColumn(title 下单时间, order 4) private Date createTime; // 有些字段不想导出干脆就不贴注解 private String internalNote; // 构造方法、getter/setter 略... }现在任何一个看这个类的人都能直接知道导出的列标题和顺序。五、第三步让导出器读注解自动生成Excel我们改造一下 ExcelOrderExporter让它利用反射读取注解信息不再需要硬编码列名。import java.lang.reflect.Field; import java.util.*; public class ExcelOrderExporter extends OrderExporter { Override public void export(ListOrder orders, String filePath) { // 1. 反射获取Order类的所有字段 Field[] fields Order.class.getDeclaredFields(); // 2. 找出所有贴了ExcelColumn的字段并按order排序 ListField excelFields new ArrayList(); for (Field field : fields) { if (field.isAnnotationPresent(ExcelColumn.class)) { excelFields.add(field); } } excelFields.sort(Comparator.comparingInt(f - { int o f.getAnnotation(ExcelColumn.class).order(); return o -1 ? Integer.MAX_VALUE : o; })); // 3. 模拟生成Excel真实项目用POI库 System.out.println(----- 导出Excel表头 -----); for (Field field : excelFields) { ExcelColumn col field.getAnnotation(ExcelColumn.class); System.out.print(col.title() \t); } System.out.println(); // 4. 遍历订单数据输出每一行 for (Order order : orders) { for (Field field : excelFields) { field.setAccessible(true); // 因为字段是private的 try { Object value field.get(order); System.out.print(value \t); } catch (IllegalAccessException e) { e.printStackTrace(); } } System.out.println(); } System.out.println(Excel已保存到: filePath); } }六、软件运行时实现了什么功能当用户点击“导出Excel”时程序会读取 Order 类上所有带了 ExcelColumn 的字段。按 order 排列顺序。用 title 做表头。自动提取每个订单对象中对应字段的值填入表格。效果哪天订单表多了个字段开发人员只要在那个字段上加一个注解导出的Excel就会自动多一列不用去修改导出器的核心代码。这就是“便利贴”带来的灵活性和低耦合。