Java 实现枚举类的统一管理(自定义starter)
前言
在我们的java项目中,有很多页面。其中,筛选条件也是很复杂,有的下拉框是枚举类的映射,有的下拉框是查了数据库,比如字典表,型号等信息。
那么,如果新增一个下拉框,后端就得对前端提供一个下拉的接口。基于这个场景,我这边设计并实现了一个自定义的starter。它可以:
- 统一管理枚举,可以让你只提供一个接口给前端。
- 兼容非枚举类的下拉场景。如果不涉及自定义的筛选条件,则可以共用一个公共下拉接口,如果涉及到自定义的筛选条件,则还是需要单独给出新的下拉接口。但是内部实现可以简化、以及复用之前的部分代码。
正文
一、项目结构
本文涉及到两个java项目模块。一个是公共枚举starter ,即【commons-enums-starter】。另一个是用于测试这个starter的模块,即【admin-starter】,引入了spring-boot-starter-web依赖,并定义了简单的配置和controller来测试公共枚举starter。
项目整体使用 java8 和 springboot 2.7.6版本。
1.1 commons-enums-starter 结构

1.2 admin-starter 结构

二、完整代码
2.1 commons-enum-starter
2.1.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.feng.commons</groupId>
<artifactId>commons-enums-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.1.2 resources/META-INF/spring.facories
配置启用自动配置类:枚举自动配置类。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.feng.commons.ec.EnumAutoConfiguration
2.1.3 IBaseEnum
基础枚举接口,定义常用的静态工具方法,如通过枚举类的Class 获取对应的 EnumSet集合。
在接口 IBaseEnum<T> 定义时,T 表示枚举编码的类型,常见的是整数或字符串。 T getValue() 方法则是获取枚举编码, String getDesc() 获取描述。
在枚举作为下拉的接口源数据时,枚举编码会作为key,描述作为 value返回。
package org.feng.commons.ec;
import java.util.EnumSet;
/**
* 枚举接口:使用本组件管理的枚举,都需要实现这个接口
*
* @param <T> 枚举值类型
*/
public interface IBaseEnum<T> {
T getValue();
String getDesc();
/**
* 获取枚举集合
*
* @param enumClass 枚举类
* @param <E> 枚举类型
* @return 枚举集合
*/
static <E extends Enum<E>> EnumSet<E> getEnums(Class<E> enumClass) {
return EnumSet.allOf(enumClass);
}
/**
* 获取枚举
*
* @param value 枚举值
* @param enumClass 枚举类
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 枚举
*/
static <E extends Enum<E> & IBaseEnum<T>, T> E getEnum(T value, Class<E> enumClass) {
return getEnums(enumClass).stream().filter(e -> e.getValue().equals(value)).findFirst().orElse(null);
}
/**
* 获取枚举描述
*
* @param value 枚举值
* @param enumClass 枚举类
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 枚举描述
*/
static <E extends Enum<E> & IBaseEnum<T>, T> String getEnumDesc(T value, Class<E> enumClass) {
return getEnums(enumClass).stream().filter(e -> e.getValue().equals(value)).map(IBaseEnum::getDesc).findFirst().orElse(null);
}
}
2.1.4 CustomerRegisterEnumConfigure
自定义配置枚举的注册信息。
本starter 支持配置扫描包,注册枚举类。
同时也使用这个接口来作为一个扩展,即你想修改枚举的注册信息,或者查询时的筛选条件,可以实现这个接口,然后将实现类注入的spring容器就会自动生效。
package org.feng.commons.ec;
/**
* 枚举注册配置
*/
@FunctionalInterface
public interface CustomerRegisterEnumConfigure {
/**
* 注册枚举信息,以及过滤条件
*/
void register();
}
2.1.5 ScanUtil
扫描工具类,提供通过包名扫描获取指定的枚举类。
package org.feng.commons.ec;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.ClassMetadata;
import java.util.HashSet;
import java.util.Set;
/**
* 扫描工具类
*/
@Slf4j
public class ScanUtil {
private ScanUtil() {
}
/**
* 扫描指定包下实现特定接口的枚举类
*
* @param batchPackages 扫描包路径
* @return 符合条件的枚举类集合
*/
public static <E extends Enum<E> & IBaseEnum<T>, T> Set<Class<E>> scanEnumClasses(String[] batchPackages) {
Set<Class<E>> enumClasses = new HashSet<>();
for (String singlePackage : batchPackages) {
enumClasses.addAll(scanEnumClasses(singlePackage));
}
log.info("扫描到枚举类:{}", enumClasses);
return enumClasses;
}
@SuppressWarnings("unchecked")
private static <E extends Enum<E> & IBaseEnum<T>, T> Set<Class<E>> scanEnumClasses(String enumPackage) {
Set<Class<E>> enumClasses = new HashSet<>();
try {
Set<BeanDefinition> candidates = getBeanDefinitions(enumPackage);
for (BeanDefinition candidate : candidates) {
try {
Class<?> clazz = Class.forName(candidate.getBeanClassName());
if (IBaseEnum.class.isAssignableFrom(clazz)) {
enumClasses.add((Class<E>) clazz);
}
} catch (ClassNotFoundException e) {
// 忽略无法加载的类
log.error("扫描枚举类异常", e);
}
}
} catch (Exception e) {
// 处理扫描异常
log.error("扫描枚举类异常", e);
}
return enumClasses;
}
/**
* 获取指定包下的类定义: 筛选条件是该类实现了 IBaseEnum.class
*
* @param enumPackage 包名
* @return 类定义集合
*/
private static Set<BeanDefinition> getBeanDefinitions(String enumPackage) {
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
// 添加过滤器,只扫描枚举类
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
try {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 检查是否实现了指定接口 interfaceClass
String className = classMetadata.getClassName();
Class<?> clazz = Class.forName(className);
return IBaseEnum.class.isAssignableFrom(clazz);
} catch (Exception e) {
return false;
}
});
return scanner.findCandidateComponents(enumPackage);
}
}
2.1.6 NonEnumObjectHandler
非枚举对象处理器。顾名思义,就是支持对非枚举对象的处理,比如我们有查数据库中某个表,然后根据这个表的数据构建一个下拉接口。就需要用到它。
package org.feng.commons.ec;
import org.springframework.lang.Nullable;
import java.util.List;
/**
* 非枚举对象处理器
*/
public interface NonEnumObjectHandler<T, QueryCriteria> {
/**
* 处理非枚举对象数据,并转换为枚举响应类型
*
* @param criteria 查询条件
* @return 枚举响应集合
*/
List<EnumResponse<T>> handle(@Nullable QueryCriteria criteria);
/**
* 获取处理器名称:建议重写,名字在全局不能重复
*
* @return 处理器名称
*/
default String getHandlerName() {
return this.getClass().getSimpleName();
}
/**
* 获取查询条件类:建议在实现类中重写,因为实现类已经能确定类型
*
* @return 查询条件类
*/
default Class<?> getQueryCriteriaClass() {
// 获取泛型中第二个参数的class类型
return ((Class<?>) ((java.lang.reflect.ParameterizedType) getClass().getGenericInterfaces()[0]).getActualTypeArguments()[1]);
}
}
2.1.7 EnumResponse
枚举响应,也可以直接作为下拉的主要数据结构。
package org.feng.commons.ec;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 枚举响应
*
* @param <T> 枚举值类型
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EnumResponse<T> implements Serializable {
private static final long serialVersionUID = 7765499629374479258L;
/**
* 枚举值
*/
private T value;
/**
* 枚举描述
*/
private String desc;
}
2.1.8 EnumRegister
对外提供枚举注册的方法,有两种注册:
1、注册枚举set,也就是枚举常量的注册。通常通过包扫描的方式就可以完成。也就是在使用时,按照本文的配置方式即可完成注册。注册的枚举set是全量的枚举常量。
2、注册枚举查询条件。这个是因为有些时候我们的下拉会有筛选条件。当然,这个条件只有在返回到响应的时候才生效。
package org.feng.commons.ec;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
/**
* 枚举注册器
*/
public class EnumRegister {
/**
* 注册枚举set
*
* @param enumClass 枚举类
* @param <E> 枚举类型
* @param <T> 枚举值类型
*/
public static <E extends Enum<E> & IBaseEnum<T>, T> void registerEnumSet(Class<E> enumClass) {
Objects.requireNonNull(enumClass);
// 注册枚举
EnumSetRegister.register(enumClass);
}
/**
* 注册枚举查询条件
*
* @param enumClass 枚举类
* @param predicate 查询筛选条件,可以为空,为空时表示无筛选条件
* @param <E> 枚举类型
* @param <T> 枚举值类型
*/
public static <E extends Enum<E> & IBaseEnum<T>, T> void registerQueryPredicate(Class<E> enumClass, Predicate<E> predicate) {
Objects.requireNonNull(enumClass);
// 注册查询筛选条件
EnumPredicateRegister.register(enumClass.getSimpleName(), predicate);
}
/**
* 获取枚举集合
*
* @param enumName 枚举名称
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 枚举集合
*/
@SuppressWarnings("all")
public static <E extends Enum<E> & IBaseEnum<T>, T> EnumSet<E> getEnumSet(String enumName) {
return (EnumSet<E>) EnumSetRegister.getEnumSet(enumName);
}
/**
* 获取筛选条件
*
* @param enumName 枚举名称
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 筛选条件
*/
@SuppressWarnings("all")
public static <E extends Enum<E> & IBaseEnum<T>, T> Predicate<E> getPredicate(String enumName) {
return (Predicate<E>) EnumPredicateRegister.getPredicate(enumName);
}
/**
* 扫描包批量注册枚举,无筛选条件
*
* @param packageName 枚举所在的包名
*/
public static void scanPackageAndRegister(String... packageName) {
ScanUtil.scanEnumClasses(packageName)
.forEach((enumClass) -> {
// 注册枚举set
EnumRegister.registerEnumSet(enumClass);
// 注册查询筛选条件
EnumRegister.registerQueryPredicate(enumClass, null);
});
}
/**
* 枚举集合注册器
*/
private static class EnumSetRegister {
private static final Map<String, EnumSet<?>> ENUM_SET_MAP = new HashMap<>();
private static EnumSet<?> getEnumSet(String enumName) {
return ENUM_SET_MAP.get(enumName);
}
private static <E extends Enum<E> & IBaseEnum<T>, T> void register(Class<E> enumClass) {
String enumName = enumClass.getSimpleName();
// 如果之前注册过,则不重复注册EnumSet
if (isRegistered(enumName)) {
return;
}
// 注册enumSet
ENUM_SET_MAP.put(enumName, IBaseEnum.getEnums(enumClass));
}
/**
* 判断枚举是否已注册
*
* @param enumName 枚举名称
* @return 是否已注册
*/
private static boolean isRegistered(String enumName) {
return ENUM_SET_MAP.containsKey(enumName);
}
private EnumSetRegister() {
}
}
/**
* 筛选条件注册器
*/
private static class EnumPredicateRegister {
private static final Map<String, Predicate<?>> PREDICATE_MAP = new HashMap<>();
private static Predicate<?> getPredicate(String enumName) {
return PREDICATE_MAP.get(enumName);
}
private static <E extends Enum<E> & IBaseEnum<T>, T> void register(String enumName, Predicate<E> predicate) {
// 如果筛选条件为空,则默认为true
if (predicate == null) {
predicate = e -> true;
}
PREDICATE_MAP.put(enumName, predicate);
}
private EnumPredicateRegister() {
}
}
}
2.1.9 EnumConvertor
枚举转换器,用于将EnumSet转换为 EnumResponse。
并支持将普通的 List<E> 集合转换为 EnumResponse,这个是对应到非枚举对象场景的。
package org.feng.commons.ec;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 枚举转换器
*/
@Component
public class EnumConvertor {
/**
* 枚举转换成响应
*
* @param enumSet 枚举集合
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 响应集合
*/
public final <E extends Enum<E> & IBaseEnum<T>, T> List<EnumResponse<T>> toEnumResponse(EnumSet<E> enumSet) {
return enumSet.stream().map(e -> new EnumResponse<>(e.getValue(), e.getDesc())).collect(Collectors.toList());
}
/**
* 枚举转换成响应
*
* @param enumSet 枚举集合
* @param predicate 过滤条件
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 响应集合
*/
public final <E extends Enum<E> & IBaseEnum<T>, T> List<EnumResponse<T>> toEnumResponse(EnumSet<E> enumSet, Predicate<E> predicate) {
if (predicate == null) {
return toEnumResponse(enumSet);
}
return enumSet.stream()
.filter(predicate)
.map(e -> new EnumResponse<>(e.getValue(), e.getDesc())).collect(Collectors.toList());
}
/**
* 普通对象转换成枚举响应
*
* @param elementList 对象集合
* @param valueFunction 值转换函数
* @param descFunction 描述转换函数
* @param <E> 对象类型
* @param <T> 对象值类型
* @return 响应集合
*/
public final <E, T> List<EnumResponse<T>> toEnumResponse(List<E> elementList, Function<E, T> valueFunction, Function<E, String> descFunction) {
List<EnumResponse<T>> enumResponseList = new ArrayList<>();
if (elementList == null || elementList.isEmpty()) {
return enumResponseList;
}
elementList.stream().filter(element -> {
T value = valueFunction.apply(element);
String desc = descFunction.apply(element);
return !ObjectUtils.isEmpty(value) && !ObjectUtils.isEmpty(desc);
}).collect(Collectors.toMap(valueFunction, descFunction, (a, b) -> b, LinkedHashMap::new))
.forEach((value, desc) -> enumResponseList.add(new EnumResponse<>(value, desc)));
return enumResponseList;
}
}
2.1.10 EnumService
枚举服务实现,提供查询枚举、查询非枚举对象、判断当前传递的name是否是非枚举对象这些方法。
EnumService也是最终我们要使用的方法。会通过自动配置的方式注入到Spring容器中。
package org.feng.commons.ec;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 枚举服务
*/
@SuppressWarnings("all")
@Slf4j
public class EnumService {
@Autowired
private EnumConvertor enumConvertor;
/**
* 非枚举对象转换处理器
*/
private Map<String, NonEnumObjectHandler> nonEnumObjectHandlerMap;
public EnumService(List<NonEnumObjectHandler> nonEnumObjectHandlers) {
this.enumConvertor = enumConvertor;
if (CollectionUtils.isEmpty(nonEnumObjectHandlers)) {
nonEnumObjectHandlerMap = Collections.emptyMap();
} else {
log.info("初始化非枚举对象转换处理器:{}", nonEnumObjectHandlers);
nonEnumObjectHandlerMap = nonEnumObjectHandlers.stream()
.collect(Collectors.toMap(NonEnumObjectHandler::getHandlerName, Function.identity()));
}
}
/**
* 查询枚举
*
* @param enumName 枚举名称,枚举类的简单类名
* @param queryAll 是否查询所有,即不使用筛选条件
* @param <E> 枚举类型
* @param <T> 枚举值类型
* @return 枚举集合
*/
@SuppressWarnings("all")
public <E extends Enum<E> & IBaseEnum<T>, T> List<EnumResponse<T>> queryEnums(String enumName, boolean queryAll) {
// 处理枚举
EnumSet<E> enumSet = EnumRegister.getEnumSet(enumName);
if (enumSet == null) {
return Collections.emptyList();
}
if (queryAll) {
return enumConvertor.toEnumResponse(enumSet);
}
Predicate<E> predicate = EnumRegister.getPredicate(enumName);
return enumConvertor.toEnumResponse(enumSet, predicate);
}
/**
* 查询非枚举对象
*
* @param handlerName 处理器名称
* @param criteria 查询条件
* @param <T> 枚举值类型
* @param <QueryCriteria> 查询条件类型
* @return 枚举集合
*/
public <T, QueryCriteria> List<EnumResponse<T>> queryNonEnumObject(String handlerName, Object criteria) {
if (nonEnumObjectHandlerMap.containsKey(handlerName)) {
NonEnumObjectHandler<T, QueryCriteria> nonEnumObjectHandler = nonEnumObjectHandlerMap.get(handlerName);
// handler 的泛型以及handle方法参数类型必须和criteria类型一致
if (criteria != null) {
Assert.isTrue(nonEnumObjectHandler.getQueryCriteriaClass().isInstance(criteria), "查询条件类型错误,类型不匹配");
return nonEnumObjectHandler.handle((QueryCriteria) criteria);
}
return nonEnumObjectHandler.handle(null);
}
return Collections.emptyList();
}
/**
* 是否是非枚举对象
*
* @param name 名称(处理器名称或枚举名)
* @return 是否是非枚举对象
*/
public boolean isNonEnumObject(String name) {
return nonEnumObjectHandlerMap.containsKey(name);
}
}
2.1.11 EnumProperties
枚举starter 对应的配置属性。目前仅一个配置项,就是指定包名。
这个包就是你想注册的枚举所在的包,可以配置多个。
package org.feng.commons.ec;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Set;
@Data
@ConfigurationProperties(prefix = "commons.enums.component")
public class EnumProperties {
/**
* 指定枚举所在的包名
*/
private Set<String> enumPackages;
}
2.1.12 EnumAutoConfiguration
枚举自动配置类。
package org.feng.commons.ec;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Set;
/**
* 枚举自动配置类
*/
@AutoConfiguration
@Slf4j
@SuppressWarnings("all")
@AllArgsConstructor
@EnableConfigurationProperties({EnumProperties.class})
public class EnumAutoConfiguration {
private final EnumProperties enumProperties;
@Autowired(required = false)
private List<NonEnumObjectHandler> nonEnumObjectHandlers;
/**
* 装配枚举服务
*/
@Bean
public EnumService enumService(@Autowired CustomerRegisterEnumConfigure customerConfigure) {
// 获取配置的枚举包名
Set<String> enumPackages = enumProperties.getEnumPackages();
if (!CollectionUtils.isEmpty(enumPackages)) {
log.info("注册枚举包名:{}", enumPackages);
// 扫描并注册枚举
EnumRegister.scanPackageAndRegister(enumPackages.toArray(new String[0]));
}
// 自定义枚举信息注册
customerConfigure.register();
return new EnumService(nonEnumObjectHandlers);
}
@Bean
@ConditionalOnMissingBean
public CustomerRegisterEnumConfigure registerEnumConfigure() {
log.info("注册默认枚举配置信息");
return () -> {
};
}
}
2.2 admin-starter
这个模块是一个简单的 spring-web 结构。
2.2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.feng</groupId>
<artifactId>admin-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.feng.commons</groupId>
<artifactId>commons-enums-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<parameters>true</parameters>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>org.feng.starter.MainApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2.2.2 resources/application.yml
配置项,这里配置了需要扫描注册的包名。
commons:
enums:
component:
enum-packages:
- org.feng.starter.enums
2.2.3 枚举示例
package org.feng.starter.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.feng.commons.ec.IBaseEnum;
@AllArgsConstructor
@Getter
public enum OrderStatusEnum implements IBaseEnum<Integer> {
PENDING(1, "待处理"),
PROCESSING(2, "处理中"),
COMPLETED(3, "已完成"),
;
private final Integer value;
private final String desc;
}
package org.feng.starter.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.feng.commons.ec.IBaseEnum;
@Getter
@AllArgsConstructor
public enum SexEnum implements IBaseEnum<Integer> {
MALE(1, "男"),
FEMALE(2, "女"),
OTHER(3, "其他");
private final Integer value;
private final String desc;
}
2.2.4 DictDTO
package org.feng.starter.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DictDTO {
private String key;
private String value;
private String type;
}
2.2.5 DictQueryCriteria
字典查询条件。
package org.feng.starter.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class DictQueryCriteria implements Serializable {
private static final long serialVersionUID = -8600134784888228198L;
private String type;
}
2.2.6 DictNonEnumObjectHandler
字典项的非枚举对象处理器实现。
package org.feng.starter.handler;
import org.feng.commons.ec.EnumConvertor;
import org.feng.commons.ec.EnumResponse;
import org.feng.commons.ec.NonEnumObjectHandler;
import org.feng.starter.dto.DictDTO;
import org.feng.starter.dto.DictQueryCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class DictNonEnumObjectHandler implements NonEnumObjectHandler<String, DictQueryCriteria> {
@Autowired
private EnumConvertor enumConvertor;
@Override
public List<EnumResponse<String>> handle(DictQueryCriteria criteria) {
// 原数据,可能从库里查到的,可能从缓存获取,可能从其他接口获取等
List<DictDTO> dicts = new ArrayList<>();
dicts.add(new DictDTO("1", "1", "a"));
dicts.add(new DictDTO("2", "2", "a"));
dicts.add(new DictDTO("3", "3", "b"));
if (criteria != null) {
// 筛选条件
dicts = dicts.stream().filter(dict -> dict.getType().equals(criteria.getType())).collect(Collectors.toList());
}
// 转换成枚举响应
return enumConvertor.toEnumResponse(dicts, DictDTO::getKey, DictDTO::getValue);
}
}
2.2.7 EnumConfig
枚举配置类,当需要额外配置时需要定义这样一个配置类。
主要是配置CustomerRegisterEnumConfigure 的 Bean实例,以及NonEnumObjectHandler 的实现。
如果你的项目中,只是涉及到简单的查询下拉全部枚举,则可以不用这个配置类。仅在配置中指定包名即可。
package org.feng.starter.config;
import lombok.AllArgsConstructor;
import org.feng.commons.ec.CustomerRegisterEnumConfigure;
import org.feng.commons.ec.EnumRegister;
import org.feng.starter.enums.SexEnum;
import org.feng.starter.handler.DictNonEnumObjectHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@AllArgsConstructor
@Configuration
public class EnumConfig {
/**
* 自定义注册枚举(没特殊需要,可以不用定义):可以覆盖扫描包生成的一部分配置
*/
@Bean
public CustomerRegisterEnumConfigure registerEnumConfigure() {
return () -> {
// 注册自己需要的枚举信息
// 注册性别枚举,查询时的筛选条件是 忽略其他,注意这里的配置会覆盖掉扫描包中的枚举
EnumRegister.registerQueryPredicate(SexEnum.class, e -> !SexEnum.OTHER.equals(e));
};
}
/**
* 枚举字典转换器:非枚举对象转换;或者可以在 DictNonEnumObjectHandler 类上使用 Component 注解。
*/
@Bean
public DictNonEnumObjectHandler dictNonEnumObjectHandler() {
return new DictNonEnumObjectHandler();
}
}
2.2.8 DropDownController
package org.feng.starter.controller;
import org.feng.commons.ec.EnumResponse;
import org.feng.commons.ec.EnumService;
import org.feng.starter.dto.DictQueryCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/dropDown")
public class DropDownController {
@Autowired
private EnumService enumService;
/**
* 下拉-查枚举
*
* @param enumName 枚举名称
* @param queryAll 是否查询所有
* @return 枚举集合
*/
@GetMapping("/queryEnum")
public List<EnumResponse<Object>> queryEnum(@RequestParam("enumName") String enumName, @RequestParam(value = "queryAll", required = false, defaultValue = "true") boolean queryAll) {
return enumService.queryEnums(enumName, queryAll);
}
/**
* 下拉-查字典(定制化下拉,字典本身不是枚举,并且携带自定义查询请求体)
*
* @param handlerName 处理器名称
* @param criteria 查询条件
* @return 字典集合
*/
@PostMapping("/queryDict")
public List<EnumResponse<Object>> queryDict(@RequestParam("handlerName") String handlerName, @RequestBody DictQueryCriteria criteria) {
return enumService.queryNonEnumObject(handlerName, criteria);
}
/**
* 下拉-查全部(不携带任何查询条件),可以查非枚举对象和枚举对象的下拉
*
* @param name 处理器名称或枚举名
* @return 响应集合
*/
@GetMapping("/queryAll")
public List<EnumResponse<Object>> query(@RequestParam("name") String name) {
if (enumService.isNonEnumObject(name)) {
return enumService.queryNonEnumObject(name, null);
}
return enumService.queryEnums(name, true);
}
}
2.2.9 MainApplication
package org.feng.starter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"org.feng.commons", "org.feng.starter"})
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
三、测试运行结果
运行MainApplication,当运行成功后,使用以下接口进行调试。
3.1 查询枚举
3.1.1 查全部性别枚举
http://127.0.0.1:8080/dropDown/queryEnum?enumName=SexEnum&queryAll=true
响应结果:
[
{
"value": 1,
"desc": "男"
},
{
"value": 2,
"desc": "女"
},
{
"value": 3,
"desc": "其他"
}
]
3.1.2 使用筛选条件,查部分性别枚举
http://127.0.0.1:8080/dropDown/queryEnum?enumName=SexEnum&queryAll=false
响应结果:
[
{
"value": 1,
"desc": "男"
},
{
"value": 2,
"desc": "女"
}
]
3.2 查询全部,支持查询枚举和非枚举
使用聚合的接口方法 queryAll。内部判断name对应的是枚举还是非枚举对象处理器。
3.2.1 使用聚合接口查性别枚举
http://127.0.0.1:8080/dropDown/queryAll?name=SexEnum
响应结果:
[
{
"value": 1,
"desc": "男"
},
{
"value": 2,
"desc": "女"
},
{
"value": 3,
"desc": "其他"
}
]
3.2.2 使用聚合接口插非枚举对象
http://127.0.0.1:8080/dropDown/queryAll?name=DictNonEnumObjectHandler
响应结果:
[
{
"value": "1",
"desc": "1"
},
{
"value": "2",
"desc": "2"
},
{
"value": "3",
"desc": "3"
}
]
3.3 定制化查询非枚举对象
携带查询条件查询非枚举对象。
http://127.0.0.1:8080/dropDown/queryDict?handlerName=DictNonEnumObjectHandler
请求参数:
{
"type": "a"
}
响应参数:
[
{
"value": "1",
"desc": "1"
},
{
"value": "2",
"desc": "2"
}
]
更多推荐


所有评论(0)