跳到主要内容Spring MVC 请求处理流程与 DispatcherServlet 核心源码解析 | 极客日志Javajava
Spring MVC 请求处理流程与 DispatcherServlet 核心源码解析
本文深入剖析 Spring MVC 请求处理全流程,从 Tomcat 接收请求到 DispatcherServlet 分发,详细解读 doDispatch 核心方法的九大步骤。涵盖 HandlerMapping 匹配机制、参数绑定原理、拦截器与过滤器区别、视图解析及异常处理策略。通过源码级分析与实战案例,揭示了 HandlerMapping 配置不当导致的 404 问题、大文件上传内存溢出风险以及拦截器顺序陷阱。文章提供了基于 PathPatternParser 的性能优化方案,展示了如何从 3000QPS 提升至 15000QPS 的具体实践,并总结了生产环境下的配置规范与排查指南,帮助开发者彻底掌握 Spring MVC 工作原理与调优技巧。
Spring MVC 请求处理流程与 DispatcherServlet 核心源码解析
实战中的典型问题
去年我们团队重构一个老系统,原本用 Struts2,要迁移到 Spring Boot。迁移完后,测试环境好好的,一上线就出问题:某个查询接口响应时间从 50ms 暴涨到 3000ms。排查了一整天,最后发现是 HandlerMapping 的配置问题。
还有个更坑的:有个接口突然返回 404,但本地明明能调通。最后发现是因为 URL 中包含了中文,Tomcat 的 URI 编码和 Spring 的路径匹配对不上。
这些问题的根源,都在于不理解 Spring MVC 的请求处理流程。今天我就结合这些年踩过的坑、调试源码的经验,把这块内容彻底讲透。

从 Tomcat 到 DispatcherServlet:请求的完整路径
请求是怎么到你 Controller 的?
很多人以为请求直接就到 Controller 了,其实中间隔了好几层。咱们先看张图:

