refactor: 移除 web-core,融合 web-core 和 core 模块

1.移除 web-core,融合 web-core 和 core 模块
2.调整部分依赖顺序
This commit is contained in:
2025-05-22 21:49:20 +08:00
parent 4f9e1ba108
commit f83a901626
56 changed files with 166 additions and 256 deletions

View File

@@ -1,46 +0,0 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.continew</groupId>
<artifactId>continew-starter-web</artifactId>
<version>${revision}</version>
</parent>
<artifactId>continew-starter-web-core</artifactId>
<description>ContiNew Starter Web 模块 - 核心模块</description>
<dependencies>
<!-- 核心模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-core</artifactId>
</dependency>
<!-- JSON 模块 - Jackson -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-json-jackson</artifactId>
</dependency>
<!-- Spring Boot Web提供 Spring MVC Web 开发能力,默认内置 Tomcat 服务器) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 移除内置 Tomcat 服务器 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,113 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.web.util;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.URLUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
/**
* 文件工具类
*
* @author Zheng Jie<a href="https://gitee.com/elunez/eladmin">ELADMIN</a>
* @author Charles7c
* @since 1.0.0
*/
public class FileUploadUtils {
private static final Logger log = LoggerFactory.getLogger(FileUploadUtils.class);
private FileUploadUtils() {
}
/**
* 上传
*
* @param multipartFile 源文件对象
* @param filePath 文件路径
* @param isKeepOriginalFilename 是否保留原文件名
* @return 目标文件对象
*/
public static File upload(MultipartFile multipartFile, String filePath, boolean isKeepOriginalFilename) {
String originalFilename = multipartFile.getOriginalFilename();
String extensionName = FileNameUtil.extName(originalFilename);
String fileName;
if (isKeepOriginalFilename) {
fileName = "%s-%s.%s".formatted(FileNameUtil.getPrefix(originalFilename), DateUtil.format(LocalDateTime
.now(), DatePattern.PURE_DATETIME_MS_PATTERN), extensionName);
} else {
fileName = "%s.%s".formatted(IdUtil.fastSimpleUUID(), extensionName);
}
try {
String pathname = filePath + fileName;
File dest = new File(pathname).getCanonicalFile();
// 如果父路径不存在,自动创建
if (!dest.getParentFile().exists() && (!dest.getParentFile().mkdirs())) {
log.error("Create upload file parent path failed.");
}
// 文件写入
multipartFile.transferTo(dest);
return dest;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 下载
*
* @param response 响应对象
* @param file 文件
*/
public static void download(HttpServletResponse response, File file) throws IOException {
download(response, new FileInputStream(file), file.getName());
}
/**
* 下载
*
* @param response 响应对象
* @param inputStream 文件流
* @param fileName 文件名
* @since 2.5.0
*/
public static void download(HttpServletResponse response,
InputStream inputStream,
String fileName) throws IOException {
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLUtil.encode(fileName));
try (inputStream; var outputStream = response.getOutputStream()) {
response.setContentLengthLong(inputStream.transferTo(outputStream));
}
}
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.web.util;
import cn.hutool.core.io.IoUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* 可重复读取请求体的包装器
* 支持文件流直接透传,非文件流可重复读取
*
* @author echo
* @since 2.10.0
*/
public class RepeatReadRequestWrapper extends HttpServletRequestWrapper {
private byte[] cachedBody;
private final HttpServletRequest originalRequest;
public RepeatReadRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.originalRequest = request;
// 判断是否为文件上传请求
if (!isMultipartContent(request)) {
this.cachedBody = IoUtil.readBytes(request.getInputStream(), false);
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 如果是文件上传,直接返回原始输入流
if (isMultipartContent(originalRequest)) {
return originalRequest.getInputStream();
}
// 非文件上传,返回可重复读取的输入流
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
// 非阻塞I/O这里可以根据需要实现
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
// 如果是文件上传直接返回原始Reader
if (isMultipartContent(originalRequest)) {
new BufferedReader(new InputStreamReader(originalRequest.getInputStream(), StandardCharsets.UTF_8));
}
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 检查是否为文件上传请求
*
* @param request 请求对象
* @return 是否为文件上传请求
*/
public boolean isMultipartContent(HttpServletRequest request) {
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart/");
}
}

View File

@@ -1,146 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.web.util;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.springframework.http.MediaType;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
/**
* 可重复读取响应内容的包装器
* 支持缓存响应内容,便于日志记录和后续处理 (不缓存SSE)
*
* @author echo
* @author Charles7c
* @since 2.10.0
*/
public class RepeatReadResponseWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream cachedOutputStream = new ByteArrayOutputStream();
private final PrintWriter writer = new PrintWriter(cachedOutputStream, true);
/**
* 是否为流式响应
*/
private boolean isStreamingResponse = false;
public RepeatReadResponseWrapper(HttpServletResponse response) {
super(response);
checkStreamingResponse();
}
@Override
public void setContentType(String type) {
super.setContentType(type);
// 根据 Content-Type 判断是否为流式响应
if (type != null) {
String lowerType = type.toLowerCase();
isStreamingResponse = lowerType.contains(MediaType.TEXT_EVENT_STREAM_VALUE);
}
}
private void checkStreamingResponse() {
String contentType = getContentType();
if (contentType != null) {
String lowerType = contentType.toLowerCase();
isStreamingResponse = lowerType.contains(MediaType.TEXT_EVENT_STREAM_VALUE);
}
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
checkStreamingResponse();
// 对于 SSE 流式响应,直接返回原始响应流,不做额外处理
if (isStreamingResponse) {
return super.getOutputStream();
}
return new ServletOutputStream() {
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
cachedOutputStream.write(b);
}
@Override
public void write(byte[] b) throws IOException {
cachedOutputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
cachedOutputStream.write(b, off, len);
}
};
}
@Override
public PrintWriter getWriter() throws IOException {
checkStreamingResponse();
if (isStreamingResponse) {
// 对于 SSE 流式响应,直接返回原始响应写入器,不做额外处理
return super.getWriter();
}
return writer;
}
/**
* 获取缓存的响应内容
*
* @return 缓存的响应内容
*/
public String getResponseContent() {
if (!isStreamingResponse) {
writer.flush();
return cachedOutputStream.toString(StandardCharsets.UTF_8);
}
return null;
}
/**
* 将缓存的响应内容复制到原始响应中
*
* @throws IOException IO 异常
*/
public void copyBodyToResponse() throws IOException {
if (!isStreamingResponse && cachedOutputStream.size() > 0) {
getResponse().getOutputStream().write(cachedOutputStream.toByteArray());
}
}
/**
* 是否为流式响应
*
* @return 是否为流式响应
*/
public boolean isStreamingResponse() {
return isStreamingResponse;
}
}

View File

@@ -1,352 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.web.util;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.UriUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.json.jackson.util.JSONUtils;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* Servlet 工具类
*
* @author Charles7c
* @author echo
* @since 1.0.0
*/
public class ServletUtils extends JakartaServletUtil {
private ServletUtils() {
}
/**
* 获取浏览器及其版本信息
*
* @param request 请求对象
* @return 浏览器及其版本信息
*/
public static String getBrowser(HttpServletRequest request) {
if (null == request) {
return null;
}
return getBrowser(request.getHeader("User-Agent"));
}
/**
* 获取浏览器及其版本信息
*
* @param userAgentString User-Agent 字符串
* @return 浏览器及其版本信息
*/
public static String getBrowser(String userAgentString) {
try {
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
if (userAgent == null || userAgent.getBrowser() == null) {
return null;
}
String browserName = userAgent.getBrowser().getName();
String version = userAgent.getVersion();
return CharSequenceUtil.isBlank(version) ? browserName : browserName + StringConstants.SPACE + version;
} catch (Exception e) {
return null;
}
}
/**
* 获取操作系统
*
* @param request 请求对象
* @return 操作系统
*/
public static String getOs(HttpServletRequest request) {
if (null == request) {
return null;
}
return getOs(request.getHeader("User-Agent"));
}
/**
* 获取操作系统
*
* @param userAgentString User-Agent 字符串
* @return 操作系统
*/
public static String getOs(String userAgentString) {
try {
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
if (userAgent == null || userAgent.getOs() == null) {
return null;
}
return userAgent.getOs().getName();
} catch (Exception e) {
return null;
}
}
/**
* 获取请求方法
*
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestMethod() {
HttpServletRequest request = getRequest();
return request != null ? request.getMethod() : null;
}
/**
* 获取请求参数
*
* @param name 参数名
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestParameter(String name) {
HttpServletRequest request = getRequest();
return request != null ? request.getParameter(name) : null;
}
/**
* 获取请求 Ip
*
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestIp() {
HttpServletRequest request = getRequest();
return request != null ? getClientIP(request) : null;
}
/**
* 获取请求头信息
*
* @return {@link Map }<{@link String }, {@link String }>
* @since 2.11.0
*/
public static Map<String, String> getRequestHeaders() {
HttpServletRequest request = getRequest();
return request != null ? getHeaderMap(request) : Collections.emptyMap();
}
/**
* 获取请求 URL包含 query 参数)
* <p>{@code http://localhost:8000/system/user?page=1&size=10}</p>
*
* @return {@link URI }
* @since 2.11.0
*/
public static URI getRequestUrl() {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
String queryString = request.getQueryString();
if (CharSequenceUtil.isBlank(queryString)) {
return URI.create(request.getRequestURL().toString());
}
try {
StringBuilder urlBuilder = appendQueryString(queryString);
return new URI(urlBuilder.toString());
} catch (URISyntaxException e) {
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
StringBuilder urlBuilder = appendQueryString(encoded);
return URI.create(urlBuilder.toString());
}
}
/**
* 获取请求路径
*
* @return {@link URI }
* @since 2.11.0
*/
public static String getRequestPath() {
HttpServletRequest request = getRequest();
return request != null ? request.getRequestURI() : null;
}
/**
* 获取请求 body 参数
*
* @return {@link String }
* @since 2.11.0
*/
public static String getRequestBody() {
HttpServletRequest request = getRequest();
if (request instanceof RepeatReadRequestWrapper wrapper && !wrapper.isMultipartContent(request)) {
String body = JakartaServletUtil.getBody(request);
return JSONUtils.isTypeJSON(body) ? body : null;
}
return null;
}
/**
* 获取请求参数
*
* @return {@link Map }<{@link String }, {@link Object }>
* @since 2.11.0
*/
public static Map<String, Object> getRequestParams() {
String body = getRequestBody();
return CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)
? JSONUtils.toBean(body, Map.class)
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(Objects.requireNonNull(getRequest())));
}
/**
* 获取响应状态
*
* @return int
* @since 2.11.0
*/
public static int getResponseStatus() {
HttpServletResponse response = getResponse();
return response != null ? response.getStatus() : -1;
}
/**
* 获取响应所有的头header信息
*
* @return header值
* @since 2.11.0
*/
public static Map<String, String> getResponseHeaders() {
HttpServletResponse response = getResponse();
if (response == null) {
return Collections.emptyMap();
}
final Collection<String> headerNames = response.getHeaderNames();
final Map<String, String> headerMap = MapUtil.newHashMap(headerNames.size(), true);
for (String name : headerNames) {
headerMap.put(name, response.getHeader(name));
}
return headerMap;
}
/**
* 获取响应 body 参数
*
* @return {@link String }
* @since 2.11.0
*/
public static String getResponseBody() {
HttpServletResponse response = getResponse();
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
String body = wrapper.getResponseContent();
return JSONUtils.isTypeJSON(body) ? body : null;
}
return null;
}
/**
* 获取响应参数
*
* @return {@link Map }<{@link String }, {@link Object }>
* @since 2.11.0
*/
public static Map<String, Object> getResponseParams() {
String body = getResponseBody();
return CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)
? JSONUtils.toBean(body, Map.class)
: null;
}
/**
* 获取 HTTP Session
*
* @return HttpSession
* @since 2.11.0
*/
public static HttpSession getSession() {
HttpServletRequest request = getRequest();
return request != null ? request.getSession() : null;
}
/**
* 获取 HTTP Request
*
* @return HttpServletRequest
* @since 2.11.0
*/
public static HttpServletRequest getRequest() {
ServletRequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
return null;
}
return attributes.getRequest();
}
/**
* 获取 HTTP Response
*
* @return HttpServletResponse
* @since 2.11.0
*/
public static HttpServletResponse getResponse() {
ServletRequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
return null;
}
return attributes.getResponse();
}
/**
* 获取请求属性
*
* @return {@link ServletRequestAttributes }
* @since 2.11.0
*/
public static ServletRequestAttributes getRequestAttributes() {
try {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes)attributes;
} catch (Exception e) {
return null;
}
}
/**
* 追加查询字符串
*
* @param queryString 查询字符串
* @return {@link StringBuilder }
*/
private static StringBuilder appendQueryString(String queryString) {
HttpServletRequest request = getRequest();
if (request == null) {
return new StringBuilder();
}
return new StringBuilder().append(request.getRequestURL())
.append(StringConstants.QUESTION_MARK)
.append(queryString);
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.web.util;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import jakarta.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.http.server.PathContainer;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import top.continew.starter.core.constant.StringConstants;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Spring Web 工具类
*
* @author Charles7c
* @since 1.1.1
*/
public class SpringWebUtils {
private SpringWebUtils() {
}
/**
* 路径是否匹配
*
* @param path 路径
* @param patterns 匹配模式列表
* @return 是否匹配
* @since 2.6.0
*/
public static boolean isMatch(String path, List<String> patterns) {
return patterns.stream().anyMatch(pattern -> isMatch(path, pattern));
}
/**
* 路径是否匹配
*
* @param path 路径
* @param patterns 匹配模式列表
* @return 是否匹配
* @since 2.6.0
*/
public static boolean isMatch(String path, String... patterns) {
return Arrays.stream(patterns).anyMatch(pattern -> isMatch(path, pattern));
}
/**
* 路径是否匹配
*
* @param path 路径
* @param pattern 匹配模式
* @return 是否匹配
* @since 2.4.0
*/
public static boolean isMatch(String path, String pattern) {
PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern);
PathContainer pathContainer = PathContainer.parsePath(path);
return pathPattern.matches(pathContainer);
}
/**
* 取消注册静态资源映射
*
* @param handlerMap 静态资源映射
*/
public static void deRegisterResourceHandler(Map<String, String> handlerMap) {
ApplicationContext applicationContext = SpringUtil.getApplicationContext();
// 获取已经注册的映射
final HandlerMapping resourceHandlerMapping = applicationContext
.getBean("resourceHandlerMapping", HandlerMapping.class);
final Map<String, Object> oldHandlerMap = (Map<String, Object>)ReflectUtil
.getFieldValue(resourceHandlerMapping, "handlerMap");
// 移除之前注册的映射
for (Map.Entry<String, String> entry : handlerMap.entrySet()) {
String pathPattern = CharSequenceUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN);
oldHandlerMap.remove(pathPattern);
}
}
/**
* 注册静态资源映射
*
* @param handlerMap 静态资源映射
*/
public static void registerResourceHandler(Map<String, String> handlerMap) {
ApplicationContext applicationContext = SpringUtil.getApplicationContext();
// 获取已经注册的映射
final HandlerMapping resourceHandlerMapping = applicationContext
.getBean("resourceHandlerMapping", HandlerMapping.class);
final Map<String, Object> oldHandlerMap = (Map<String, Object>)ReflectUtil
.getFieldValue(resourceHandlerMapping, "handlerMap");
// 重新注册映射
final ServletContext servletContext = applicationContext.getBean(ServletContext.class);
final ContentNegotiationManager contentNegotiationManager = applicationContext
.getBean("mvcContentNegotiationManager", ContentNegotiationManager.class);
final UrlPathHelper urlPathHelper = applicationContext.getBean("mvcUrlPathHelper", UrlPathHelper.class);
final ResourceHandlerRegistry resourceHandlerRegistry = new ResourceHandlerRegistry(applicationContext, servletContext, contentNegotiationManager, urlPathHelper);
for (Map.Entry<String, String> entry : handlerMap.entrySet()) {
// 移除之前注册的映射
String pathPattern = CharSequenceUtil.appendIfMissing(CharSequenceUtil.removeSuffix(entry
.getKey(), StringConstants.SLASH), StringConstants.PATH_PATTERN);
oldHandlerMap.remove(pathPattern);
// 重新注册映射
String resourceLocations = CharSequenceUtil.appendIfMissing(entry.getValue(), StringConstants.SLASH);
resourceHandlerRegistry.addResourceHandler(pathPattern).addResourceLocations("file:" + resourceLocations);
}
final Map<String, ?> additionalUrlMap = ReflectUtil
.<SimpleUrlHandlerMapping>invoke(resourceHandlerRegistry, "getHandlerMapping")
.getUrlMap();
ReflectUtil.<Void>invoke(resourceHandlerMapping, "registerHandlers", additionalUrlMap);
}
}

