SpringBoot AnnotationFormatterFactory接口+自定义注解实现类型转换

参考资料

  1. 自定义AnnotationFormatterFactory实现注解方式类型转换
  2. Spring MVC 基于AnnotationFormatterFactory接口实现自定义的规则


一. 前期准备

1.1. 自定义转换标记注解

⏹code补全注解,标记code补全到指定的位数

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CompletedCodeAnnotation {
	
	// 补全的位数
    int completedLength();
}

⏹身份证处理注解,可将身份证号处理为一个实体类

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface IdentityCardHandleAnnotation {
    
}

1.2 入参form

import lombok.Data;

@Data
public class Test27Form {

    private String id = "110120119";
	
	// 若code的长度不为5,则左补全至5位
    @CompletedCodeAnnotation(completedLength = 5)
    private String applyNo;
}
import lombok.Builder;
import lombok.ToString;

import java.time.LocalDate;

// 通过lombok的@Builder注解来实现构建者模式创建对象
@Builder
@ToString
public class PersonInfo {

    private String id;

    private String sex;

    private LocalDate birthday;
}

二. 实现AnnotationFormatterFactory接口,构建格式化Factory

2.1 code补全Factory

import org.apache.commons.lang3.StringUtils;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

@Component
public class CompletedCodeFormatterFactory implements AnnotationFormatterFactory<CompletedCodeAnnotation> {

    private final Set<Class<?>> types = new HashSet<>();
	
	// 指定格式化的Field的类型为String类型
    @Override
    public Set<Class<?>> getFieldTypes() {
        types.add(String.class);
        return types;
    }

    @Override
    public Printer<?> getPrinter(CompletedCodeAnnotation annotation, Class<?> fieldType) {
        return null;
    }

    @Override
    public Parser<?> getParser(CompletedCodeAnnotation annotation, Class<?> fieldType) {
		
		// 获取注解上标记的补全长度
        int completedLength = annotation.completedLength();
        return new CompletedCodeFormatter(completedLength);
    }
	
	// 定义一个内部类,更好的聚合
    private class CompletedCodeFormatter implements Formatter<String> {

        private int completedLength;

        private CompletedCodeFormatter(int completedLength) {
            this.completedLength = completedLength;
        }

        @Override
        public String parse(String fieldValue, Locale locale) throws ParseException {
            if (ObjectUtils.isEmpty(fieldValue)) {
                return "";
            }
			
			// 使用apache.commons.lang3包补全code
            return StringUtils.leftPad(fieldValue, completedLength, '0');
        }

        @Override
        public String print(String object, Locale locale) {
            return null;
        }
    }
}

2.2 身份证号处理Factory

import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

@Component
public class IdentityCardFormatterFactory implements AnnotationFormatterFactory<IdentityCardHandleAnnotation> {

    private final Set<Class<?>> types = new HashSet<>();
	
	// 指定格式化的Field的类型为自定义的PersonInfo类型
    @Override
    public Set<Class<?>> getFieldTypes() {
        types.add(PersonInfo.class);
        return types;
    }

    @Override
    public Printer<?> getPrinter(IdentityCardHandleAnnotation annotation, Class<?> fieldType) {
        return null;
    }

    @Override
    public Parser<?> getParser(IdentityCardHandleAnnotation annotation, Class<?> fieldType) {
        return new IdentityCardHandle();
    }

    private class IdentityCardHandle implements Formatter<PersonInfo> {

        @Override
        public PersonInfo parse(String identityId, Locale locale) throws ParseException {
            if (ObjectUtils.isEmpty(identityId)) {
                return null;
            }
			
			// 通过构建者模式创建一个PersonInfo类
            return PersonInfo.builder()
                    .id(identityId)
                    .birthday(this.getBirthday(identityId))
                    .sex(this.getSex(identityId))
                    .build();
        }

        @Override
        public String print(PersonInfo object, Locale locale) {
            return null;
        }
		
		// 从身份证号中获取出出生日期,转换为LocalDate格式
        private LocalDate getBirthday(String identityId) throws ParseException {
            String birthdayStr = identityId.substring(6, 14);
            return LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
        }

        private String getSex(String identityId) {
            // 偶数:女,奇数:男
            return identityId.charAt(16) % 2 == 0 ? "女" : "男";
        }
    }
}

三. 将自定义的格式化Factory添加到Spring中

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Map;

@Configuration
public class MyWebConfigurer implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void addFormatters(FormatterRegistry registry) {
		// 获取Ioc容器中实现了AnnotationFormatterFactory接口的所有的Bean
        Map<String, AnnotationFormatterFactory> beansOfType = applicationContext.getBeansOfType(AnnotationFormatterFactory.class);
        beansOfType.forEach((key, value) -> registry.addFormatterForFieldAnnotation(value));
    }
}

四. 效果

⏹前台

  • 需要使用表单提交数据,不能提交json数据
const formData = {
    applyNo: '10',
    personInfo: '370281199412164456'
};

$.ajax({
    url: "/test27/test",
    type: 'POST',
    data: formData,
    success: function (data, status, xhr) {
        console.log(data);
    }
});

⏹后台

@PostMapping("/test")
@ResponseBody
public Test27Form test(Test27Form form, @IdentityCardHandleAnnotation PersonInfo personInfo) {
    System.out.println(form);
    System.out.println(personInfo);
    return form;
}

可以看到

💪前台提交的applyNo不满5位,已经被补全到5位数
💪前台提交的personInfo(身份证号),已经被处理为一个entity对象了

五. 注意点

  1. 上述转换方式只适用于表单提交数据或者url传参提交数据这两种方式,
  2. 如果是前台使用contentType: "application/json",后台使用@RequestBody来接收数据的话,不适用于此处理。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值