spring boot 基于jackson 多态数据类型处理简化rest api 开发
开发好可扩展的rest api 是一门技术,同时开发灵活扩展的rest api 也是比较费事的,很多时候
我们为了业务开发了特别多的rest api,造成系统的维护以及使用都很复杂,graphql 是一种不错的
解决方法(同时业界也有类似通用查询处理),以下是一个简单的基于jackson 多态数据处理简单
开发的一个玩法,可以参考
核心说明
实际上是基于约定的,包含了实体处理(基于jackson多态处理),以及服务层处理(很多时候我们是需要包含依赖的ioc)
项目准备
- 项目结构
就是一个标准的spring boot maven 项目
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── dalong
│ │ └── jacksonapp
│ │ ├── ContextUtil.java
│ │ ├── ExceptionHandlers.java
│ │ ├── FirstMyService.java
│ │ ├── FirstUser.java
│ │ ├── JacksonappApplication.java
│ │ ├── MyApi.java
│ │ ├── MyService.java
│ │ ├── MyUser.java
│ │ ├── SecondMyService.java
│ │ └── SecondUser.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
- pom.xml
基于starter生成的,主要就是一个web 项目 - jackson 实体定义说明
// 基于JsonTypeInfo 定义类型
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
public interface MyUser<T> {
String token(); // 实体demo 方法
T instance(); // 返回实体类型
Class serviceType(); // 方便实体需要的服务类型定义(比如不同实体会有不同的service,dao层。。。。)
}
子类
FirstUser
@Data
public class FirstUser implements MyUser<FirstUser>{
private String name;
private int age;
@Override
public String token() {
return String.format("FirstUser-%d-%s",this.age,this.name);
}
@Override
public FirstUser instance() {
return this;
}
@Override
public Class serviceType() {
return FirstMyService.class; // 使用FirstMyService服务处理bean
}
}
SecondUser
@Data
public class SecondUser implements MyUser<SecondUser>{
private String name;
private int age;
@Override
public String token() {
return String.format("SecondUser-%d-%s",this.age,this.name);
}
@Override
public SecondUser instance() {
return this;
}
@Override
public Class serviceType() {
return SecondMyService.class; // 使用SecondMyService 服务处理bean
}
}
实体的服务处理契约(处理具体实体的service)
public interface MyService<T extends MyUser> {
default String demo(T t){
return t.token();
}
}
FirstUser 的服务处理实现
@Service
public class FirstMyService implements MyService<FirstUser> {
}
SecondUser 的service
@Service
public class SecondMyService implements MyService<SecondUser> {
}
服务工具类ContextUtil,方便基于bean 类型获取特定实体的服务处理bean
public class ContextUtil {
public static <T extends MyService> T getBean(Class<T> cls) {
return JacksonappApplication.applicationContext.getBean(cls);
}
}
通用服务api 处理入口
@RestController
public class MyApi {
@RequestMapping(value = {"/demoapp"})
public Object demo(@RequestBody MyUser myUser){
// 通过实体可以找到servie,然后基于service 的方法处理对于实体的业务逻辑
return ContextUtil.getBean(myUser.serviceType()).demo(myUser);
}
}
spring boot 入口
通过CommandLineRunner 进行数据类型的注册,没有直接使用JsonSubTypes 注解是因为直接基于直接的话,代码就固化了
使用CommandLineRunner的好处是可以灵活的扩展服务(比如多模块,插件化处理。。。)
@SpringBootApplication
public class JacksonappApplication {
public static ApplicationContext applicationContext;
@Bean
public CommandLineRunner commandLineRunner(ObjectMapper objectMapper){
return args -> {
objectMapper.registerSubtypes(new NamedType(FirstUser.class, "first"));
objectMapper.registerSubtypes(new NamedType(SecondUser.class, "second"));
};
}
public static void main(String[] args) {
applicationContext= SpringApplication.run(JacksonappApplication.class, args);
}
}
使用效果
- 访问first 类型的
curl --location --request POST 'http://localhost:8080/demoapp' \
--header 'Content-Type: application/json' \
--data-raw '{
"type":"first",
"age":333,
"name":"dalong"
}'
效果
- 访问second 类型
curl --location --request POST 'http://localhost:8080/demoapp' \
--header 'Content-Type: application/json' \
--data-raw '{
"type":"second",
"age":333,
"name":"dalong"
}'
说明
以上是一个简单的实践玩法,我们基于此可以实现一个入口灵活的rest api 处理,比如适合post 类型的处理(比如graphql 就是完全基于post 进行数据处理的,包装了查询以及修改数据),完整代码可以参考github 代码
参考资料
https://www.cnblogs.com/rongfengliang/p/15999843.html
https://github.com/rongfengliang/spring-boot-jackson