View File

@@ -1,40 +0,0 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.continew</groupId>
<artifactId>continew-starter-web</artifactId>
<version>${revision}</version>
</parent>
<artifactId>continew-starter-web-support</artifactId>
<description>ContiNew Starter Web 模块 - 增强支持模块</description>
<dependencies>
<!-- Web 模块 - 核心模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-web-core</artifactId>
</dependency>
<!-- API 文档模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-api-doc</artifactId>
<optional>true</optional>
</dependency>
<!-- Undertow 服务器(采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- Graceful ResponseSpring Boot技术栈下的优雅响应处理组件可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程提高开发效率提高代码质量 -->
<dependency>
<groupId>com.feiniaojin</groupId>
<artifactId>graceful-response</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -9,12 +9,46 @@
<version>${revision}</version>
</parent>
<packaging>pom</packaging>
<artifactId>continew-starter-web</artifactId>
<description>ContiNew Starter Web 模块</description>
<modules>
<module>continew-starter-web-core</module>
<module>continew-starter-web-support</module>
</modules>
<dependencies>
<!-- JSON 模块 - Jackson -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-json-jackson</artifactId>
</dependency>
<!-- API 文档模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-api-doc</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot Web提供 Spring MVC Web 开发能力,默认内置 Tomcat 服务器) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 移除内置 Tomcat 服务器 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Undertow 服务器(采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- Graceful Response一个Spring Boot技术栈下的优雅响应处理组件可以帮助开发者完成响应数据封装、异常处理、错误码填充等过程提高开发效率提高代码质量 -->
<dependency>
<groupId>com.feiniaojin</groupId>
<artifactId>graceful-response</artifactId>
</dependency>
</dependencies>
</project>