写了一个 Java 中过滤实体类字段的小项目

166 阅读6分钟

项目简介

一个轻量级的Java字段过滤工具,只需两个注解即可优雅地控制接口返回字段。

为何开发这个项目?

​ Java 在平时开发中,我们难免会遇到不想返回某些字段的情况,又或者某些字段的值过于敏感不方便展示,这种情况我们通常会新建一个类,包装成一个视图对象。那么随着业务的增长,视图对象会越来越多,所以开发了这个项目。

会不会有使用成本?

​ 不会,仅需配置 2 个注解即可完成所有功能。并且可以将粒度细分到方法,返回同一对象,A 方法返回所有字段,B 方法返回需要的字段。

GitHub 地址:sensitive-field-filter

快速开始

引入依赖

目前仅支持 Spring Boot 项目,引入依赖

<dependency>
  <groupId>io.github.soora33</groupId>
  <artifactId>sft</artifactId>
  <version>1.0.0</version>
</dependency>

注解说明

注解共有三个,其中 @SftFilter 加在需要过滤的实体类字段上,@SftObjectFilter@SftResponseFilter 根据方法的返回值选择其中一个。注解详情如下:

@SftFilter:配置在实体类上需要过滤的字段

可配置项:

value:过滤后的字段值,默认为 null

@SftObjectFilter: 配置在方法上,适用于直接返回对象,配置后会对该方法的返回值按照 SftFilter配置的字段进行过滤

可配置项:

entity:方法的返回值类型

preserveField:是否需要保留字段,默认为 true

@SftResponseFilter:配置在方法上,适用于封装格式对象,配置后会对该方法的返回值按照 SftFilter配置的字段进行过滤(默认获取封装对象中 data 中的对象)

可配置项:

entity:方法的返回值类型

key:封装数据体中存储数据的字段名,默认为 data

preserveField:是否需要保留字段,默认为 true

对于 @SftObjectFilter:用在直接返回视图对象的方法

对于 @SftResponseFilter:用在返回经过统一格式封装的对象,如 Result.success(data),AjaxResult.success(data)等等

⚠️⚠️⚠️ 注意:如果配置 preserveField 为 false,则会将返回值中的实体类对象转为 LinkedHashMap。因为去除过滤字段的实现方式是通过将非过滤字段加入到 Map 内实现的。如果对业务有影响,请不要使用!!!

如果看不明白没关系,下面提供了示例,结合示例更容易明白。

使用示例

实体类说明:

以下面 Person 举例说明,并设置 name 字段为 null,email 为 Nah

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String id;
    @SftFilter // 默认将该字段值设为 null
    private String name;
    @SftFilter(value = "Nah") // 修改默认值为 Nah
    private String email;
}

SftObjectFilter

SftObjectFilter 注解用于对象的过滤,可配置的参数有 2 个:

entity:方法的返回值类型

preserveField:是否需要保留字段,默认为 true

下面介绍常见的两种场景

1. 返回单个对象
// 保留字段
@SftObjectFilter(entity = Person.class)
public Person getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  return person;
}

// 返回值类型以及返回值
class com.sora.domain.Person
Person(id=1, name=null, email=Nah)
  
  
// 不保留字段
@SftObjectFilter(entity = Person.class, preserveField = false)
public Person getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  return person;
}

// 返回值类型以及返回值
class java.util.LinkedHashMap
{id=1}
2. 返回集合类型
// 保留字段
@SftObjectFilter(entity = Person.class)
public List<Person> getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  Person person2 = new Person("2","nayuta","nayuta@email.com");
  Person person3 = new Person("3","aznayu","aznayu@email.com");
  ArrayList<Person> list = Lists.newArrayList(person, person2, person3);
  return list;
}

// 返回值类型以及返回值
class java.util.ArrayList
[Person(id=1, name=null, email=Nah), Person(id=2, name=null, email=Nah), Person(id=3, name=null, email=Nah)]
 
  
// 不保留字段
@SftObjectFilter(entity = Person.class, preserveField = false)
public List<Person> getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  Person person2 = new Person("2","nayuta","nayuta@email.com");
  Person person3 = new Person("3","aznayu","aznayu@email.com");
  ArrayList<Person> list = Lists.newArrayList(person, person2, person3);
  return list;
}

