spring boot 基於jackson 多型資料型別處理簡化rest api 開發
阿新 • • 發佈:2022-03-13
開發好可擴充套件的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