反射机制的简单示例
- 创业
- 2025-08-31 22:42:01

一个使用反射机制的简单示例,这个示例将展示如何使用反射来实现一个通用的数据导出功能。
首先,让我们创建必要的项目结构和文件:
首先修改 pom.xml 添加依赖: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http:// .w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.yam</groupId> <artifactId>reflection-demo</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> </project> 创建实体类: package cn.yam.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("user") public class User { private Long id; private String username; private String email; private Integer age; } 创建一个自定义注解,用于标记需要导出的字段: package cn.yam.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExportField { String value() default ""; } 更新实体类,添加注解: package cn.yam.entity; import cn.yam.annotation.ExportField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("user") public class User { private Long id; @ExportField("用户名") private String username; @ExportField("邮箱") private String email; @ExportField("年龄") private Integer age; } 创建通用的导出工具类: package cn.yam.util; import cn.yam.annotation.ExportField; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class ExportUtil { public static <T> List<String[]> exportData(List<T> dataList) { List<String[]> result = new ArrayList<>(); if (dataList == null || dataList.isEmpty()) { return result; } // 使用反射获取类的所有字段 Class<?> clazz = dataList.get(0).getClass(); Field[] fields = clazz.getDeclaredFields(); // 获取标题行 List<Field> exportFields = new ArrayList<>(); List<String> headers = new ArrayList<>(); for (Field field : fields) { ExportField exportField = field.getAnnotation(ExportField.class); if (exportField != null) { exportFields.add(field); headers.add(exportField.value()); } } // 添加标题行 result.add(headers.toArray(new String[0])); // 添加数据行 for (T data : dataList) { List<String> rowData = new ArrayList<>(); for (Field field : exportFields) { try { field.setAccessible(true); Object value = field.get(data); rowData.add(value == null ? "" : value.toString()); } catch (IllegalAccessException e) { rowData.add(""); } } result.add(rowData.toArray(new String[0])); } return result; } } 创建 Service 和 Controller: package cn.yam.service; import cn.yam.entity.User; import com.baomidou.mybatisplus.extension.service.IService; public interface UserService extends IService<User> { } package cn.yam.service.impl; import cn.yam.entity.User; import cn.yam.mapper.UserMapper; import cn.yam.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { } package cn.yam.controller; import cn.yam.entity.User; import cn.yam.service.UserService; import cn.yam.util.ExportUtil; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; @RestController @RequestMapping("/user") public class UserController { @Resource private UserService userService; @GetMapping("/export") public List<String[]> exportUsers() { List<User> users = userService.list(); return ExportUtil.exportData(users); } } 创建 Mapper 接口: package cn.yam.mapper; import cn.yam.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper extends BaseMapper<User> { } 最后更新主类: package cn.yam; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("cn.yam.mapper") public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } } 详细剖析实现细节好的,我来详细解读 ExportUtil 的实现细节:
首先看方法签名: public static <T> List<String[]> exportData(List<T> dataList) 这是一个静态泛型方法,<T> 表示可以接收任意类型的列表返回类型是 List<String[]>,即字符串数组的列表,每个数组代表一行数据 输入检查: List<String[]> result = new ArrayList<>(); if (dataList == null || dataList.isEmpty()) { return result; } 创建结果集容器如果输入列表为空或null,直接返回空结果集 获取类的字段信息: Class<?> clazz = dataList.get(0).getClass(); Field[] fields = clazz.getDeclaredFields(); 通过第一个对象获取类的Class对象getDeclaredFields() 获取该类声明的所有字段(不包括继承的字段) 处理标题行: List<Field> exportFields = new ArrayList<>(); List<String> headers = new ArrayList<>(); for (Field field : fields) { ExportField exportField = field.getAnnotation(ExportField.class); if (exportField != null) { exportFields.add(field); headers.add(exportField.value()); } } 创建两个列表:一个存储要导出的字段,一个存储表头遍历所有字段,检查是否有 @ExportField 注解如果有注解,将字段添加到 exportFields,将注解的value值添加到headers 添加标题行到结果集: result.add(headers.toArray(new String[0])); 将headers转换为字符串数组并添加为结果集的第一行 处理数据行: for (T data : dataList) { List<String> rowData = new ArrayList<>(); for (Field field : exportFields) { try { field.setAccessible(true); Object value = field.get(data); rowData.add(value == null ? "" : value.toString()); } catch (IllegalAccessException e) { rowData.add(""); } } result.add(rowData.toArray(new String[0])); } 外层循环遍历每个数据对象内层循环遍历要导出的字段field.setAccessible(true) 允许访问私有字段field.get(data) 获取字段值将字段值转换为字符串,如果为null则转换为空字符串如果访问出错(比如没有访问权限),添加空字符串将每行数据转换为字符串数组并添加到结果集让我用一个具体例子来说明整个过程:
// 示例类 public class User { @ExportField("姓名") private String name; @ExportField("年龄") private int age; private String password; // 不导出 } // 使用示例 List<User> users = Arrays.asList( new User("张三", 20), new User("李四", 25) ); List<String[]> result = ExportUtil.exportData(users);执行过程:
首先获取User类的所有字段(name, age, password)找到带有@ExportField注解的字段(name, age)生成标题行 [“姓名”, “年龄”]处理第一行数据 [“张三”, “20”]处理第二行数据 [“李四”, “25”]最终结果:
[ ["姓名", "年龄"], // 标题行 ["张三", "20"], // 数据行1 ["李四", "25"] // 数据行2 ]这个示例展示了如何使用反射机制实现一个通用的数据导出功能:
我们创建了一个 @ExportField 注解,用于标记需要导出的字段。在 ExportUtil 类中,我们使用反射机制: 获取类的所有字段 (clazz.getDeclaredFields())获取字段上的注解 (field.getAnnotation(ExportField.class))动态访问对象的字段值 (field.get(data))这样的设计有以下优点:
通用性强:可以用于任何带有 @ExportField 注解的实体类可扩展性好:只需要在实体类的字段上添加注解就可以控制导出维护方便:导出逻辑集中在一个工具类中要运行这个示例,你需要:
创建对应的数据库和表在 application.yml 中配置数据库连接信息启动应用并访问 http://localhost:8080/user/export 接口这个示例展示了反射机制在实际业务中的应用,通过反射我们可以在运行时动态获取类的信息,实现更加灵活和通用的功能。