当前位置: 首页 > news >正文

反射机制的简单示例

一个使用反射机制的简单示例,这个示例将展示如何使用反射来实现一个通用的数据导出功能。

首先,让我们创建必要的项目结构和文件:

  1. 首先修改 pom.xml 添加依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.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>
  1. 创建实体类:
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;
}
  1. 创建一个自定义注解,用于标记需要导出的字段:
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 "";
}
  1. 更新实体类,添加注解:
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;
}
  1. 创建通用的导出工具类:
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;
    }
}
  1. 创建 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);
    }
}
  1. 创建 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> {
}
  1. 最后更新主类:
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 的实现细节:

  1. 首先看方法签名:
public static <T> List<String[]> exportData(List<T> dataList)
  • 这是一个静态泛型方法,<T> 表示可以接收任意类型的列表
  • 返回类型是 List<String[]>,即字符串数组的列表,每个数组代表一行数据
  1. 输入检查:
List<String[]> result = new ArrayList<>();
if (dataList == null || dataList.isEmpty()) {
    return result;
}
  • 创建结果集容器
  • 如果输入列表为空或null,直接返回空结果集
  1. 获取类的字段信息:
Class<?> clazz = dataList.get(0).getClass();
Field[] fields = clazz.getDeclaredFields();
  • 通过第一个对象获取类的Class对象
  • getDeclaredFields() 获取该类声明的所有字段(不包括继承的字段)
  1. 处理标题行:
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
  1. 添加标题行到结果集:
result.add(headers.toArray(new String[0]));
  • 将headers转换为字符串数组并添加为结果集的第一行
  1. 处理数据行:
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);

执行过程:

  1. 首先获取User类的所有字段(name, age, password)
  2. 找到带有@ExportField注解的字段(name, age)
  3. 生成标题行 [“姓名”, “年龄”]
  4. 处理第一行数据 [“张三”, “20”]
  5. 处理第二行数据 [“李四”, “25”]

最终结果:

[
    ["姓名", "年龄"],     // 标题行
    ["张三", "20"],      // 数据行1
    ["李四", "25"]       // 数据行2
]

这个示例展示了如何使用反射机制实现一个通用的数据导出功能:

  1. 我们创建了一个 @ExportField 注解,用于标记需要导出的字段。
  2. ExportUtil 类中,我们使用反射机制:
    • 获取类的所有字段 (clazz.getDeclaredFields())
    • 获取字段上的注解 (field.getAnnotation(ExportField.class))
    • 动态访问对象的字段值 (field.get(data))

这样的设计有以下优点:

  1. 通用性强:可以用于任何带有 @ExportField 注解的实体类
  2. 可扩展性好:只需要在实体类的字段上添加注解就可以控制导出
  3. 维护方便:导出逻辑集中在一个工具类中

要运行这个示例,你需要:

  1. 创建对应的数据库和表
  2. 在 application.yml 中配置数据库连接信息
  3. 启动应用并访问 http://localhost:8080/user/export 接口

这个示例展示了反射机制在实际业务中的应用,通过反射我们可以在运行时动态获取类的信息,实现更加灵活和通用的功能。

相关文章:

  • pytorch预训练模型下载保存路径更改
  • 【干货教程】DeepSeek R1+Open WebUI构建RAG检索增强知识库的实战教程
  • 《DeepSeek Janus Pro 7B:多模态人工智能大模型部署全攻略》
  • 用 UniApp 打造新颖美观的弹出框
  • C#多线程
  • UEFI Spec 学习笔记---11 - Protocols — UEFI Driver Model(1)
  • 嵌入式音视频开发(二)ffmpeg音视频同步
  • 大一计算机的自学总结:前缀树(字典树、Trie树)
  • 【Ai】辅助编程利器:Cline、Continue
  • 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1)
  • #渗透测试#批量漏洞挖掘#Apache Log4j反序列化命令执行漏洞
  • 基于AdaIN的实时图像风格迁移-照片艺术化
  • 初识LLMs
  • 第四十三篇——零和博弈(鞍点理论):如何找到双方的平衡点?
  • 【Linux系统】生产者消费者模型:基于阻塞队列 BlockingQueue
  • 【笔记】LLM|Ubuntu22服务器极简本地部署DeepSeek+API使用方式
  • 使用apt-rdepends制作软件离线deb安装包
  • 网站搭建基本流程
  • RK3568平台开发系列讲解(PWM篇)SG90 舵机驱动实验
  • 蓝桥杯题目(36进制)
  • 想要“逆转”糖尿病,减少这两处脂肪是关键
  • 农村青年寻路纪|劳动者的书信⑤
  • 中国驻日本大使吴江浩就日本民用飞机侵闯我钓鱼岛领空向日方提出严正交涉
  • 熬夜又不想伤肝?方法只有一个
  • 竞彩湃|德甲保级白热化,都灵主帅直面旧主
  • 人民日报:上海“模速空间”何以汇聚超百家大模型企业