java自定义注解实现字段格式化

实际需求中,有这一个场景,从上游接数据,数据长度和精度五花八门,但输出的时候要统一修改为保留两位小数

一、自定义格式化注解

我自定义了一个注解,来完成这个功能

这个注解定义在对象属性之上,有两个字段,一个代表目标格式,默认是保留两位小数,一个是如果遇到异常,比如字段为空的时候,这个时候默认值是多少

@Target({ ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DecimalFormat {
     String format() default  "%.2f";
     String exception() default "0";
}

二、注解处理器

定义好之后,就可以把注解加上,但想要注解生效,还需要一个注解处理器

public class DecimalFormatProcessor {

    public static void doFormat(Object obj) throws IllegalAccessException {
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if(declaredField.isAnnotationPresent(DecimalFormat.class)){
                DecimalFormat format = declaredField.getDeclaredAnnotation(DecimalFormat.class);
                String format1 = format.format();
                String exceptionResult = format.exception();
                declaredField.setAccessible(true);
                Object o = declaredField.get(obj);
                if (o!=null) {
                    try {
                        double v = Double.parseDouble(o.toString());
                        String result = new Formatter().format(format1, v).toString();
                        declaredField.set(obj,result);
                    } catch (NumberFormatException e) {
                        declaredField.set(obj,exceptionResult);
                    }
                }else{
                    declaredField.set(obj,exceptionResult);
                }


            }
        }
    }
}

这样,生成对象后,手动调用注解处理器,就可以把对象的属性格式化一遍,类似下面

public EnergyRealTime realTime() {
        EnergyRealTime lastRecord = realTimeRepository.findLastRecord();
        try {
            DecimalFormatProcessor.process(lastRecord);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return lastRecord;
    }

三、注解处理自动化

但是当使用的地方比较多,每次都手动调,显得很不优雅,我想到了spring的切面AOP

先改造下上面的注解处理器,让它成为spring的一个bean

@Service
public class DecimalFormatProcessor{

    public  void process(Object obj) throws IllegalAccessException {
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if(declaredField.isAnnotationPresent(DecimalFormat.class)){
                DecimalFormat format = declaredField.getDeclaredAnnotation(DecimalFormat.class);
                String format1 = format.format();
                String exceptionResult = format.exception();
                declaredField.setAccessible(true);
                Object o = declaredField.get(obj);
                if (o!=null) {
                    try {
                        double v = Double.parseDouble(o.toString());
                        String result = new Formatter().format(format1, v).toString();
                        declaredField.set(obj,result);
                    } catch (NumberFormatException e) {
                        declaredField.set(obj,exceptionResult);
                    }
                }else{
                    declaredField.set(obj,exceptionResult);
                }


            }
        }
    }


}

然后定义一个切点注解,用于标记注解处理器在哪里执行,这个注解只能用在方法上

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FormatPoint {
}

 

最后我们实现一个AOP,告诉AOP在有FormatPoint注解的方法上,对这个方法的返回值对象进行一次数据格式化

@Slf4j
@Component
@Aspect
public class FormatAop {
    @Pointcut("@annotation(cn.jinka.gcdp.infrastructure.utils.FormatPoint)")
    public void ServiceAspect() {
    }

    @Autowired
    private DecimalFormatProcessor processor;


    @Around("ServiceAspect()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
        Object[] args = proceedingJoinPoint.getArgs();
        Object proceed = null;
        try {
            proceed = proceedingJoinPoint.proceed(args);
            processor.process(proceed);
        } catch (Throwable e) {
            log.error(e.getMessage());
        }
        return proceed;

    }
}

现在想调用注解,只需要把@FormatPoint加到对应的方法上,就可以生效

@FormatPoint
    public EnergyRealTime realTime() {
        return realTimeRepository.findLastRecord();
    }

这样就大功告成!

posted @ 2023-03-08 15:44  Mars.wang  阅读(1444)  评论(0编辑  收藏  举报