Java实战之自定义注解(以excel导出为案例)

  一、注解的作用

        在java中,注解其实就是一个标识,他可以标注类、方法、字段。然后我们可以通过反射判断该类、方法、字段上面有没有该注解,并同时可以获取注解设置的值。在我们的项目中,往往会和aop联合使用,去做一些日志打印或者容器初始化工作等等。

二、接下来直接进入实战

1、首先定义两个注解@Excel、@PrintExcelLog。

@Excel用于设置excel的标题和默认值

@PrintExcelLog用于标识是否打印excel导出日志

/**
 * 用于设置excel的标题和默认值
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Component
public @interface Excel {

    String title() default "";

    String value() default "";
}
/**
 * 用于标识是否打印excel导出日志
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PrintExcelLog {

    boolean value() default true;

}

其中@Retention是用来定义生命周期,我们这里是运行时;

@Target表示作用目标,有以下8种:

  1. ElementType.TYPE: 可以应用于类、接口、枚举(enum)。

  2. ElementType.FIELD: 可以应用于字段(包括枚举常量)。

  3. ElementType.METHOD: 可以应用于方法。

  4. ElementType.PARAMETER: 可以应用于方法的参数。

  5. ElementType.CONSTRUCTOR: 可以应用于构造方法。

  6. ElementType.LOCAL_VARIABLE: 可以应用于局部变量。

  7. ElementType.ANNOTATION_TYPE: 可以应用于注解类型。

  8. ElementType.PACKAGE: 可以应用于包。

2、然后创建一个用户类和用户性别枚举,并加上@Excel注解指定excel的标题和默认值

/**
 * 用户
 */
@Data
public class User {

    // 唯一标识
    private String id;

    @Excel(title = "姓名")
    private String name;

    @Excel(title = "年龄")
    private int age;

    /**
     * 性别 0-女 1-男
     */
    @Excel(title = "性别", value = "未知")
    private int gender;

    public User(String name, int age, int gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
}
/**
 * 性别
 */
@Getter
public enum Gender {
    MALE(1, "男"),
    FEMALE(0, "女"),
    UNKNOWN(2, "未知")

    ;

    private final int code;
    private final String description;

    Gender(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public static String getDescriptionByCode(int code) {
        for (Gender gender : Gender.values()) {
            if (code == gender.code) {
                return gender.description;
            }
        }
        throw new RuntimeException("编码不存在!");
    }
}

3、创建excel导出工具类,这里我们使用的是XssWorkbook这个工具类,通过反射获取注解的title值之后设置给excel的cellValue。并且对于性别我们需要把0、1转换成男、女,默认未知。

/**
 * excel导出工具类
 */
@Component
public class ExcelExportUtil {

    @PrintExcelLog
    public void exportUserData(List<User> userList, OutputStream outputStream) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet();

            // 写入标题行
            Row row = sheet.createRow(0);
            Field[] declaredFields = User.class.getDeclaredFields();
            int columnIndex = 0;
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(Excel.class)) {
                    String title = field.getAnnotation(Excel.class).title();
                    Cell cell = row.createCell(columnIndex++);
                    cell.setCellValue(title);
                }
            }

            // 写入数据行
            for (int i = 0; i < userList.size(); i++) {
                // 从第二行开始写数据
                Row userRow = sheet.createRow(i + 1);
                writeRowData(userRow, userList.get(i));
            }

            workbook.write(outputStream);

        } catch (IOException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private static void writeRowData(Row userRow, User user) throws IllegalAccessException {
        Field[] fields = User.class.getDeclaredFields();
        int columnIndex = 0;
        for (Field field : fields) {
            if (field.isAnnotationPresent(Excel.class)) {
                field.setAccessible(true);
                Cell cell = userRow.createCell(columnIndex++);
                String value = field.getAnnotation(Excel.class).value();

                // 如果为性别字段,通过枚举转换为男/女
                if (StringUtils.hasLength(value)) {
                    String description = Gender.getDescriptionByCode(user.getGender());
                    cell.setCellValue(description);
                } else {
                    cell.setCellValue(String.valueOf(field.get(user)));
                }

                field.setAccessible(false);
            }
        }
    }
}

4、接下来我们对方法exportUserData()进行aop,获取它的参数值及注解信息,打印相关日志。

@Slf4j
@Aspect
@Component
public class GenderAspect {

    @Pointcut("execution(* com.xxl.job.admin.annotationdemo.util.ExcelExportUtil.exportUserData(..))")
    public void setGenderValue() {

    }

    @Around("setGenderValue()")
    public Object doSetGenderValue(ProceedingJoinPoint point) {
        // 获取用户信息
        List<?> userList = new ArrayList<>();
        Object[] args = point.getArgs();
        if (args != null && args.length > 0 && args[0] instanceof List) {
            userList = (List<?>) args[0];
        }
        // 获取注解信息
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        PrintExcelLog annotation = method.getAnnotation(PrintExcelLog.class);
        // 打印日志
        if (Objects.nonNull(annotation) && annotation.value()) {
            userList.forEach(user -> log.info("正在进行Excel导出,用户信息为:{}, 当前时间:{}", user, LocalDateTime.now()));
        }
        try {
            return point.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
}

5、最后创建测试类进行测试,这里就不用post方式传参了,简单new一下,注意java9才支持List.of()写法,java8的需要一个个add了或者使用其他工具类。

@RestController
@RequestMapping("/test")
public class test {
    @Autowired
    private ExcelExportUtil excelExportUtil;

    @GetMapping("/userDataExport")
    public void userDataExport() throws IOException {
        List<User> users = List.of(
                new User("张三", 20, 1),
                new User("李四", 30, 0),
                new User("王五", 40, 1)
        );
        OutputStream outputStream = Files.newOutputStream(Paths.get("F:\\Edge\\springcloud-hrm-master\\xxl-job\\xxl-job-admin\\target\\users.xlsx"));
        excelExportUtil.exportUserData(users, outputStream);
    }

}

6、最终效果

ps:以下是我整理的java面试资料,感兴趣的可以看看。最后,创作不易,觉得写得不错的可以点点关注!

链接:https://www.yuque.com/u39298356/uu4hxh?# 《Java知识宝典》  

  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值