龙空技术网

Jackson-自定义注解及实现数据脱敏、枚举转换

爱做梦的程序员 1185

前言:

现在同学们对“java中文转码”大体比较珍视,姐妹们都需要剖析一些“java中文转码”的相关知识。那么小编同时在网上汇集了一些对于“java中文转码””的相关知识,希望兄弟们能喜欢,我们快快来学习一下吧!

这一章,我们来说说jackson提供的自定义注解及一些应用吧。

废话不多说,撸起来。

1、自定义注解

在Jackson中使用 @JacksonAnnotationsInside来标注当前定义的注解为jackson注解。我们通过一个简单例子来看下。

1.1 自定义注解

java复制代码/** * @author: jiangjs * @description: * @date: 2023/6/15 10:07 **/@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@JacksonAnnotationsInside@JsonInclude(JsonInclude.Include.NON_NULL)@JsonPropertyOrder({"id","nickName","password","name"})public @interface DataOrderAndFilter {}

上述注解实现了序列化时排列的顺序,以及不显示数值为null的字段。

1.2 创建实体

java复制代码/** * @author: jiangjs * @description: * @date: 2023/6/15 10:11 **/@Data@AllArgsConstructor@DataOrderAndFilterpublic class CustomDataEnter {    private Integer id;    private String name;    private String nickName;    private String password;}
1.3 测试
java复制代码@Testpublic void useJacksonAnnotationsInside() throws JsonProcessingException {    CustomDataEnter dataEnter = new CustomDataEnter(1, "zhangsan", null, "123456");    String json = new ObjectMapper().writeValueAsString(dataEnter);    log.info("使用@JacksonAnnotationsInside实现自定义注解序列化json:" + json);}

输出:

上述代码通过简单的例子说明了@JacksonAnnotationsInside可以自定义注解。下面我们来看看在我们日常工作中在哪些地方可以使用。

2、自定义注解应用2.1 实现数据脱敏

结合@JsonSerialize在数据进行序列化时,进行数据的脱敏。

需求:用户信息在返回到前端时,对用户名,身份证号,手机号等进行数据脱敏。

用户名:如果两个字时,只保留姓,后面为*,例如:张三,脱敏后:张*,超过三个字时,则保留前后两个字,中间使用*,例如:张小四,脱敏后:张*四身份证号:前后各保留四位,中间用四个连接,例如:450218199103458901,脱敏后:4501***8901手机号:保留前三位和后四位,中间四位用连接,例如:13456789012,脱敏后:134***9012

根据上述需求,我们先建一个枚举,实现不同类型数据脱敏,也方便后期实现其他字段脱敏时,只要在这个枚举类中加入就行。

2.1.1 创建脱敏枚举

