├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── mb │ ├── annotation │ └── Export.java │ ├── aspect │ └── ExportAspect.java │ ├── config │ ├── AopConfig.java │ └── ExportPropertiesAutoConfig.java │ ├── properties │ └── ExportProperties.java │ ├── request │ └── ModifiableRequestWrapper.java │ └── utils │ ├── GenericTypeUtils.java │ └── ReflectionFieldUtils.java └── resources └── META-INF └── spring.factories /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 通用导出 2 | 在开发过程中,我们经常会遇到需要导出数据的需求,比如将列表的数据导出到Excel中,那么这时候就需要开发一个接口。这个库是一个通用的导出库,在有导出需求时,无需编写导出接口即可非常方便的实现导出需求。 3 | 4 | ## 快速入门 5 | 6 | ### 1. 引入依赖 7 | ```xml 8 | 9 | com.github.houbb 10 | export 11 | 0.0.1 12 | 13 | ``` 14 | 15 | ### 2. 使用示例 16 | 2.1 在列表接口上添加注解 `@Export` 17 | ```java 18 | @RestController 19 | public class TestController { 20 | 21 | // 在列表接口上添加注解 22 | @Export 23 | @GetMapping("/list") 24 | public List list() { 25 | return testService.list(); 26 | } 27 | 28 | // 在列表接口上添加注解 29 | @Export(responseData = "data") 30 | @GetMapping("/list2") 31 | public ResultVO list2(ParamDTO paramDTO) { 32 | List list = testService.list(); 33 | return ResultUtil.success(list); 34 | } 35 | } 36 | ``` 37 | 38 | 2.2 接口的返回类中的需要添加 `@ExcelProperty` 注解,底层使用了阿里的 `EasyExcel` 来实现导出功能 39 | ```java 40 | @Data 41 | public class Test { 42 | 43 | @ExcelProperty("ID") 44 | private Long id; 45 | 46 | @ExcelProperty("名称") 47 | private String name; 48 | 49 | @ExcelProperty("年龄") 50 | private Integer age; 51 | 52 | @ExcelProperty("创建时间") 53 | private Date createTime; 54 | } 55 | ``` 56 | 57 | ### 3. 访问接口 58 | http://localhost:8080/list?export=1&exportAll=1 59 |
60 | http://localhost:8080/list2?export=1&exportAll=1 61 | 62 | 在访问接口时,请求参数中添加 `export=1` 即可达到导出的效果,也可以传 `export=true` 63 |
64 | 如果需要导出全部数据,那么需要额外在请求参数中添加 `exportAll=1` ,同样也可以传 `exportAll=true` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.6.13 11 | 12 | 13 | io.github.uuzzii 14 | export-spring-boot-starter 15 | 1.1 16 | 17 | export-spring-boot-starter 18 | 通用导出 19 | https://github.com/UUzzii/export-spring-boot-starter/tree/master 20 | 21 | 22 | 23 | shipeng 24 | sp_nice@163.com 25 | https://github.com/UUzzii/export-spring-boot-starter/tree/master 26 | 27 | 28 | 29 | 30 | 31 | Apache License, Version 2.0 32 | https://www.apache.org/licenses/LICENSE-2.0 33 | 34 | 35 | 36 | 37 | scm:https://github.com/UUzzii/export-spring-boot-starter.git 38 | https://github.com/UUzzii/export-spring-boot-starter/tree/master 39 | 40 | 41 | 42 | 43 | central 44 | https://s01.oss.sonatype.org/content/repositories/snapshots 45 | 46 | 47 | central 48 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 49 | 50 | 51 | 52 | 53 | 8 54 | 8 55 | UTF-8 56 | UTF-8 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-web 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter-aop 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-configuration-processor 73 | true 74 | 75 | 76 | 77 | org.projectlombok 78 | lombok 79 | true 80 | 81 | 82 | 83 | org.apache.commons 84 | commons-lang3 85 | 86 | 87 | 88 | cn.hutool 89 | hutool-all 90 | 5.8.16 91 | 92 | 93 | 94 | com.alibaba 95 | easyexcel 96 | 4.0.3 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.springframework.boot 105 | spring-boot-maven-plugin 106 | 107 | exec 108 | 112 | true 113 | 114 | 115 | 116 | 117 | org.sonatype.central 118 | central-publishing-maven-plugin 119 | 0.4.0 120 | true 121 | 122 | central 123 | true 124 | true 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-gpg-plugin 131 | 1.5 132 | 133 | D:\GnuPG\bin\gpg.exe 134 | shipeng 135 | 136 | 137 | 138 | sign-artifacts 139 | verify 140 | 141 | sign 142 | 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-source-plugin 150 | 2.2.1 151 | 152 | 153 | attach-sources 154 | 155 | jar-no-fork 156 | 157 | 158 | 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-javadoc-plugin 164 | 2.9.1 165 | 166 | 167 | -Xdoclint:none 168 | D:\Program Files\Java\jdk-1.8\bin\javadoc.exe 169 | 170 | 171 | 172 | attach-javadocs 173 | 174 | jar 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/main/java/com/mb/annotation/Export.java: -------------------------------------------------------------------------------- 1 | package com.mb.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 导出注解 10 | * @author cxn 11 | * @date 2024/12/18 11:13 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface Export { 16 | 17 | /** 导出参数名 */ 18 | String exportParam() default ""; 19 | 20 | /** 导出全部参数名 */ 21 | String exportAllParam() default ""; 22 | 23 | /** 页码参数名 */ 24 | String pageNumParam() default ""; 25 | 26 | /** 每页大小参数名 */ 27 | String pageSizeParam() default ""; 28 | 29 | /** 30 | * 响应数据 31 | * 如果为空或者default,就直接取方法返回值 32 | * 如果是统一返回类,那么就写类中数据的字段名,比如data 33 | * */ 34 | String responseData() default ""; 35 | 36 | /** 导出文件文件名称 */ 37 | String fileName() default "导出文件"; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/mb/aspect/ExportAspect.java: -------------------------------------------------------------------------------- 1 | package com.mb.aspect; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.collection.ListUtil; 5 | import com.alibaba.excel.EasyExcel; 6 | import com.mb.annotation.Export; 7 | import com.mb.properties.ExportProperties; 8 | import com.mb.utils.GenericTypeUtils; 9 | import com.mb.utils.ReflectionFieldUtils; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.aspectj.lang.ProceedingJoinPoint; 13 | import org.aspectj.lang.annotation.Around; 14 | import org.aspectj.lang.annotation.Aspect; 15 | import org.aspectj.lang.reflect.MethodSignature; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.core.LocalVariableTableParameterNameDiscoverer; 18 | import org.springframework.web.context.request.RequestContextHolder; 19 | import org.springframework.web.context.request.ServletRequestAttributes; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import java.lang.reflect.Field; 24 | import java.net.URLEncoder; 25 | import java.util.ArrayList; 26 | import java.util.Collection; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.Optional; 31 | 32 | /** 33 | * 导出切面 34 | * @author cxn 35 | * @date 2024/12/18 11:29 36 | */ 37 | @Slf4j 38 | @Aspect 39 | public class ExportAspect { 40 | 41 | @Autowired 42 | private ExportProperties exportProperties; 43 | 44 | 45 | /** 46 | * 环绕通知 47 | * @param pjp 48 | * @return 49 | */ 50 | @Around("@annotation(export)") 51 | public Object export(ProceedingJoinPoint pjp, Export export) throws Throwable { 52 | ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 53 | if (sra == null) { 54 | return pjp.proceed(); 55 | } 56 | 57 | /* 参数采用最近原则,优先取注解的,如果注解没有指定才取全局的 */ 58 | String exportParam = StringUtils.isNotBlank(export.exportParam()) ? export.exportParam() : exportProperties.getExportParam(); 59 | String exportAllParam = StringUtils.isNotBlank(export.exportAllParam()) ? export.exportAllParam() : exportProperties.getExportAllParam(); 60 | String pageNumParam = StringUtils.isNotBlank(export.pageNumParam()) ? export.pageNumParam() : exportProperties.getPageNumParam(); 61 | String pageSizeParam = StringUtils.isNotBlank(export.pageSizeParam()) ? export.pageSizeParam() : exportProperties.getPageSizeParam(); 62 | 63 | // 根据请求方式获取请求参数,判断是否为导出 64 | Map exportMap = this.checkExport(pjp, sra.getRequest(), exportParam, exportAllParam); 65 | if (!exportMap.get(exportParam)) { 66 | return pjp.proceed(); 67 | } 68 | 69 | log.info("开始处理导出..."); 70 | 71 | // 获取方法的参数, 72 | Object[] args = pjp.getArgs(); 73 | 74 | if (exportMap.get(exportAllParam)) { 75 | this.handleExportAll(pjp, pageNumParam, pageSizeParam, args); 76 | } 77 | 78 | // 执行方法 79 | Object proceed = pjp.proceed(args); 80 | 81 | HttpServletResponse response = sra.getResponse(); 82 | if (response == null) { 83 | return proceed; 84 | } 85 | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 86 | response.setCharacterEncoding("utf-8"); 87 | String fileName = URLEncoder.encode(export.fileName(), "UTF-8").replaceAll("\\+", "%20"); 88 | response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); 89 | 90 | // 响应数据的字段名 91 | String responseData = StringUtils.isNotBlank(export.responseData()) ? export.responseData() : exportProperties.getResponseData(); 92 | 93 | // 解析响应数据 94 | if (StringUtils.isBlank(responseData) || "default".equals(responseData)) { 95 | Collection data = (Collection) proceed; 96 | if (CollUtil.isEmpty(data)) { 97 | EasyExcel.write(response.getOutputStream()).sheet("").doWrite(new ArrayList<>()); 98 | } else { 99 | Optional> listElementType = GenericTypeUtils.getListElementType(proceed); 100 | EasyExcel.write(response.getOutputStream(), listElementType.get()).sheet("").doWrite(data); 101 | } 102 | } else { 103 | Class proceedClass = proceed.getClass(); 104 | Field field = proceedClass.getDeclaredField(responseData); 105 | field.setAccessible(true); 106 | Collection data = (Collection) field.get(proceed); 107 | if (CollUtil.isEmpty(data)) { 108 | EasyExcel.write(response.getOutputStream()).sheet("").doWrite(new ArrayList<>()); 109 | } else { 110 | Optional> listElementType = GenericTypeUtils.getListElementType(data); 111 | EasyExcel.write(response.getOutputStream(), listElementType.get()).sheet("").doWrite(data); 112 | } 113 | } 114 | 115 | log.info("导出完成!"); 116 | return null; 117 | } 118 | 119 | /** 120 | * 校验是否为导出请求 121 | * @param pjp 122 | * @param request 123 | * @param exportParam 124 | * @param exportAllParam 125 | * @return 126 | * @throws Exception 127 | */ 128 | private Map checkExport(ProceedingJoinPoint pjp, HttpServletRequest request, String exportParam, String exportAllParam) throws Exception { 129 | // 判断是否导出请求,导出的话是否为导出全部 130 | boolean isExport = false; 131 | boolean isExportAll = false; 132 | 133 | String exportParamValue = request.getParameter(exportParam); 134 | if (StringUtils.isNotBlank(exportParamValue) 135 | && ("1".equals(exportParamValue) || "true".equals(exportParamValue))) { 136 | isExport = true; 137 | // 导出请求,接着判断是否为导出全部 138 | String exportAllParamValue = request.getParameter(exportAllParam); 139 | if (StringUtils.isNotBlank(exportAllParamValue) 140 | && ("1".equals(exportAllParamValue) || "true".equals(exportAllParamValue))) { 141 | isExportAll = true; 142 | } 143 | } 144 | 145 | if ("POST".equals(request.getMethod()) && StringUtils.isBlank(exportParamValue)) { 146 | // 如果是POST请求,那么可能是在body里传的 147 | Object[] args = pjp.getArgs(); 148 | for (Object arg : args) { 149 | // 过滤基本数据类型 150 | if (this.filterBasic(arg)) continue; 151 | // 排除掉所有的基本数据类型和他们的包装类,剩下的就是实体类,获取实体类的所有属性 152 | List fieldList = ReflectionFieldUtils.getAllFieldsOfName(arg.getClass(), ListUtil.toList(exportParam, exportAllParam)); 153 | if (CollUtil.isEmpty(fieldList)) { 154 | continue; 155 | } 156 | // 修改分页参数 157 | for (Field field : fieldList) { 158 | field.setAccessible(true); 159 | if (field.getName().equals(exportParam)) { 160 | Object o = field.get(arg); 161 | if (o != null && ("1".equals(o.toString()) || "true".equals(o.toString()))) { 162 | isExport = true; 163 | } 164 | } else if (field.getName().equals(exportAllParam)) { 165 | Object o = field.get(arg); 166 | if (o != null && ("1".equals(o.toString()) || "true".equals(o.toString()))) { 167 | isExportAll = true; 168 | } 169 | } 170 | } 171 | } 172 | } 173 | 174 | Map map = new HashMap<>(); 175 | map.put(exportParam, isExport); 176 | map.put(exportAllParam, isExportAll); 177 | return map; 178 | } 179 | 180 | /** 181 | * 过滤基本数据类型 182 | * @param arg 183 | * @return 184 | */ 185 | private boolean filterBasic(Object arg) { 186 | if (arg instanceof Integer) { 187 | return true; 188 | } else if (arg instanceof Long) { 189 | return true; 190 | } else if (arg instanceof String) { 191 | return true; 192 | } else if (arg instanceof Boolean) { 193 | return true; 194 | } else if (arg instanceof Float) { 195 | return true; 196 | } else if (arg instanceof Double) { 197 | return true; 198 | } else if (arg instanceof Character) { 199 | return true; 200 | } else if (arg instanceof Byte) { 201 | return true; 202 | } else if (arg instanceof Short) { 203 | return true; 204 | } 205 | return false; 206 | } 207 | 208 | /** 209 | * 处理导出全部 210 | * @param pjp 211 | * @param pageNumParam 212 | * @param pageSizeParam 213 | * @param args 214 | * @throws Exception 215 | */ 216 | private void handleExportAll(ProceedingJoinPoint pjp, String pageNumParam, String pageSizeParam, Object[] args) throws Exception { 217 | // 导出全部,那么就需要修改分页参数 218 | MethodSignature signature = (MethodSignature) pjp.getSignature(); 219 | // 使用 Spring 的工具类获取参数名 220 | String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(signature.getMethod()); 221 | if (parameterNames == null || parameterNames.length == 0) { 222 | throw new RuntimeException("方法没有参数,你是怎么获取的?用HttpServletRequest吗?"); 223 | } 224 | 225 | // 先判断分页参数是否在方法参数中 226 | boolean isReturn = false; 227 | 228 | for (int i = 0; i < parameterNames.length; i++) { 229 | String parameterName = parameterNames[i]; 230 | if (parameterName.equals(pageNumParam) || parameterName.equals(pageSizeParam)) { 231 | args[i] = null; 232 | isReturn = true; 233 | } 234 | } 235 | 236 | if (isReturn) { 237 | return; 238 | } 239 | 240 | // 如果分页参数不在方法参数中,那么就在实体类中 241 | for (Object arg : args) { 242 | // 过滤基本数据类型 243 | if (this.filterBasic(arg)) continue; 244 | // 排除掉所有的基本数据类型和他们的包装类,剩下的就是实体类,获取实体类的所有属性 245 | List fieldList = ReflectionFieldUtils.getAllFieldsOfName(arg.getClass(), ListUtil.toList(pageNumParam, pageSizeParam)); 246 | if (CollUtil.isEmpty(fieldList)) { 247 | continue; 248 | } 249 | // 修改分页参数 250 | for (Field field : fieldList) { 251 | field.setAccessible(true); 252 | if (field.getName().equals(pageNumParam) || field.getName().equals(pageSizeParam)) { 253 | field.set(arg, null); 254 | } 255 | } 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/main/java/com/mb/config/AopConfig.java: -------------------------------------------------------------------------------- 1 | package com.mb.config; 2 | 3 | import com.mb.aspect.ExportAspect; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * AOP配置 9 | * @author cxn 10 | * @date 2024/12/18 11:41 11 | */ 12 | @Configuration 13 | public class AopConfig { 14 | 15 | @Bean 16 | public ExportAspect exportAspect() { 17 | return new ExportAspect(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/mb/config/ExportPropertiesAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.mb.config; 2 | 3 | import com.mb.properties.ExportProperties; 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * @author 石鹏 10 | * @date 2024/12/20 16:52 11 | */ 12 | @Configuration 13 | @EnableConfigurationProperties(ExportProperties.class) 14 | public class ExportPropertiesAutoConfig { 15 | 16 | @Bean 17 | public ExportProperties exportProperties() { 18 | return new ExportProperties(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/mb/properties/ExportProperties.java: -------------------------------------------------------------------------------- 1 | package com.mb.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * 导出配置 8 | * @author 石鹏 9 | * @date 2024/12/20 16:49 10 | */ 11 | @ConfigurationProperties(prefix = "export") 12 | @Data 13 | public class ExportProperties { 14 | 15 | /** 导出参数名 */ 16 | private String exportParam = "export"; 17 | 18 | /** 导出全部参数名 */ 19 | private String exportAllParam = "exportAll"; 20 | 21 | /** 页码参数名 */ 22 | private String pageNumParam = "pageNum"; 23 | 24 | /** 每页大小参数名 */ 25 | private String pageSizeParam = "pageSize"; 26 | 27 | /** 28 | * 响应数据 29 | * 如果为空或default,就直接取方法返回值 30 | * 如果是统一返回类,那么就写类中数据的字段名,比如data 31 | * */ 32 | private String responseData; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/mb/request/ModifiableRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package com.mb.request; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletRequestWrapper; 5 | import java.util.*; 6 | 7 | public class ModifiableRequestWrapper extends HttpServletRequestWrapper { 8 | private final Map modifiedParameters; 9 | private final Map headerMap; 10 | 11 | public ModifiableRequestWrapper(HttpServletRequest request) { 12 | super(request); 13 | // 复制原始参数 14 | this.modifiedParameters = new HashMap<>(request.getParameterMap()); 15 | this.headerMap = new HashMap<>(); 16 | } 17 | 18 | // 修改参数值 19 | public void setParameter(String name, String value) { 20 | modifiedParameters.put(name, new String[]{value}); 21 | } 22 | 23 | // 修改参数值(数组) 24 | public void setParameter(String name, String[] values) { 25 | modifiedParameters.put(name, values); 26 | } 27 | 28 | // 添加参数 29 | public void addParameter(String name, String value) { 30 | String[] values = modifiedParameters.get(name); 31 | if (values == null) { 32 | setParameter(name, value); 33 | } else { 34 | String[] newValues = Arrays.copyOf(values, values.length + 1); 35 | newValues[values.length] = value; 36 | modifiedParameters.put(name, newValues); 37 | } 38 | } 39 | 40 | // 删除参数 41 | public void removeParameter(String name) { 42 | modifiedParameters.remove(name); 43 | } 44 | 45 | // 设置请求头 46 | public void setHeader(String name, String value) { 47 | headerMap.put(name, value); 48 | } 49 | 50 | @Override 51 | public String getParameter(String name) { 52 | String[] values = modifiedParameters.get(name); 53 | return values != null && values.length > 0 ? values[0] : null; 54 | } 55 | 56 | @Override 57 | public Map getParameterMap() { 58 | return Collections.unmodifiableMap(modifiedParameters); 59 | } 60 | 61 | @Override 62 | public Enumeration getParameterNames() { 63 | return Collections.enumeration(modifiedParameters.keySet()); 64 | } 65 | 66 | @Override 67 | public String[] getParameterValues(String name) { 68 | return modifiedParameters.get(name); 69 | } 70 | 71 | @Override 72 | public String getHeader(String name) { 73 | String headerValue = headerMap.get(name); 74 | return headerValue != null ? headerValue : super.getHeader(name); 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/java/com/mb/utils/GenericTypeUtils.java: -------------------------------------------------------------------------------- 1 | package com.mb.utils; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import java.util.Optional; 6 | 7 | public class GenericTypeUtils { 8 | 9 | /** 10 | * 从Object对象中安全地获取List的元素类型 11 | * 12 | * @param obj 可能是List类型的对象 13 | * @return Optional> 如果成功获取到类型则返回,否则返回empty 14 | */ 15 | public static Optional> getListElementType(Object obj) { 16 | // 检查对象是否为空 17 | if (obj == null) { 18 | return Optional.empty(); 19 | } 20 | 21 | // 检查对象是否为List类型 22 | if (!(obj instanceof List)) { 23 | return Optional.empty(); 24 | } 25 | 26 | List list = (List) obj; 27 | 28 | // 检查List是否为空 29 | if (list.isEmpty()) { 30 | return Optional.empty(); 31 | } 32 | 33 | // 获取第一个非空元素 34 | Object firstElement = list.stream() 35 | .filter(Objects::nonNull) 36 | .findFirst() 37 | .orElse(null); 38 | 39 | // 如果没有非空元素,返回empty 40 | if (firstElement == null) { 41 | return Optional.empty(); 42 | } 43 | 44 | // 返回元素的类型 45 | return Optional.of(firstElement.getClass()); 46 | } 47 | 48 | /** 49 | * 从Object对象中安全地获取List的元素类型,如果无法获取则抛出异常 50 | * 51 | * @param obj 可能是List类型的对象 52 | * @return Class 列表元素的类型 53 | * @throws IllegalArgumentException 如果无法获取类型 54 | */ 55 | public static Class getListElementTypeOrThrow(Object obj) { 56 | return getListElementType(obj) 57 | .orElseThrow(() -> new IllegalArgumentException("无法获取List元素类型:对象为null或空集合或非List类型")); 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/com/mb/utils/ReflectionFieldUtils.java: -------------------------------------------------------------------------------- 1 | package com.mb.utils; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | public class ReflectionFieldUtils { 9 | 10 | /** 11 | * 获取类及其所有父类的字段(不包括 Object 类的字段) 12 | * 13 | * @param clazz 要获取字段的类 14 | * @return 字段列表 15 | */ 16 | public static List getAllFields(Class clazz) { 17 | List fieldList = new ArrayList<>(); 18 | 19 | // 当前类不为空且不是Object类时,继续向上遍历 20 | while (clazz != null && clazz != Object.class) { 21 | // 获取当前类声明的所有字段(包括私有字段) 22 | Field[] fields = clazz.getDeclaredFields(); 23 | fieldList.addAll(Arrays.asList(fields)); 24 | 25 | // 获取父类,继续遍历 26 | clazz = clazz.getSuperclass(); 27 | } 28 | 29 | return fieldList; 30 | } 31 | 32 | public static List getAllFieldsOfName(Class clazz, List fieldNameList) { 33 | List allFields = getAllFields(clazz); 34 | List resultFields = new ArrayList<>(); 35 | 36 | for (Field field : allFields) { 37 | field.setAccessible(true); 38 | if (fieldNameList.contains(field.getName())) { 39 | resultFields.add(field); 40 | } 41 | } 42 | 43 | return resultFields; 44 | } 45 | 46 | /** 47 | * 获取类及其所有父类的特定类型的字段 48 | * 49 | * @param clazz 要获取字段的类 50 | * @param fieldType 字段类型 51 | * @return 指定类型的字段列表 52 | */ 53 | public static List getAllFieldsOfType(Class clazz, Class fieldType) { 54 | List allFields = getAllFields(clazz); 55 | List resultFields = new ArrayList<>(); 56 | 57 | for (Field field : allFields) { 58 | if (fieldType.isAssignableFrom(field.getType())) { 59 | resultFields.add(field); 60 | } 61 | } 62 | 63 | return resultFields; 64 | } 65 | 66 | /** 67 | * 获取类及其所有父类中带有特定注解的字段 68 | * 69 | * @param clazz 要获取字段的类 70 | * @param annotationClass 注解类型 71 | * @return 带有指定注解的字段列表 72 | */ 73 | public static List getAllFieldsWithAnnotation(Class clazz, 74 | Class annotationClass) { 75 | List allFields = getAllFields(clazz); 76 | List resultFields = new ArrayList<>(); 77 | 78 | for (Field field : allFields) { 79 | if (field.isAnnotationPresent(annotationClass)) { 80 | resultFields.add(field); 81 | } 82 | } 83 | 84 | return resultFields; 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.mb.config.AopConfig,\ 3 | com.mb.config.ExportPropertiesAutoConfig --------------------------------------------------------------------------------