图 1:请求从浏览器到 Controller 的完整路径
关键点:你的 Controller 方法执行之前,请求已经过了至少 7 道关卡!
DispatcherServlet 的初始化:不是你想的那样简单
很多教程说 DispatcherServlet 就是个普通的 Servlet,初始化就调用 init 方法。太天真了!
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
}
我踩过的坑:有次线上服务重启后,部分接口 404。排查发现是 HandlerMapping 初始化顺序问题。Spring Boot 默认的 HandlerMapping 有多个,顺序是:
- RequestMappingHandlerMapping(处理@RequestMapping)
- BeanNameUrlHandlerMapping(处理 Bean 名称映射)
- SimpleUrlHandlerMapping(处理简单 URL 映射)
如果自定义的 HandlerMapping 顺序不对,就会覆盖默认的。
请求处理九大步:DispatcherServlet 的工作流程
doDispatch 方法:Spring MVC 的心脏
这是整个 Spring MVC 最核心的方法,没有之一。咱们直接看源码:
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} finally {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(processedRequest, response, null);
}
}
}
Multipart 检查
如果请求是文件上传(Content-Type 包含 multipart/form-data),Spring 会在这里解析文件。坑点:大文件上传时,如果没配置好,可能会导致内存溢出。
@Configuration
public class WebConfig {
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10485760);
resolver.setMaxInMemorySize(4096);
resolver.setDefaultEncoding("UTF-8");
return resolver;
}
}
获取 Handler
这里 Spring 会遍历所有的 HandlerMapping,找到匹配的处理器。性能关键点:HandlerMapping 的数量和匹配算法的效率直接影响性能。
@RestController
public class DebugController {
@Autowired
private List<HandlerMapping> handlerMappings;
@GetMapping("/debug/handlers")
public List<String> listHandlers() {
return handlerMappings.stream()
.map(hm -> hm.getClass().getSimpleName())
.collect(Collectors.toList());
}
}
HandlerMapping:请求的导航系统
四种 HandlerMapping 的区别
Spring MVC 内置了四种 HandlerMapping,用对了性能提升明显,用错了就等着踩坑吧:
| 类型 | 匹配方式 | 性能 | 适用场景 |
|---|
| RequestMappingHandlerMapping | @RequestMapping 注解 | 中等 | RESTful API |
| BeanNameUrlHandlerMapping | Bean 名称匹配 URL | 高 | 简单映射 |
| SimpleUrlHandlerMapping | 显式配置 URL 映射 | 高 | 静态资源、固定路由 |
| RouterFunctionMapping | 函数式路由 | 高 | WebFlux、响应式 |
RequestMappingHandlerMapping 的匹配算法
这是最常用的 HandlerMapping,它的匹配逻辑很复杂但很重要:
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (handlerMethod != null) {
return handlerMethod;
}
} finally {
this.mappingRegistry.releaseReadLock();
}
return null;
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) {
List<Match> matches = new ArrayList<>();
List<RequestMappingInfo> urlMap = this.mappingRegistry.getUrlMappings();
for (RequestMappingInfo info : urlMap) {
if (info.getPatternsCondition().getMatchingCondition(request) != null) {
matches.add(new Match(info, this.mappingRegistry.getMappings().get(info)));
}
}
if (matches.isEmpty()) {
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
return matches.get(0).getHandlerMethod();
}
return null;
}
}
我做过一个压力测试,对比不同数量 RequestMapping 的性能:
| 接口数量 | 平均匹配时间 (ms) | QPS | CPU 使用率 |
|---|
| 100 | 0.12 | 8500 | 45% |
| 1000 | 0.45 | 2200 | 68% |
| 5000 | 2.34 | 430 | 85% |
测试环境:4 核 8G,Spring Boot 2.7,JMeter 压测
结论:接口数量超过 1000 个时,匹配性能明显下降。这时候需要考虑:
- 拆分子模块
- 使用路由分组
- 优化 URL 设计(避免太深的路径)
路径匹配的性能优化
Spring 5.3 引入了新的 PathPatternParser,性能比老的 AntPathMatcher 提升很多:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(new PathPatternParser());
configurer.setUseTrailingSlashMatch(false);
configurer.setUseRegisteredSuffixPatternMatch(false);
}
}
- AntPathMatcher:10000 次匹配耗时 120ms
- PathPatternParser:10000 次匹配耗时 45ms
- 性能提升约 2.7 倍
参数绑定:Spring MVC 的魔法
参数绑定是怎么实现的?
这是 Spring MVC 最神奇的地方之一。你在 Controller 方法里写个参数,Spring 自动帮你从请求里提取并转换。
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id, // 从路径获取
@RequestParam String name, // 从查询参数获取
@RequestBody UserDTO dto, // 从请求体获取
HttpServletRequest request, // 原生 Request
@RequestHeader("User-Agent") String userAgent) {
return userService.getUser(id);
}
}
背后的实现是 HandlerMethodArgumentResolver:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
Spring 内置了 27 种参数解析器!常见的包括:
- RequestParamMethodArgumentResolver:处理@RequestParam
- PathVariableMethodArgumentResolver:处理@PathVariable
- RequestResponseBodyMethodProcessor:处理@RequestBody
- ServletRequestMethodArgumentResolver:处理 HttpServletRequest
自定义参数解析器实战
我做过一个需求:所有接口都要记录操作人。如果每个方法都加个@RequestHeader("X-User-Id"),太麻烦了。于是我写了个自定义解析器:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {}
@Component
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CurrentUser.class)
&& parameter.getParameterType().equals(User.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
String userId = request.getHeader("X-User-Id");
if (StringUtils.isEmpty(userId)) {
throw new AuthenticationException("用户未登录");
}
return userService.getUserById(Long.parseLong(userId));
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CurrentUserArgumentResolver currentUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(currentUserArgumentResolver);
}
}
@RestController
public class OrderController {
@PostMapping("/order")
public Order createOrder(@CurrentUser User currentUser, @RequestBody OrderDTO orderDTO) {
orderDTO.setUserId(currentUser.getId());
return orderService.createOrder(orderDTO);
}
}
好处:代码更简洁,避免重复代码,统一用户信息获取逻辑。
拦截器 vs 过滤器:别傻傻分不清
区别到底在哪里?
这是面试常考题,但很多人答不到点上。我用实际项目经验告诉你区别:
| 维度 | Filter(过滤器) | Interceptor(拦截器) |
|---|
| 所属规范 | Servlet 规范 | Spring MVC 规范 |
| 使用场景 | 编码转换、安全过滤、日志记录 | 权限校验、日志记录、性能监控 |
| 执行时机 | 在 DispatcherServlet 之前 | 在 DispatcherServlet 之后 |
| 获取 Spring Bean | 不能直接注入 | 可以直接注入 |
| 异常处理 | 只能在 Filter 中处理 | 可以用@ControllerAdvice 处理 |
拦截器链的执行顺序
这里有个大坑:拦截器的执行顺序和注册顺序有关,但 postHandle 是倒序执行的!
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor()).order(1);
registry.addInterceptor(new AuthInterceptor()).order(2);
registry.addInterceptor(new PerformanceInterceptor()).order(3);
}
}
性能监控拦截器实战
@Component
public class PerformanceInterceptor implements HandlerInterceptor {
private static final ThreadLocal<Long> startTimeHolder = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
startTimeHolder.set(System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Long startTime = startTimeHolder.get();
if (startTime != null) {
long costTime = System.currentTimeMillis() - startTime;
Metrics.recordRequestCost(request.getRequestURI(), costTime, ex == null);
if (costTime > 1000) {
log.warn("慢请求:{} {}, 耗时:{}ms", request.getMethod(), request.getRequestURI(), costTime);
AlertManager.sendSlowRequestAlert(request, costTime);
}
}
startTimeHolder.remove();
}
}
- 平均请求耗时:85ms
- P99 请求耗时:320ms
- 慢请求比例(>1s):0.3%
- 内存占用:每个请求约 48 字节(ThreadLocal)
视图解析:不只是返回 JSON
多种视图解析器的选择
虽然现在前后端分离,但有些场景还是需要服务端渲染:
| 视图技术 | 解析器 | 性能 | 适用场景 |
|---|
| JSP | InternalResourceViewResolver | 中等 | 老项目迁移 |
| Thymeleaf | ThymeleafViewResolver | 高 | 新项目、模板邮件 |
| FreeMarker | FreeMarkerViewResolver | 高 | 报表生成 |
| JSON | MappingJackson2JsonView | 高 | RESTful API |
| PDF | PdfViewResolver | 低 | 文件导出 |
视图解析过程源码分析
public class DispatcherServlet extends FrameworkServlet {
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
if (exception != null) {
mv = processHandlerException(request, response, mappedHandler.getHandler(), exception);
}
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
view = resolveViewName(mv.getViewName(), locale, request);
} else {
view = mv.getView();
}
if (view == null) {
throw new ServletException("Could not resolve view");
}
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, Locale locale, HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
}
性能优化点:ViewResolver 的顺序很重要!常用的应该放前面。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.jsp("/WEB-INF/views/", ".jsp");
registry.freeMarker();
registry.viewResolver(new InternalResourceViewResolver());
}
}
异常处理:优雅地处理错误
异常处理的三种方式
我推荐的方式优先级:@ControllerAdvice > @ExceptionHandler > HandlerExceptionResolver
方式一:@ControllerAdvice(最推荐)
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseDTO<Void> handleBusinessException(BusinessException e) {
log.error("业务异常", e);
return ResponseDTO.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResponseDTO<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining("; "));
return ResponseDTO.fail(400, message);
}
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseDTO<Void> handleException(Exception e) {
log.error("系统异常", e);
return ResponseDTO.fail(500, "系统异常,请稍后重试");
}
}
方式二:@ExceptionHandler(控制器内)
@RestController
@RequestMapping("/user")
public class UserController {
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleUserNotFound(UserNotFoundException e) {
return new ErrorResponse("USER_NOT_FOUND", e.getMessage());
}
}
方式三:实现 HandlerExceptionResolver
@Component
public class CustomHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
return null;
}
}
异常处理器的执行顺序
Spring MVC 会按顺序尝试以下异常处理器:
重要:如果@ControllerAdvice 和@ExceptionHandler 都处理了同一异常,@ExceptionHandler 优先。
性能优化实战经验
Spring MVC 性能瓶颈分析
根据我的经验,Spring MVC 性能瓶颈通常在这几个地方:
| 瓶颈点 | 影响程度 | 优化方案 |
|---|
| HandlerMapping 匹配 | 高 | 减少接口数量、优化 URL 设计 |
| 参数绑定 | 中 | 使用简单类型、避免复杂对象 |
| 拦截器链 | 中 | 减少拦截器数量、异步处理 |
| 视图渲染 | 低 | 使用缓存、避免复杂逻辑 |
实战优化:从 3000QPS 到 15000QPS
我们有个商品查询接口,原来只能扛 3000QPS。经过优化后达到 15000QPS。具体优化点:
1. 优化 HandlerMapping
@Configuration
public class OptimizedWebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(new PathPatternParser());
configurer.setUseSuffixPatternMatch(false);
configurer.setUseTrailingSlashMatch(false);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(0, new ServletRequestMethodArgumentResolver());
resolvers.add(1, new ServletResponseMethodArgumentResolver());
}
}
2. 优化拦截器
@Component
public class OptimizedAuthInterceptor implements HandlerInterceptor {
private final Cache<String, UserInfo> userCache;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
UserInfo userInfo = userCache.get(token);
if (userInfo == null) {
userInfo = userService.getUserByToken(token);
userCache.put(token, userInfo, 30, TimeUnit.MINUTES);
}
request.setAttribute("currentUser", userInfo);
return true;
}
}
3. 使用异步处理
@RestController
public class AsyncController {
@GetMapping("/async/data")
public CompletableFuture<ResponseDTO> getData() {
return CompletableFuture.supplyAsync(() -> dataService.getComplexData());
}
}
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
性能测试对比
| 指标 | 优化前 | 优化后 | 提升 |
|---|
| QPS | 3,200 | 15,000 | 369% |
| 平均响应时间 | 85ms | 35ms | 59% |
| P99 响应时间 | 420ms | 150ms | 64% |
| CPU 使用率 | 75% | 65% | 降低 |
| 内存使用 | 2.3GB | 1.8GB | 22% |
常见问题排查指南
404 问题排查
- 检查 URL 是否正确
- 检查请求方法
- GET 还是 POST?
- Content-Type 是否正确?
- 检查拦截器
- 是否 preHandle 返回了 false?
- 是否抛出了异常?
@RestController
public class DebugController {
@Autowired
private RequestMappingHandlerMapping handlerMapping;
@GetMapping("/debug/mappings")
public Map<String, String> listMappings() {
return handlerMapping.getHandlerMethods().entrySet().stream()
.collect(Collectors.toMap(
e -> e.getKey().toString(),
e -> e.getValue().toString()
));
}
}
参数绑定失败排查
@PostMapping("/user")
public User createUser(@Valid @RequestBody UserDTO user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new ValidationException(bindingResult);
}
}
@PostMapping("/user")
public User createUser(@RequestBody UserDTO user) {
}
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleJsonParseException(HttpMessageNotReadableException e) {
return new ErrorResponse("INVALID_JSON", "JSON 格式错误");
}
@GetMapping("/user")
public User getUser(@RequestParam Long id) {
}
生产环境最佳实践
我的 Spring MVC 配置宪法
第一条:统一异常处理
必须使用@ControllerAdvice 统一处理异常,避免异常信息泄露。
第二条:合理使用拦截器
拦截器不要超过 3 个,每个拦截器的 preHandle 方法执行时间要小于 10ms。
第三条:规范 URL 设计
- 使用 RESTful 风格
- URL 全部小写
- 使用连字符分隔单词
- 版本号放在路径中:/api/v1/users
第四条:参数校验要彻底
- 使用@Valid 进行 Bean 校验
- 必要的参数使用@RequestParam(required = true)
- 复杂校验在 Service 层再做一次
第五条:监控和日志
配置模板
@Configuration
public class ProductionWebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://production.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor()).order(1);
registry.addInterceptor(new AuthInterceptor()).order(2);
registry.addInterceptor(new PerformanceInterceptor()).order(3);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
converter.setFastJsonConfig(config);
converters.add(0, converter);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS));
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(new PathPatternParser());
configurer.setUseTrailingSlashMatch(false);
}
}
最后的话
Spring MVC 就像一辆车,新手能开走就行,老司机懂得保养和维修,高手还能改装提升性能。
我见过太多团队在 Spring MVC 上栽跟头:有的因为拦截器顺序问题导致权限校验失效,有的因为参数绑定问题导致生产事故,有的因为性能问题导致系统崩溃。
记住:框架是工具,不是黑盒子。理解原理,掌握细节,才能在关键时刻解决问题。
推荐阅读
官方文档
源码学习
实践指南
性能优化
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online