├── .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 extends java.lang.annotation.Annotation> 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
--------------------------------------------------------------------------------