From d43abed086d051d5097b6cd11da4739b8464a96c Mon Sep 17 00:00:00 2001
From: andanyang <1218853253@qq.com>
Date: Wed, 1 Nov 2023 13:38:14 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E4=BC=98=E9=9B=85?=
=?UTF-8?q?=E7=9A=84=E4=BD=BF=E7=94=A8=E6=9E=9A=E4=B8=BE=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
enum-spring-boot-starter/pom.xml | 59 ++++++++++++++++
.../EnumConverterAutoConfigure.java | 49 ++++++++++++++
.../EnumConverterWebConfigure.java | 24 +++++++
.../framework/converter/EnumConverter.java | 16 +++++
.../factory/EnumConverterFactory.java | 67 +++++++++++++++++++
.../converter/impl/EnumValueConverter.java | 53 +++++++++++++++
.../converter/impl/JackosnEnumConverter.java | 55 +++++++++++++++
.../main/resources/META-INF/spring.factories | 2 +
.../java/com/admin4j/framework/AppTest.java | 14 ++++
.../admin4j/framework/EnumConverterTest.java | 40 +++++++++++
.../framework/constant/UserStatus.java | 34 ++++++++++
.../controller/EnumConverterController.java | 21 ++++++
pom.xml | 1 +
13 files changed, 435 insertions(+)
create mode 100644 enum-spring-boot-starter/pom.xml
create mode 100644 enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterAutoConfigure.java
create mode 100644 enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterWebConfigure.java
create mode 100644 enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/EnumConverter.java
create mode 100644 enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/factory/EnumConverterFactory.java
create mode 100644 enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/EnumValueConverter.java
create mode 100644 enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/JackosnEnumConverter.java
create mode 100644 enum-spring-boot-starter/src/main/resources/META-INF/spring.factories
create mode 100644 enum-spring-boot-starter/src/test/java/com/admin4j/framework/AppTest.java
create mode 100644 enum-spring-boot-starter/src/test/java/com/admin4j/framework/EnumConverterTest.java
create mode 100644 enum-spring-boot-starter/src/test/java/com/admin4j/framework/constant/UserStatus.java
create mode 100644 enum-spring-boot-starter/src/test/java/com/admin4j/framework/controller/EnumConverterController.java
diff --git a/enum-spring-boot-starter/pom.xml b/enum-spring-boot-starter/pom.xml
new file mode 100644
index 0000000..5e2e245
--- /dev/null
+++ b/enum-spring-boot-starter/pom.xml
@@ -0,0 +1,59 @@
+
+ 4.0.0
+
+ com.admin4j
+ framework
+ 0.8.0
+
+
+ com.admin4j.framework
+ enum-spring-boot-starter
+ jar
+
+ enum-spring-boot-starter
+ 优雅的使用枚举参数
+
+
+
+ UTF-8
+
+
+
+
+ com.baomidou
+ mybatis-plus-annotation
+ 3.5.3.1
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.13.5
+ provided
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure-processor
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+
diff --git a/enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterAutoConfigure.java b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterAutoConfigure.java
new file mode 100644
index 0000000..a4205fa
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterAutoConfigure.java
@@ -0,0 +1,49 @@
+package com.admin4j.framework.autoconfigure;
+
+import com.admin4j.framework.converter.EnumConverter;
+import com.admin4j.framework.converter.impl.EnumValueConverter;
+import com.admin4j.framework.converter.impl.JackosnEnumConverter;
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.convert.ConversionService;
+
+import java.util.List;
+
+/**
+ * @author andanyang
+ * @since 2023/11/1 11:37
+ */
+public class EnumConverterAutoConfigure {
+
+ @Bean
+ @Order(1)
+ @ConditionalOnClass(name = "com.baomidou.mybatisplus.annotation.EnumValue")
+ public EnumValueConverter enumValueConverter() {
+ return new EnumValueConverter(EnumValue.class);
+ }
+
+ @Bean
+ @Order(2)
+ @ConditionalOnClass(name = "com.fasterxml.jackson.annotation.JsonValue")
+ public EnumValueConverter jsonEnumValueConverter() {
+ return new EnumValueConverter(JsonValue.class);
+ }
+
+ @Bean
+ @Order(3)
+ @ConditionalOnClass(name = "com.fasterxml.jackson.annotation.JsonCreator")
+ public JackosnEnumConverter jackosnEnumConverter(ObjectProvider objectProvider) {
+ return new JackosnEnumConverter(objectProvider);
+ }
+
+ @Bean
+ @ConditionalOnBean(EnumConverter.class)
+ public EnumConverterWebConfigure enumConverterWebConfigure(List enumConverters) {
+ return new EnumConverterWebConfigure(enumConverters);
+ }
+}
diff --git a/enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterWebConfigure.java b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterWebConfigure.java
new file mode 100644
index 0000000..c2c4de7
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/autoconfigure/EnumConverterWebConfigure.java
@@ -0,0 +1,24 @@
+package com.admin4j.framework.autoconfigure;
+
+import com.admin4j.framework.converter.EnumConverter;
+import com.admin4j.framework.converter.factory.EnumConverterFactory;
+import lombok.RequiredArgsConstructor;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * @author andanyang
+ * @since 2023/11/1 13:26
+ */
+@RequiredArgsConstructor
+public class EnumConverterWebConfigure implements WebMvcConfigurer {
+
+ private final List enumConverters;
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ registry.addConverterFactory(new EnumConverterFactory(enumConverters));
+ }
+}
diff --git a/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/EnumConverter.java b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/EnumConverter.java
new file mode 100644
index 0000000..b940e00
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/EnumConverter.java
@@ -0,0 +1,16 @@
+package com.admin4j.framework.converter;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * 枚举转化器
+ *
+ * @author andanyang
+ * @since 2023/11/1 9:57
+ */
+public interface EnumConverter {
+
+
+ @Nullable
+ T convert(String source, Class enumType);
+}
diff --git a/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/factory/EnumConverterFactory.java b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/factory/EnumConverterFactory.java
new file mode 100644
index 0000000..256ddc0
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/factory/EnumConverterFactory.java
@@ -0,0 +1,67 @@
+package com.admin4j.framework.converter.factory;
+
+
+import com.admin4j.framework.converter.EnumConverter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author andanyang
+ * @since 2023/11/1 9:22
+ */
+@RequiredArgsConstructor
+public class EnumConverterFactory implements ConverterFactory {
+
+ @SuppressWarnings("rawtypes")
+ private static final Map CONVERTERS = new ConcurrentHashMap<>(64);
+ private final List enumConverters;
+
+ /**
+ * Get the converter to convert from S to target type T, where T is also an instance of R.
+ *
+ * @param targetType the target type to convert to
+ * @return a converter from S to T
+ */
+ @Override
+ public Converter getConverter(Class targetType) {
+ return CONVERTERS.computeIfAbsent(targetType, (key) -> new StringToEnum(targetType, enumConverters));
+ }
+
+ private static class StringToEnum implements Converter {
+
+ private final Class enumType;
+ private final List enumConverters;
+
+ StringToEnum(Class enumType, List enumConverters) {
+ this.enumType = enumType;
+ this.enumConverters = enumConverters;
+ }
+
+ /**
+ * Convert the source object of type {@code S} to target type {@code T}.
+ *
+ * @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
+ * @return the converted object, which must be an instance of {@code T} (potentially {@code null})
+ * @throws IllegalArgumentException if the source cannot be converted to the desired target type
+ */
+ @Override
+ public T convert(String source) {
+ if (source.isEmpty()) {
+ // It's an empty enum identifier: reset the enum value to null.
+ return null;
+ }
+ for (EnumConverter enumConverter : enumConverters) {
+ T convert = enumConverter.convert(source, enumType);
+ if (convert != null) {
+ return convert;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/EnumValueConverter.java b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/EnumValueConverter.java
new file mode 100644
index 0000000..9e08056
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/EnumValueConverter.java
@@ -0,0 +1,53 @@
+package com.admin4j.framework.converter.impl;
+
+
+import com.admin4j.framework.converter.EnumConverter;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+/**
+ * 使用 EnumValue注解 字段进行匹配
+ *
+ * @author andanyang
+ * @since 2023/11/1 10:07
+ */
+// @Service
+public class EnumValueConverter implements EnumConverter {
+
+ private final Class targetAnnotation;
+
+ public EnumValueConverter(Class targetAnnotation) {
+ this.targetAnnotation = targetAnnotation;
+ }
+
+ @Override
+ public T convert(String source, Class enumType) {
+
+ // 被注解的值字段
+ Field valueField = null;
+ Field[] declaredFields = enumType.getDeclaredFields();
+ for (Field field : declaredFields) {
+ Annotation annotation = field.getAnnotation(targetAnnotation);
+ if (annotation != null) {
+ valueField = field;
+ }
+ }
+
+ if (valueField != null) {
+ T[] enumConstants = enumType.getEnumConstants();
+ // 查找枚举
+ for (T e : enumConstants) {
+ try {
+ valueField.setAccessible(true);
+ if (String.valueOf(valueField.get(e)).equals(source)) {
+ return e;
+ }
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/JackosnEnumConverter.java b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/JackosnEnumConverter.java
new file mode 100644
index 0000000..287fe3f
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/java/com/admin4j/framework/converter/impl/JackosnEnumConverter.java
@@ -0,0 +1,55 @@
+package com.admin4j.framework.converter.impl;
+
+
+import com.admin4j.framework.converter.EnumConverter;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.core.convert.ConversionService;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * 使用 @JsonCreator 方法注解静态字段进行匹配
+ *
+ * @author andanyang
+ * @since 2023/11/1 10:07
+ */
+
+@RequiredArgsConstructor
+public class JackosnEnumConverter implements EnumConverter {
+
+ private final ObjectProvider conversionServiceObjectProvider;
+
+ @Override
+ public T convert(String source, Class enumType) {
+
+ Method[] declaredMethods = enumType.getDeclaredMethods();
+
+ for (Method method : declaredMethods) {
+ if (method.isAnnotationPresent(JsonCreator.class)) {
+ try {
+ Object invoke;
+ Class> parameterType = method.getParameterTypes()[0];
+ Object cast = conversionServiceObjectProvider.getIfAvailable().convert(source, parameterType);
+ invoke = method.invoke(enumType, cast);
+
+ if (invoke == null) {
+ return null;
+ }
+ if (!enumType.isInstance(invoke)) {
+ throw new IllegalArgumentException("JsonCreator error: target " + enumType.getCanonicalName() + " but get " + invoke.getClass().getCanonicalName());
+ }
+
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/enum-spring-boot-starter/src/main/resources/META-INF/spring.factories b/enum-spring-boot-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..f529124
--- /dev/null
+++ b/enum-spring-boot-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.admin4j.framework.autoconfigure.EnumConverterAutoConfigure
\ No newline at end of file
diff --git a/enum-spring-boot-starter/src/test/java/com/admin4j/framework/AppTest.java b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/AppTest.java
new file mode 100644
index 0000000..396d384
--- /dev/null
+++ b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/AppTest.java
@@ -0,0 +1,14 @@
+package com.admin4j.framework;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Unit test for simple App.
+ */
+@SpringBootApplication
+public class AppTest {
+ public static void main(String[] args) {
+ SpringApplication.run(AppTest.class);
+ }
+}
diff --git a/enum-spring-boot-starter/src/test/java/com/admin4j/framework/EnumConverterTest.java b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/EnumConverterTest.java
new file mode 100644
index 0000000..58907ba
--- /dev/null
+++ b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/EnumConverterTest.java
@@ -0,0 +1,40 @@
+package com.admin4j.framework;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+/**
+ * @author andanyang
+ * @since 2023/11/1 11:58
+ */
+@SpringBootTest(classes = AppTest.class)
+@AutoConfigureMockMvc
+public class EnumConverterTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @ParameterizedTest
+ @ValueSource(strings = {"3", "2", "1"})
+ void genderIdCode(String userStatus) throws Exception {
+ final String result = mockMvc.perform(
+ MockMvcRequestBuilders.get("/UserStatus")
+ .param("userStatus", userStatus)
+ )
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcResultHandlers.print())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ Assertions.assertEquals(result, userStatus);
+ }
+}
diff --git a/enum-spring-boot-starter/src/test/java/com/admin4j/framework/constant/UserStatus.java b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/constant/UserStatus.java
new file mode 100644
index 0000000..1011536
--- /dev/null
+++ b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/constant/UserStatus.java
@@ -0,0 +1,34 @@
+package com.admin4j.framework.constant;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author andanyang
+ * @since 2023/11/1 9:12
+ */
+@Getter
+@AllArgsConstructor
+public enum UserStatus {
+
+ NORMAL(1, "正常"),
+ FREEZE(2, "冻结");
+
+ @EnumValue
+ @JsonValue
+ private final int value;
+ private final String text;
+
+ @JsonCreator
+ public static UserStatus valueOf(int value) {
+ for (UserStatus status : UserStatus.values()) {
+ if (status.value == value) {
+ return status;
+ }
+ }
+ return null;
+ }
+}
diff --git a/enum-spring-boot-starter/src/test/java/com/admin4j/framework/controller/EnumConverterController.java b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/controller/EnumConverterController.java
new file mode 100644
index 0000000..5fa1e49
--- /dev/null
+++ b/enum-spring-boot-starter/src/test/java/com/admin4j/framework/controller/EnumConverterController.java
@@ -0,0 +1,21 @@
+package com.admin4j.framework.controller;
+
+import com.admin4j.framework.constant.UserStatus;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author andanyang
+ * @since 2023/11/1 13:12
+ */
+@RestController
+@RequestMapping
+public class EnumConverterController {
+ @RequestMapping("UserStatus")
+ public UserStatus userStatus(@RequestParam UserStatus userStatus) {
+
+ return userStatus;
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index ebbb84f..059f0d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,7 @@
tenant-spring-boot-starter
feign-spring-boot-starter
ttl-spring-boot-starter
+ enum-spring-boot-starter
0.9.0-SNAPSHOT