// 返回值类型以及返回值
class java.util.ArrayList
[{id=1}, {id=2}, {id=3}]

SftResponseFilter

在使用 SftResponseFilter 之前,我先来介绍一下目前 Java 项目的统一返回值格式,大体上分为两种:

第一种是以对象的形式,通过字段存储数据,例如:

public class AjaxResult implements Serializable {
    @Serial
    private static final long serialVersionUID = -7126327333321005351L;
    private String msg;
    private Integer code;
    private Object data;

    // get...set...方法

    private static AjaxResult rest(Object object) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setMsg("success");
        ajaxResult.setData(object);
        ajaxResult.setCode(200);
        return ajaxResult;
    }

    public static AjaxResult success(Object object) {
        return rest(object);
    }
}

第二种则是 Map 的形式存储数据,内部通过 put 的方式存储数据,例如:

public class AjaxResultMap extends HashMap<Object,Object> implements Serializable {
    @Serial
    private static final long serialVersionUID = -7127333321005351L;
    private static String msg;
    private Integer code;
    private Object data;


    private AjaxResultMap(Object object) {
        super.put("msg", "success");
        super.put("data", object);
        super.put("code", 200);
    }

    public static AjaxResultMap success(Object object) {
        return new AjaxResultMap(object);
    }
}

无论你使用的是哪种格式包装数据,项目内部都对其进行了实现,使用方法上是完全一样的!这里仅使用其中一种进行演示。下面是具体的使用方法。

SftResponseFilter 注解用于被封装格式封装的数据,可配置的参数有 3 个:

entity:方法的返回值类型

key:封装数据体中存储数据的字段名,默认为 data

preserveField:是否需要保留字段,默认为 true

1. 返回单个对象
// 保留字段
@SftResponseFilter(entity = AjaxResult.class)
public AjaxResult getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  return AjaxResult.success(person);
}

// 返回值类型以及返回值
class com.sora.result.AjaxResult
AjaxResult(code=200, msg=success, data=Person(id=1, name=null, email=Nah))
  
  
// 不保留字段
@SftResponseFilter(entity = AjaxResult.class, preserveField = false)
public AjaxResult getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  return AjaxResult.success(person);
}

// 返回值类型以及返回值
class com.sora.result.AjaxResult
AjaxResult(code=200, msg=success, data={id=1})
2. 返回集合类型
// 保留字段
@SftResponseFilter(entity = AjaxResult.class)
public AjaxResult getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  Person person2 = new Person("2","nayuta","nayuta@email.com");
  Person person3 = new Person("3","aznayu","aznayu@email.com");
  ArrayList<Person> list = Lists.newArrayList(person, person2, person3);
  return AjaxResult.success(list);
}

// 返回值类型以及返回值
class com.sora.result.AjaxResult
AjaxResult(code=200, msg=success, data=[Person(id=1, name=null, email=Nah), Person(id=2, name=null, email=Nah), Person(id=3, name=null, email=Nah)])
  
 // 不保留字段
@SftResponseFilter(entity = AjaxResult.class, preserveField = false)
public AjaxResult getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  Person person2 = new Person("2","nayuta","nayuta@email.com");
  Person person3 = new Person("3","aznayu","aznayu@email.com");
  ArrayList<Person> list = Lists.newArrayList(person, person2, person3);
  return AjaxResult.success(list);
}

// 返回值类型以及返回值
class com.sora.result.Result
Result(code=200, msg=操作成功, data=[{id=1}, {id=2}, {id=3}])
3. 自定义 key 演示

如果说你存储数据的字段不叫 data ,那么使用 key 配置正确的字段名就可以,例如小明的项目使用类名为 Result 作为封装对象,并且使用 body 字段存储数据,那么可以这么写:

@SftResponseFilter(entity = Result.class, key = "body")
public Result getPerson() {
  Person person = new Person("1","azki","azki@email.com");
  Person person2 = new Person("2","nayuta","nayuta@email.com");
  Person person3 = new Person("3","aznayu","aznayu@email.com");
  ArrayList<Person> list = Lists.newArrayList(person, person2, person3);
  return Result.success(list);
}

结束语

欢迎各位提出建议,后续也会优化性能不断扩展新功能

更多知识请移步个人博客:33sora.com