java复制代码/** * @author: jiangjs * @description: 脱敏策略 * @date: 2022/4/15 16:23 **/public enum DesensitizedStrategy {    /**     * 用户信息脱敏     */    USER_NAME(s -> s.replaceAll("(\S)\S(\S*)", "$1*$2")),    /**     * 身份证信息脱敏     */    ID_CARD(s -> s.replaceAll("(\d{4})\d{10}(\w{4})", "$1****$2")),    /**     * 手机号脱敏     */    PHONE(s -> s.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2"));    private final Desensitized desensitized;    DesensitizedStrategy(Desensitized desensitized){        this.desensitized = desensitized;    }    public Desensitized getDesensitize() {        return desensitized;    }}

定义的脱敏字段中采用了Function函数来实现。

java复制代码/** * @author: jiangjs * @description: 脱敏接口 * @date: 2022/4/15 16:22 **/public interface Desensitized extends Function<String,String> {}
2.1.2 创建注解
java复制代码/** * @author: jiangjs * @description: 使用jackson进行数据脱敏 * @date: 2023/6/1 15:50 **/@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)@JacksonAnnotationsInside@JsonSerialize(using = JacksonDataDesensitized.class)public @interface JsonSensitive {    DesensitizedStrategy strategy();}

@JsonSerialize:实现数据的序列化,using则是实现序列化的具体类

2.1.3 创建序列化类

java复制代码/** * @author: jiangjs * @description: 通过jackson数据序列化来实现脱敏 * @date: 2023/6/1 15:54 **/@NoArgsConstructor@AllArgsConstructorpublic class JacksonDataDesensitized extends JsonSerializer<String> implements ContextualSerializer {    /**     * 脱敏数据     */    private DesensitizedStrategy strategy;    @Override    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {        gen.writeString(strategy.getDesensitize().apply(value));    }    @Override    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {        if (Objects.isNull(property)){           return prov.findNullValueSerializer(null);        }        //校验当前bean是否为String        if (Objects.equals(property.getType().getRawClass(),String.class)){            JsonSensitive sensitive = property.getAnnotation(JsonSensitive.class);            if (Objects.isNull(sensitive)){                sensitive = property.getContextAnnotation(JsonSensitive.class);            }            if (Objects.nonNull(sensitive)){                return new JacksonDataDesensitized(sensitive.strategy());            }        }        return prov.findValueSerializer(property.getType(), property);    }}

创建的序列化类继承了JsonSerializer类,指定了数据的类型,实现了ContextualSerializer上下文序列化来保证JsonSerializer不为空。

JsonGenerator:用来生成Json格式的内容,可以使用JsonFactory来生成一个实例。

2.1.4 使用注解

java复制代码/** * @author: jiangjs * @description: * @date: 2022/4/15 11:33 **/@Data@Accessors(chain = true)@EqualsAndHashCodepublic class UserInfo implements Serializable{    /**     * 主键     */    private Integer id;    /**     * 用户名     */    private String userName;    /**     * 昵称     */    @JsonSensitive(strategy = DesensitizedStrategy.USER_NAME)    private String nickName;    /**     * 手机号     */    @JsonSensitive(strategy = DesensitizedStrategy.PHONE)    private String phone;    /**     * 身份证号     */    @JsonSensitive(strategy = DesensitizedStrategy.ID_CARD)    private String idCard;    /**     * 创建时间     */    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",timezone = "GTM+8")    private Date createTime;    private static final long serialVersionUID = 1L;}

在实体中通过注解来指定各字段脱敏的类型。

2.1.4 测试

java复制代码/** * @author: jiangjs * @description: * @date: 2023/6/15 11:31 **/@RestController@RequestMapping("/desensitized")public class DesensitizedController {    @GetMapping("/getUserInfo.do")    public UserInfo getUserInfo(){        UserInfo userInfo = new UserInfo();        userInfo.setId(1).setUserName("zhangsan").setNickName("张三")                .setIdCard("450218199103458901")                .setPhone("13456789012");        return userInfo;    }}

输出:

从输出结果中,我们可以看到使用注解的字段已经实现数据脱敏。

2.2 通用枚举转换

需求:在实体中有时候会用到枚举,前端往往会传递一个值保存到数据,但是查询返回时往往又是对应的另外的值。因此做一个通用的枚举类型转换,查询数据时进行赋值。

2.2.1 创建注解

java复制代码/** * @author: jiangjs * @description: * @date: 2023/6/15 16:08 **/@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@JacksonAnnotationsInside@JsonSerialize(using = TypeChangeSerialize.class)public @interface TypeChange {     //值得替换 导出是{a_id,b_id} 导入反过来,所以只用写一个     String[] replace() default {};}

replace():仿照Easypoi中@Excel注解中的replace。

2.2.1 创建序列化实现类

java复制代码/** * @author: jiangjs * @description: * @date: 2023/6/15 16:21 **/@AllArgsConstructorpublic class TypeChangeSerialize extends JsonSerializer<Object> implements ContextualSerializer {    private final String[] annotationValues;    @Override    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {        String[] types = this.annotationValues;        Map<String, String> typeMap = new ConcurrentHashMap<>();        if (Objects.nonNull(types) && types.length > 0) {            for (String type : types) {                String[] s = type.split("_");                typeMap.put(s[0],s[1]);            }        }        gen.writeString(typeMap.get(String.valueOf(value)));    }    @Override    public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException {        if (Objects.isNull(property)){            return provider.findNullValueSerializer(null);        }        //校验当前bean是否为String或Integer        if (Objects.equals(property.getType().getRawClass(),String.class) ||                Objects.equals(property.getType().getRawClass(),Integer.class)){            TypeChange typeChange = property.getAnnotation(TypeChange.class);            if (Objects.isNull(typeChange)){                typeChange = property.getContextAnnotation(TypeChange.class);            }            if (Objects.nonNull(typeChange)){                return new TypeChangeSerialize(typeChange.replace());            }        }        return provider.findValueSerializer(property.getType(), property);    }}

annotationValues:定义接受注解replace的值。

new TypeChangeSerialize(typeChange.replace()):初始化序列化处理类,将获取到的replace()值赋值给annotationValues。

2.2.3 使用注解

java复制代码/** * @author: jiangjs * @description: * @date: 2022/4/15 11:33 **/@Data@Accessors(chain = true)@EqualsAndHashCodepublic class UserInfo implements Serializable{    /**     * 主键     */    private Integer id;    @TypeChange(replace = {"1_男","2_女"})    private Integer gender;    private static final long serialVersionUID = 1L;}

@TypeChange(replace = {"1_男","2_女"}):按照注解的规则,赋值replace。

2.2.4 测试

java复制代码/** * @author: jiangjs * @description: * @date: 2023/6/15 11:31 **/@RestController@RequestMapping("/desensitized")public class DesensitizedController {    @GetMapping("/getUserInfo.do")    public UserInfo getUserInfo(){        UserInfo userInfo = new UserInfo();        userInfo.setId(1).setGender(1);        return userInfo;    }}

输出:

从输出结果来看,gender赋值为1,序列化后返回的是对应的中文:男。也就是说,这个实现了我们需求,即字段进行了替换。

上述就是我在工作中使用jackson的一些应用,大家可以举一反三来实现自己的需求,例如:自定义时间格式化等等。

源码:github.com/lovejiashn/…

工欲善其事,必先利其器,小伙伴们撸起来吧。

原文链接:

标签: #java中文转码