深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析 - 实践
Spring MVC作为Spring生态的核心Web框架,其设计哲学源于经典的MVC模式,但通过创新的组件化设计实现了高度解耦。其核心架构包含三大核心组件:
- DispatcherServlet:前端控制器,承担请求分发中枢角色
- HandlerMapping:请求路由解析器,实现URL到处理方法的精准映射
- HandlerAdapter:处理器适配器,统一不同类型处理器的调用方式
这三个组件构成请求处理的核心管道,配合拦截器、视图解析器等辅助组件,形成完整的请求生命周期管理
一、Spring MVC 核心架构:DispatcherServlet 的 “中央调度” 模式
Spring MVC 的核心设计模式是前端控制器模式(Front Controller Pattern),而 DispatcherServlet 正是这个模式的实现者。它承担了请求分发、组件协调的核心职责,是整个 Spring MVC 框架的 “大脑”。
1.1 DispatcherServlet 的初始化流程
在我们的配置类MvcConfig
中,通过@Bean
注解显式定义了 DispatcherServlet:
@Bean
public DispatcherServlet dispatcherServlet() {
log.info("Creating DispatcherServlet instance");
return new DispatcherServlet();
}
当 Spring 容器初始化时,DispatcherServlet 会经历以下关键阶段:
- 构造阶段:创建 DispatcherServlet 实例,此时尚未初始化核心组件
- 初始化阶段:容器调用
init()
方法,触发onRefresh()
钩子函数 - 策略初始化:在
onRefresh()
中调用initStrategies()
,初始化九大核心组件
九大核心策略组件及其作用
组件类型 | 核心作用 | 默认实现 | 自定义扩展点 |
---|---|---|---|
MultipartResolver | 处理文件上传请求 | StandardServletMultipartResolver | 可实现自定义文件上传逻辑 |
HandlerMapping | 请求 URL 到处理器的映射 | RequestMappingHandlerMapping | 自定义 URL 匹配规则 |
HandlerAdapter | 调用处理器方法的适配器 | RequestMappingHandlerAdapter | 支持特殊参数类型解析 |
ViewResolver | 逻辑视图名到物理视图的解析 | InternalResourceViewResolver | 集成 Thymeleaf、Freemarker 等 |
LocaleResolver | 处理国际化资源 | AcceptHeaderLocaleResolver | 基于 Cookie/Session 的国际化 |
ThemeResolver | 管理应用主题 | FixedThemeResolver | 动态主题切换 |
HandlerExceptionResolver | 统一异常处理 | ExceptionHandlerExceptionResolver | 全局异常处理策略 |
RequestToViewNameTranslator | 自动生成视图名 | DefaultRequestToViewNameTranslator | 自定义视图名生成规则 |
FlashMapManager | 跨请求数据传递 | SessionFlashMapManager | 分布式环境下的 Flash 数据管理 |
1.2 DispatcherServlet 的注册机制
创建 DispatcherServlet 实例后,还需要将其注册到 Servlet 容器中,这一过程由DispatcherServletRegistrationBean
完成:
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(
DispatcherServlet servlet, WebMvcProperties mvcProperties) {
DispatcherServletRegistrationBean bean =
new DispatcherServletRegistrationBean(servlet, "/");
bean.setLoadOnStartup(mvcProperties.getServlet().getLoadOnStartup());
return bean;
}
这里有两个关键配置:
- URL 映射路径:
"/"
表示拦截所有请求(除了 JSP 请求,由 Tomcat 默认 Servlet 处理) - 加载顺序:
loadOnStartup
值决定 Servlet 的初始化时机,正数表示容器启动时初始化,数值越小优先级越高
二、请求映射的底层实现:RequestMappingHandlerMapping
请求映射是 Spring MVC 的核心功能之一,用户通过@GetMapping
、@PostMapping
等注解定义的接口,最终都由RequestMappingHandlerMapping
来管理和匹配。
2.1 请求映射的注册过程
在DispatcherServletInitCase
的调试代码中,我们可以看到请求映射的注册详情:
RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class)
;
mapping.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) ->
{
logHandlerMethodDetails(handlerMethod, requestMappingInfo);
});
RequestMappingHandlerMapping
在初始化时会执行以下步骤:
- 组件扫描:扫描所有带有
@Controller
或@RestController
注解的类 - 方法解析:解析类和方法上的
@RequestMapping
及其变体注解 - 条件封装:将 URL 路径、HTTP 方法、请求参数、请求头等条件封装为
RequestMappingInfo
- 映射注册:将
RequestMappingInfo
与对应的HandlerMethod
(封装控制器方法)存入MappingRegistry
2.2 请求匹配的核心逻辑
当请求到达时,RequestMappingHandlerMapping
会执行复杂的匹配逻辑,找到最适合的处理器方法:
- 路径匹配:根据请求 URL 匹配
@RequestMapping
定义的路径模式(支持 Ant 风格通配符) - 方法匹配:验证请求的 HTTP 方法(GET/POST/PUT 等)是否与注解定义一致
- 参数匹配:检查请求是否包含
params
属性指定的参数(如params = "id"
要求必须包含 id 参数) - 头信息匹配:验证请求头是否满足
headers
属性的要求(如headers = "X-Auth"
要求包含 X-Auth 头) - 内容类型匹配:根据
consumes
属性验证请求的Content-Type
(如consumes = "application/json"
)
以代码中的getUser
方法为例:
@GetMapping(value = "/user", params = "id", headers = "X-Auth", consumes = "application/json")
public String getUser(@RequestParam("id") Long id, @RequestHeader("X-Auth") String token) {
// 业务逻辑
}
该方法的匹配条件被封装为RequestMappingInfo
,包含:
- 路径:
/dispatcher/servlet/user
(类上的@RequestMapping
与方法上的路径拼接) - HTTP 方法:GET
- 参数条件:必须包含 id 参数
- 请求头条件:必须包含 X-Auth 头
- 内容类型:仅接受 application/json
三、自定义参数解析:HandlerMethodArgumentResolver 的实践
Spring MVC 默认支持多种参数类型的解析(如@RequestParam
、@RequestHeader
等),但在实际开发中,我们常常需要自定义参数解析逻辑,例如统一的 Token 参数处理。
3.1 自定义参数解析器的实现
代码中的TokenArgumentResolver
实现了HandlerMethodArgumentResolver
接口,用于解析带有@Token
注解的参数:
@Slf4j
public class TokenArgumentResolver
implements HandlerMethodArgumentResolver {
private static final String DEFAULT_TOKEN_HEADER = "X-Auth-Token";
private static final String DEFAULT_TOKEN_PARAM = "authToken";
private static final boolean DEFAULT_VALIDATE_TOKEN = true;
@Override
public boolean supportsParameter(@NonNull MethodParameter parameter) {
Token tokenAnnotation = parameter.getParameterAnnotation(Token.class)
;
if (nonNull(tokenAnnotation)) {
log.debug("Detected Token annotation on parameter: {}", parameter.getParameterName());
return true;
}
return false;
}
@Override
public Object resolveArgument(@NonNull MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
@NonNull NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) {
// 多路径获取token
String token = getHeaderToken(webRequest).orElseGet(() ->
getParameterToken(webRequest).orElse(null));
log.info("Resolved token: {}", maskToken(token));
if (DEFAULT_VALIDATE_TOKEN &&
nonNull(token)) {
validateJwtToken();
}
return token;
}
/**
* 从请求头获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getHeaderToken(NativeWebRequest request) {
// 可配置参数
String tokenHeader = DEFAULT_TOKEN_HEADER;
String headerValue = request.getHeader(tokenHeader);
log.debug("Checking header token from: {}", tokenHeader);
return Optional.ofNullable(headerValue);
}
/**
* 从请求参数获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getParameterToken(NativeWebRequest request) {
String tokenParam = DEFAULT_TOKEN_PARAM;
String paramValue = request.getParameter(tokenParam);
log.debug("Checking parameter token from: {}", tokenParam);
return Optional.ofNullable(paramValue);
}
/**
* JWT Token校验
*/
private void validateJwtToken() {
}
private String maskToken(String token) {
return token != null && token.length() >
4
? "****" + token.substring(token.length() - 4)
: "****";
}
}
3.2 参数解析器的注册与执行流程
自定义参数解析器需要注册到RequestMappingHandlerAdapter
中才能生效:
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
log.debug("Creating custom RequestMappingHandlerAdapter");
MyRequestMappingHandlerAdapter adapter = new MyRequestMappingHandlerAdapter();
// 添加自定义参数解析器
TokenArgumentResolver tokenResolver = new TokenArgumentResolver();
adapter.setCustomArgumentResolvers(List.of(tokenResolver));
// 添加自定义返回值处理器
JsonReturnValueHandler jsonHandler = new JsonReturnValueHandler();
adapter.setCustomReturnValueHandlers(List.of(jsonHandler));
log.info("Custom adapter configured with {} argument resolvers and {} return handlers",
Objects.requireNonNull(adapter.getCustomArgumentResolvers()).size(),
Objects.requireNonNull(adapter.getCustomReturnValueHandlers()).size());
return adapter;
}
参数解析的执行流程如下:
- 预处理:DispatcherServlet 接收到请求后,通过 HandlerMapping 找到对应的 HandlerMethod
- 适配准备:DispatcherServlet 调用 HandlerAdapter 的
supports()
方法判断是否支持该 HandlerMethod - 参数解析:HandlerAdapter 遍历所有注册的 ArgumentResolver,通过
supportsParameter()
找到合适的解析器 - 方法调用:使用解析得到的参数值,通过反射调用目标处理器方法
四、自定义返回值处理:HandlerMethodReturnValueHandler 的应用
除了参数解析,Spring MVC 还允许我们自定义返回值的处理逻辑。代码中的JsonReturnValueHandler
实现了HandlerMethodReturnValueHandler
接口,用于将方法返回值自动序列化为 JSON 格式。
4.1 自定义返回值处理器的实现
@Slf4j
public class JsonReturnValueHandler
implements HandlerMethodReturnValueHandler {
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String APPLICATION_JSON = MediaType.APPLICATION_JSON_VALUE;
@Override
public boolean supportsReturnType(MethodParameter returnType) {
log.debug("Checking return type support: {}", returnType.getParameterName());
return returnType.hasMethodAnnotation(JsonObject.class)
;
}
@Override
public void handleReturnValue(Object returnValue, @NonNull MethodParameter returnType, @NonNull ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
log.info("Handling JSON return value for method: {}", Objects.requireNonNull(returnType.getMethod()).getName());
// 获取响应对象
HttpServletResponse response = Objects.requireNonNull(
webRequest.getNativeResponse(HttpServletResponse.class)
,
"HttpServletResponse not found in request"
);
// 设置响应头
response.setContentType(APPLICATION_JSON);
response.setCharacterEncoding(DEFAULT_CHARSET);
log.debug("Set response content type to {}", APPLICATION_JSON);
// 序列化处理
String jsonStr = JSON.toJSON(returnValue);
log.debug("Serialized return value: {}", jsonStr);
// 写入响应体
response.getWriter().write(jsonStr);
response.getWriter().flush();
// 标记请求已处理
mavContainer.setRequestHandled(true);
log.debug("Marked request as fully handled");
}
}
4.2 返回值处理的核心机制
返回值处理的关键在于mavContainer.setRequestHandled(true)
这行代码,它告诉 DispatcherServlet:
- 该请求的响应已经完全处理完毕
- 不需要再进行后续的视图解析和视图渲染流程
- 直接返回响应给客户端
这与传统的 MVC 流程(处理器→ModelAndView→视图解析→渲染)不同,适用于 RESTful API 等不需要视图渲染的场景。
五、完整请求处理流程:从请求到响应的全链路
结合以上所有组件,我们可以梳理出 Spring MVC 完整的请求处理流程:
graph TD
A[客户端发送请求] --> B[Tomcat容器接收请求]
B --> C[DispatcherServlet拦截请求]
C --> D[调用HandlerMapping查找HandlerMethod]
D --> E[构建HandlerExecutionChain(包含拦截器)]
E --> F[调用HandlerAdapter适配处理器]
F --> G[HandlerAdapter调用ArgumentResolver解析参数]
G --> H[反射调用目标Controller方法]
H --> I[HandlerAdapter调用ReturnValueHandler处理返回值]
I --> J[通过拦截器的afterCompletion方法]
J --> K[返回响应给客户端]
各阶段的核心职责
- 请求接收阶段:Tomcat 容器将 HTTP 请求封装为 HttpServletRequest 对象
- 请求分发阶段:DispatcherServlet 作为前端控制器,协调各组件工作
- 处理器查找阶段:HandlerMapping 根据请求信息找到匹配的 HandlerMethod
- 参数解析阶段:ArgumentResolver 解析请求参数,为方法调用做准备
- 方法调用阶段:通过反射调用 Controller 的目标方法
- 返回值处理阶段:ReturnValueHandler 处理方法返回值,生成响应
- 响应返回阶段:将处理结果封装为 HTTP 响应,返回给客户端
六、自定义配置的最佳实践与扩展建议
基于本文的代码示例,我们可以总结出 Spring MVC 自定义配置的最佳实践:
6.1 组件扩展的原则
- 面向接口编程:自定义组件应实现 Spring 提供的标准接口(如 HandlerMethodArgumentResolver),而非继承具体实现类
- 优先级控制:通过
setCustomArgumentResolvers
而非addArgumentResolvers
来确保自定义组件的优先级 - 职责单一:每个自定义组件应只负责单一功能(如 Token 解析、JSON 序列化)
- 可配置性:将硬编码的配置(如 Token 的请求头名称)改为可配置项,通过
@ConfigurationProperties
绑定
6.2 常见扩展场景
- 统一认证授权:扩展 TokenArgumentResolver,集成 JWT、OAuth2 等认证机制
- 统一返回格式:自定义 ReturnValueHandler,将所有接口返回值封装为统一格式(如 {code:200, data:{}, message:“success”})
- 特殊参数支持:实现自定义 ArgumentResolver,支持如日期、枚举等特殊类型的参数解析
- 全局异常处理:扩展 HandlerExceptionResolver,统一处理业务异常和系统异常
6.3 性能优化建议
- 组件复用:避免频繁创建自定义组件实例,通过 Spring Bean 管理实现复用
- 缓存机制:对请求映射、参数解析结果等进行缓存,减少重复计算
- 异步处理:对于耗时操作,使用
@Async
注解实现异步处理,提高系统吞吐量 - 拦截器优化:合理使用拦截器,避免在拦截器中执行耗时操作
七、总结
Spring MVC 通过 DispatcherServlet、HandlerMapping、HandlerAdapter 等核心组件,构建了一个松耦合、高可扩展的 MVC 框架。本文通过一份完整的自定义配置代码,深入解析了 Spring MVC 的底层原理,包括:
- DispatcherServlet 的初始化和九大核心组件
- RequestMappingHandlerMapping 的请求映射机制
- HandlerMethodArgumentResolver 的自定义参数解析
- HandlerMethodReturnValueHandler 的自定义返回值处理
- 完整的请求处理流程和组件协作机制
完整代码
package com.dwl.mvc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName DispatcherServletController
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
@Controller
@RequestMapping("/dispatcher/servlet/")
public class DispatcherServletController
{
@GetMapping("index")
public String index() {
log.debug("Handling index request");
return "index";
}
@GetMapping("param")
public String param(@RequestParam("name") String name) {
log.info("Received param request with name: {}", name);
return name;
}
@GetMapping(value = "/user", params = "id", headers = "X-Auth", consumes = "application/json")
public String getUser(@RequestParam("id") Long id, @RequestHeader("X-Auth") String token) {
log.info("Processing user request - ID: {}, Token: {}", id, token);
return id + "+" + token;
}
@PutMapping("/putMapping")
public String putMapping(@Token String token) {
log.debug("PUT request received with token: {}", token);
return token;
}
@RequestMapping("/jsonObject")
@JsonObject
public User jsonObject() {
log.info("Generating JSON response");
User user = new User();
user.setName("Dwl");
user.setAge(18);
return user;
}
@Data
public static class User
{
private String name;
private Integer age;
}
}
package com.dwl.mvc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName Json
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonObject {
}
package com.dwl.mvc;
import com.dwl.json.JSON;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Objects;
/**
* @ClassName JsonReturnValueHandler
* @Description JSON 返回值处理器
* 该处理器通过 {@link JsonObject} 注解标识需要 JSON 序列化的返回值,
* 自动将对象转换为 JSON 格式并设置响应头
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class JsonReturnValueHandler
implements HandlerMethodReturnValueHandler {
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String APPLICATION_JSON = MediaType.APPLICATION_JSON_VALUE;
@Override
public boolean supportsReturnType(MethodParameter returnType) {
log.debug("Checking return type support: {}", returnType.getParameterName());
return returnType.hasMethodAnnotation(JsonObject.class)
;
}
@Override
public void handleReturnValue(Object returnValue, @NonNull MethodParameter returnType, @NonNull ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
log.info("Handling JSON return value for method: {}", Objects.requireNonNull(returnType.getMethod()).getName());
// 获取响应对象
HttpServletResponse response = Objects.requireNonNull(
webRequest.getNativeResponse(HttpServletResponse.class)
,
"HttpServletResponse not found in request"
);
// 设置响应头
response.setContentType(APPLICATION_JSON);
response.setCharacterEncoding(DEFAULT_CHARSET);
log.debug("Set response content type to {}", APPLICATION_JSON);
// 序列化处理
String jsonStr = JSON.toJSON(returnValue);
log.debug("Serialized return value: {}", jsonStr);
// 写入响应体
response.getWriter().write(jsonStr);
response.getWriter().flush();
// 标记请求已处理
mavContainer.setRequestHandled(true);
log.debug("Marked request as fully handled");
}
}
package com.dwl.mvc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.List;
import java.util.Objects;
/**
* @ClassName MvcConfig
* @Description MVC 核心配置类
* <p>
* 功能特性:
* 1. 自动扫描组件(排除 Controller 类,由其他配置处理)
* 2. 配置内嵌 Tomcat 容器参数
* 3. 注册 DispatcherServlet 到 Servlet 容器
* 4. 绑定 MVC 相关配置属性
* <p>
* 设计原理:
* - 基于 Spring Boot 自动配置机制,扩展 WebMvc 配置
* - 通过属性绑定实现配置文件与 Java 对象的映射
* - 遵循 Servlet 规范实现 Servlet 容器初始化
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
* @see DispatcherServlet Spring MVC 的核心调度器
* @see TomcatServletWebServerFactory Tomcat 容器工厂
*/
@Configuration
@ComponentScan
@PropertySource(
value = "classpath:application.yaml",
factory = YamlPropertySourceFactory.class // 自定义 YAML 属性源工厂
// 说明:默认只支持 properties 文件,需通过工厂解析 YAML 格式
)
@EnableConfigurationProperties({
// 启用配置属性绑定
WebMvcProperties.class,
// MVC 框架配置
ServerProperties.class // 服务器配置
})
@Slf4j
public class MvcConfig
{
/**
* 创建内嵌 Tomcat 容器工厂
* <p>
* 配置项来源:
* - application.yaml 中的 server.port
* - server.tomcat.connection-timeout
* 设计要点:
* - 通过参数注入 ServerProperties 实现配置解耦
* - 提供默认端口 8080 的容错机制
*/
@Bean
@Nullable
public TomcatServletWebServerFactory tomcatServletWebServerFactory(@Nullable ServerProperties serverProperties // 通过参数注入配置属性
) {
return new TomcatServletWebServerFactory(serverProperties != null ? serverProperties.getPort() : 8080);
}
/**
* 创建 DispatcherServlet 实例
* <p>
* 核心流程:
* 1. 初始化 Spring MVC 的核心调度器
* 2. 延迟初始化策略:由 Servlet 容器首次访问时初始化
* 3. 支持异步请求处理(Servlet 3.0+)
* 生命周期:
* - 构造阶段:创建 Servlet 实例
* - 初始化阶段:由容器调用 init() 方法
* - 销毁阶段:由容器调用 destroy() 方法
*/
@Bean
public DispatcherServlet dispatcherServlet() {
/*
* 在 onRefresh()方法中,DispatcherServlet 调用 initStrategies()初始化九大核心策略组件
* 组件类型 作用 默认实现
* MultipartResolver 处理文件上传请求 StandardServletMultipartResolver
* HandlerMapping 将请求 URL 映射到具体处理器(Controller) RequestMappingHandlerMapping(注解驱动)
* HandlerAdapter 调用处理器方法,支持不同参数解析和返回值处理 RequestMappingHandlerAdapter
* ViewResolver 将逻辑视图名解析为具体视图(如 JSP、Thymeleaf) InternalResourceViewResolver
* LocaleResolver 解析客户端区域信息,支持国际化 AcceptHeaderLocaleResolver
* ThemeResolver 解析应用主题(如 CSS 样式) FixedThemeResolver
* HandlerExceptionResolver 统一处理请求过程中的异常 ExceptionHandlerExceptionResolver
* RequestToViewNameTranslator 自动生成视图名(若未显式指定) DefaultRequestToViewNameTranslator
* FlashMapManager 管理跨请求数据传递(如重定向时传递参数) SessionFlashMapManager
*/
log.info("Creating DispatcherServlet instance");
return new DispatcherServlet();
}
/**
* 注册 DispatcherServlet 到 Servlet 容器
* <p>
* 关键配置项:
* - URL 映射路径:根路径 "/" 接收所有请求
* - 加载顺序:通过 loadOnStartup 控制初始化优先级
* - Servlet 名称:默认 "dispatcherServlet"
* 扩展点:
* - 可配置多个 DispatcherServlet 实现多应用上下文
* - 支持自定义 Servlet 初始化参数
*/
@Bean
@Nullable
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(
@Nullable DispatcherServlet servlet, // 注入创建的 Servlet 实例
@Nullable WebMvcProperties mvcProperties // 绑定 MVC 配置属性
) {
// 处理可能的null值
int loadOnStartup = mvcProperties != null ? mvcProperties.getServlet().getLoadOnStartup() : -1;
// 没有其他路径,请求都会和'/'匹配,由servlet请求分发到其他的控制器上
DispatcherServletRegistrationBean bean = new DispatcherServletRegistrationBean(servlet != null ? servlet : new DispatcherServlet(), "/");
/*
* setLoadOnStartups设置大于0的值,会在tomcat启动时进行初始化
* 设置初始化顺序(数值越小优先级越高)
* 对应配置文件的配置
* spring:
* mvc:
* servlet:
* load-on-startup: 1
*/
bean.setLoadOnStartup(loadOnStartup);
return bean;
}
/**
* 1.默认行为
* DispatcherServlet在初始化时会从 Spring 容器中查找 HandlerMapping类型的 Bean,并将其作为成员变量。
* 默认实现:如果未显式配置 RequestMappingHandlerMapping,Spring 会通过 DispatcherServlet.properties文件中的默认配置加载 RequestMappingHandlerMapping(前提是容器中存在该 Bean)
* 自动注册:若容器中存在 @Controller或 @RestController注解的类,Spring 会自动扫描并注册 RequestMappingHandlerMapping,无需手动配置
* 2.显式配置的优先级
* 如果用户显式配置了自定义的 HandlerMapping(如 BeanNameUrlHandlerMapping),则 DispatcherServlet会优先使用显式配置的组件
* RequestMappingHandlerMapping是 Spring Boot 通过 WebMvcAutoConfiguration自动注册的 Bean。
* 未启用自动配置:若未使用 @SpringBootApplication或 @EnableAutoConfiguration,Spring 无法自动创建该 Bean
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* 自定义请求处理适配器
* <p>
* 扩展功能:
* - 添加Token参数解析器
* - 注册JSON返回值处理器
* - 自定义消息转换器
*
* @return 自定义适配器Bean
*/
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
log.debug("Creating custom RequestMappingHandlerAdapter");
MyRequestMappingHandlerAdapter adapter = new MyRequestMappingHandlerAdapter();
// 添加自定义参数解析器
TokenArgumentResolver tokenResolver = new TokenArgumentResolver();
adapter.setCustomArgumentResolvers(List.of(tokenResolver));
// 添加自定义返回值处理器
JsonReturnValueHandler jsonHandler = new JsonReturnValueHandler();
adapter.setCustomReturnValueHandlers(List.of(jsonHandler));
log.info("Custom adapter configured with {} argument resolvers and {} return handlers",
Objects.requireNonNull(adapter.getCustomArgumentResolvers()).size(),
Objects.requireNonNull(adapter.getCustomReturnValueHandlers()).size());
return adapter;
}
}
package com.dwl.mvc;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* @ClassName MyRequestMappingHandlerAdapter
* @Description 自定义请求映射处理器适配器
* <p>
* 扩展功能:
* 1. 增强请求参数校验
* 2. 添加请求处理日志
* 3. 支持自定义参数解析器
* 4. 增强返回值处理
* 5. 异常统一处理
* </p>
* <p>
* 设计原理:
* - 继承RequestMappingHandlerAdapter实现请求处理链扩展
* - 重写invokeHandlerMethod方法添加自定义逻辑
* - 通过参数解析器和返回值处理器实现扩展点
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
public class MyRequestMappingHandlerAdapter
extends RequestMappingHandlerAdapter {
@Override
public ModelAndView invokeHandlerMethod(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull HandlerMethod handlerMethod) throws Exception {
return super.invokeHandlerMethod(request, response, handlerMethod);
}
}
package com.dwl.mvc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName Token
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}
package com.dwl.mvc;
import io.micrometer.common.lang.Nullable;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Optional;
import static java.util.Objects.nonNull;
/**
* @ClassName TokenArgumentResolver
* @Description Token参数解析器
* <p>
* 功能特性:
* 1. 自动解析@Token注解标记的参数
* 2. 支持请求头/请求参数双路径获取token
* 3. 集成JWT校验逻辑
* 4. 提供默认值配置
* 5. 异常处理机制
* </p>
* <p>
* 设计原理:
* - 实现HandlerMethodArgumentResolver接口
* - 优先从请求头获取token,回退到请求参数
* - 支持JWT格式token的自动解析
* - 集成Spring Security上下文
* </p>
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class TokenArgumentResolver
implements HandlerMethodArgumentResolver {
private static final String DEFAULT_TOKEN_HEADER = "X-Auth-Token";
private static final String DEFAULT_TOKEN_PARAM = "authToken";
private static final boolean DEFAULT_VALIDATE_TOKEN = true;
@Override
public boolean supportsParameter(@NonNull MethodParameter parameter) {
Token tokenAnnotation = parameter.getParameterAnnotation(Token.class)
;
if (nonNull(tokenAnnotation)) {
log.debug("Detected Token annotation on parameter: {}", parameter.getParameterName());
return true;
}
return false;
}
@Override
public Object resolveArgument(@NonNull MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer,
@NonNull NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) {
// 多路径获取token
String token = getHeaderToken(webRequest).orElseGet(() ->
getParameterToken(webRequest).orElse(null));
log.info("Resolved token: {}", maskToken(token));
if (DEFAULT_VALIDATE_TOKEN &&
nonNull(token)) {
validateJwtToken();
}
return token;
}
/**
* 从请求头获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getHeaderToken(NativeWebRequest request) {
// 可配置参数
String tokenHeader = DEFAULT_TOKEN_HEADER;
String headerValue = request.getHeader(tokenHeader);
log.debug("Checking header token from: {}", tokenHeader);
return Optional.ofNullable(headerValue);
}
/**
* 从请求参数获取token
*
* @param request Web请求
* @return Optional包装的token值
*/
private Optional<
String> getParameterToken(NativeWebRequest request) {
String tokenParam = DEFAULT_TOKEN_PARAM;
String paramValue = request.getParameter(tokenParam);
log.debug("Checking parameter token from: {}", tokenParam);
return Optional.ofNullable(paramValue);
}
/**
* JWT Token校验
*/
private void validateJwtToken() {
}
private String maskToken(String token) {
return token != null && token.length() >
4
? "****" + token.substring(token.length() - 4)
: "****";
}
}
package com.dwl.mvc;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.Properties;
/**
* @ClassName YamlPropertySourceFactory
* @Description
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
public class YamlPropertySourceFactory
implements PropertySourceFactory {
@Override
@NonNull
public PropertySource<
?> createPropertySource(@Nullable String name, EncodedResource resource) {
// 1. 创建 YAML 解析器
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
// 2. 解析 YAML 到 Properties 对象
Properties properties = factory.getObject();
// 3. 获取文件名(处理空名情况)
String sourceName = name != null ? name : resource.getResource().getFilename();
// 4. 创建 Spring 可识别的属性源
assert properties != null;
return new PropertiesPropertySource(sourceName != null ? sourceName : "yaml", properties);
}
}
package com.dwl.mvc;
import com.dwl.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @ClassName DispatcherServletInitCase
* @Description DispatcherServlet初始化调试案例
* <p>
* 核心功能:
* 1. 请求映射注册的底层实现跟踪
* 2. 请求处理链的完整执行流程
* 3. 参数解析与返回值处理的调试信息
* 4. 拦截器执行顺序的可视化监控
* </p>
* <p>
* 设计原理:
* - 基于Spring MVC的DispatcherServlet初始化流程
* - 通过RequestMappingHandlerMapping解析请求映射
* - 使用HandlerExecutionChain管理拦截器链
* - 通过HandlerAdapter调用目标处理器方法
* </p>
* @Version 1.0.0
* @Date 2025
* @Author By Dwl
*/
@Slf4j
public class DispatcherServletInitCase
{
public static void main(String[] args) throws Exception {
// 初始化Spring Web容器
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(MvcConfig.class)
;
/*
* 1.请求映射管理
* 扫描与注册:自动扫描所有带有 @Controller或 @RestController注解的类,解析类和方法上的 @RequestMapping及其变体注解(如 @GetMapping、@PostMapping),生成 请求映射规则(RequestMappingInfo)。
* 映射表维护:将生成的 RequestMappingInfo与对应的 HandlerMethod(封装了控制器方法的对象)关联,存储在内部的 MappingRegistry中,形成请求到处理方法的映射表。
*
* 2.请求匹配与路由
* 请求解析:当请求到达时,解析请求的 URL、HTTP 方法、请求参数、请求头等信息。
* 匹配逻辑:根据 RequestMappingInfo中的条件(路径模式、HTTP 方法、参数条件等),从映射表中筛选出最匹配的 HandlerMethod。
* 返回执行链:将匹配的 HandlerMethod封装为 HandlerExecutionChain(包含拦截器链),返回给 DispatcherServlet执行
*/
RequestMappingHandlerMapping mapping =
context.getBean(RequestMappingHandlerMapping.class)
;
log.info("========== 请求映射注册详情 ==========");
// 遍历所有已注册的处理器方法
mapping.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) ->
{
logHandlerMethodDetails(handlerMethod, requestMappingInfo);
});
set(context, null, "/dispatcher/servlet/index", "GET");
set(context, Map.of("name", "Dwl"), "/dispatcher/servlet/param", "GET");
set(context, Map.of("token", "Dwl"), "/dispatcher/servlet/putMapping", "PUT");
set(context, null, "/dispatcher/servlet/jsonObject", "POST");
}
/**
* 打印处理器方法详细信息
*
* @param handlerMethod 处理器方法
* @param mappingInfo 请求映射信息
*/
private static void logHandlerMethodDetails(HandlerMethod handlerMethod,
RequestMappingInfo mappingInfo) {
log.info("【处理器方法】{}", handlerMethod.getMethod().toGenericString());
log.info("【所属类】{}", handlerMethod.getBeanType().getSimpleName());
// 解析请求映射条件
log.info("【请求路径】{}", getPathPatterns(mappingInfo));
log.info("【HTTP方法】{}", getHttpMethods(mappingInfo));
log.info("【参数条件】{}", getParamsConditions(mappingInfo));
log.info("【请求头】{}", getHeadersConditions(mappingInfo));
log.info("【内容类型】{}", getConsumesConditions(mappingInfo));
log.info("【响应类型】{}", getProducesConditions(mappingInfo));
log.info("----------------------------------------");
}
/**
* 请求条件解析工具方法
*/
private static String getPathPatterns(RequestMappingInfo info) {
return Objects.requireNonNull(info.getPatternsCondition()).getPatterns().stream()
.map(path -> path + (path.endsWith("/") ? "" : "/"))
.collect(Collectors.joining(" | "));
}
private static String getHttpMethods(RequestMappingInfo info) {
return info.getMethodsCondition().getMethods().stream()
.map(RequestMethod::name)
.collect(Collectors.joining(" | "));
}
private static String getParamsConditions(RequestMappingInfo info) {
return info.getParamsCondition().getExpressions().stream()
.map(Object::toString)
.collect(Collectors.joining(" & "));
}
private static String getHeadersConditions(RequestMappingInfo info) {
return info.getHeadersCondition().getExpressions().stream()
.map(Object::toString)
.collect(Collectors.joining(" & "));
}
private static String getConsumesConditions(RequestMappingInfo info) {
return info.getConsumesCondition().getConsumableMediaTypes().stream()
.map(MediaType::toString)
.collect(Collectors.joining(" | "));
}
private static String getProducesConditions(RequestMappingInfo info) {
return info.getProducesCondition().getProducibleMediaTypes().stream()
.map(MediaType::toString)
.collect(Collectors.joining(" | "));
}
public static void set(AnnotationConfigServletWebServerApplicationContext context,
Map<
String, ?> params,
String requestURI,
String method) throws Exception {
// 创建请求对象
MockHttpServletRequest request = new MockHttpServletRequest(method, requestURI);
if (params != null) {
params.forEach((k, v) ->
{
request.addParameter(k, v.toString());
request.addHeader(k, v.toString());
// 模拟Header参数
});
}
// 获取处理链(包含拦截器和处理器)
HandlerExecutionChain chain =
context.getBean(RequestMappingHandlerMapping.class)
.getHandler(request);
log.info("【处理链信息】拦截器数量: {}", Objects.requireNonNull(Objects.requireNonNull(chain).getInterceptors()).length);
log.info("【目标处理器】{}", chain.getHandler());
// 创建响应对象
MockHttpServletResponse response = new MockHttpServletResponse();
// 获取自定义适配器
MyRequestMappingHandlerAdapter adapter =
context.getBean(MyRequestMappingHandlerAdapter.class)
;
// 执行请求处理流程
log.info("========== 开始处理请求 ==========");
adapter.invokeHandlerMethod(request, response,
ObjectUtil.cast(chain.getHandler()));
// 打印处理链信息
log.info("========== 处理链执行完成 ==========");
log.info("【响应状态】{}", response.getStatus());
log.info("【响应内容】{}", response.getContentAsString());
// 打印组件信息
printComponentDetails(adapter);
byte[] arr = response.getContentAsByteArray();
System.out.println(new String(arr, StandardCharsets.UTF_8));
}
private static void printComponentDetails(MyRequestMappingHandlerAdapter adapter) {
log.info("========== 组件信息 ==========");
Objects.requireNonNull(adapter.getArgumentResolvers()).forEach(resolver ->
{
log.info("参数解析器: {}", resolver.getClass().getSimpleName());
});
Objects.requireNonNull(adapter.getReturnValueHandlers()).forEach(handler ->
{
log.info("返回值处理器: {}", handler.getClass().getSimpleName());
});
}
}
相关文章:
深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析 - 实践
深入 Spring MVC 底层:从 DispatcherServlet 到自定义组件的全链路解析 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "…...
podman 替代docker
podman machine init podman machine start podman machine stop podman machine set --rootless podman machine start--- 她说, 她是仙,她不是神...
202404_古剑山杯_数独
拼图,gapsTags:拼图,gaps 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202404_古剑山杯_数独.zip 0x01. WP 01.解压缩后发现一个image.png类似拼图02.使用工具gaps进行自动拼图 gaps run image.png output.…...
m1芯片装windows系统使用感受
m1芯片装Windows系统使用感受 随着技术的不断进步,苹果公司在其Mac系列电脑中引入了自家设计的M1芯片。这款芯片不仅在性能上有着显著的提升,同时也带来了能效比上的巨大飞跃。然而,对于一些用户来说,macOS可能无法满足他们的所有需求,特别是那些需要运行特定Windows应用程…...
mac book怎么切换windows系统
如何在MacBook上安装并切换Windows系统 一、引言 随着科技的发展和个人需求的多样化,许多人可能会遇到需要在苹果公司的MacBook上运行Windows操作系统的情况。这可能是因为某些特定的应用程序或游戏只能在Windows环境下运行,或者用户希望体验不同操作系统的特色。本篇文章将详…...
硬件内在函数
AVX-512支持:SIMD的终极形态AVX-512支持:SIMD的终极形态// 优化的数值计算// 优化前的代码 public double[] ProcessData(double[] input) {var result = new double[input.Length];for (int i = 0; i < input.Length; i++){result[i] = Math.Sin(input[i]) * Math.Cos(in…...
202205_宁波市赛_DocDocDoc
DOCX,PNG高度隐写,凯撒密码Tags:DOCX,PNG高度隐写,凯撒密码 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202205_第五届市赛_DocDocDoc.zip 0x01. WP解压文件后的到一docx文件,查看内容无明显信息将扩展名…...
DP题
1.区间dp--精妙状态设计与转移 https://codeforces.com/contest/2129/problem/D dp[l][r][a][b]: 表示只考虑放置[l,r]区间内部的数,并且满足所有的s[l]~s[r]的条件,对l-1的贡献是a,对r+1的贡献是b的方案数 转移: 对于dp[l][r][a][b]来说,枚举第一个放置的数,比如说是k,如果k对…...
LGP7115 [NOIP 2020] 移球游戏 学习笔记
LGP7115 [NOIP 2020] 移球游戏 学习笔记 Luogu Link 前言\(\texttt{NOIP2020}\) 笑传之 \(\texttt{Change Content of Balls.in}\)。致敬传奇修改文件选手我也不知道是谁。 题意简述 你面前有 \(n+1\) 根柱子。对于前 \(n\) 个柱子,每根上有 \(m\) 个球,而第 \(n+1\) 根初始是…...
阿里为何建议MVC+Manager层混合架构?
MVC 架构的弊端 Manager 层的特征 Manager 层使用案例传统三层架构代码示 引入 Manager 层后的代码示例初入编程世界时,前辈们总会教导我们,系统设计应遵循 MVC(Model - View - Controller) 架构。MVC 架构就像一个精巧的齿轮组,将整个系统清晰地划分为 Model(模型)、Vi…...
Android(Kotlin)+ ML Kit:移动端英文数字验证码识别实战
1 概述与适用场景 在移动端直接对截图或拍照的英文数字验证码做识别,可以用于自动化测试、无障碍辅助或内部工具。使用 Google ML Kit 的 Text Recognition(可离线运行)可以避免服务端延迟。为了提升识别率,我们在前端加入图像预处理(灰度、二值化、去噪和放大)再送给 OC…...
用Android(Kotlin)+ ML Kit:移动端英文数字验证码识别实战
1 概述与适用场景 在移动端直接对截图或拍照的英文数字验证码做识别,可以用于自动化测试、无障碍辅助或内部工具。使用 Google ML Kit 的 Text Recognition(可离线运行)可以避免服务端延迟。为了提升识别率,我们在前端加入图像预处理(灰度、二值化、去噪和放大)再送给 OC…...
“人工智能+”的坚硬内核,边缘地带的“数字火种”:大模型如何烧出一片新天地
本文由大模型根据作者提问生成,仅修改标题。一场由“失业工程师+过气博主+快烂了的瓜”引发的变革,正悄然重塑中国AI的商业逻辑。如果你只关注科技头条,你会觉得AI的故事属于巨头:万亿参数、军备竞赛、AGI威胁论。但在主流视野之外,真正的革命正在边缘地带发生。这里没有光…...
详细介绍:10:00开始面试,10:06就出来了,问的问题有点变态。。。
详细介绍:10:00开始面试,10:06就出来了,问的问题有点变态。。。pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier Ne…...
PHP启动报错:liboing.so.5:cannot op如何处理?
在 PHP 启动时报错 liboing.so.5: cannot open shared object file: No such file or directory 是因为系统无法找到或加载 liboing.so.5 共享库。这通常是由于以下原因之一导致的:库文件缺失:系统中没有安装 liboing.so.5 文件。 路径未配置:系统未正确配置动态链接库路径。…...
时空倒流 Time - 题解
设 \(x\) 轮为负操作,\(y\) 轮为正操作,\(d\) 为需要修改的差值(正负同理,取正即可),那么可更改成的范围为 \(-x \times R +y*L \sim -x*L+y*R\),令上式为 \(M \sim N\),易得 M 为最小的可能值,N 为最大的可能值,可通过不断给 M 加 1,使 M 变成 N。 综上,枚举 \(x \…...
202508_QQ_XORPNG
Tags:XOR,PNGLSB 0x00. 题目 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202508_QQ_XOR&PNG 0x01. WP 01.十六进制编辑器核对 打开发现文件内容并非PNG常见文件头,尝试与常见PNG文件头XOR发现循环的KEY为kelaibe…...
Voice Agent 全球开发者比赛,TEN Dev Challenge 2025 等你来战!
TEN Dev Challenge 2025 全球开发者大赛现已启动,本次赛事聚焦实时交互与对话式AI领域,面向全球开发者开放参与通道。无论您是独立开发者,还是 3 人以内的小型开发团队,均可通过线上形式参与,并有机会角逐总计 1.1 万美元奖金,同时获得行业级展示与合作资源。💰 USD 11…...
第02周 预习:Java基础语法2、面向对象入门 - hohohoho--
第02周 预习:Java基础语法2、面向对象入门项目名称 内容课程名称 java班级 网安2413学生姓名 王璐学号 202421336068预习 1.1 学习目标掌握引用类型及常见类:数组、数组列表(ArrayList)、方法及引用类型作为方法参数 掌握类、对象、方法、属性相关基本概念,对象的初始化。 能…...
第六届机器学习与计算机应用国际学术会议(ICMLCA 2025)
第六届机器学习与计算机应用国际学术会议(ICMLCA 2025) 2025 6th International Conference on Machine Learning and Computer Application(ICMLCA 2025) 第六届机器学习与计算机应用国际学术会议(ICMLCA 2025)定于2025年10月17-19日在中国深圳隆重举行。本届会议将主要关注…...
设计模式-享源模式 - MaC
什么是享元模式? 享元模式是一种结构型设计模式,它通过共享技术来有效地支持大量细粒度的对象。享元模式通过共享已经存在的对象来减少创建对象的数量,从而减少内存占用和提高性能。 享元模式包含以下角色:享元接口(Flyweight):声明一个接口,通过它可以接受并作用于外部状…...
# 数论知识讲解与C++代码:唯一分解定理、辗转相除法、埃氏筛与线性筛(含质因数分解示例)
C++ 模板:唯一分解定理、辗转相除法、埃氏筛与线性筛(含质因数分解示例) 下面给出一套实用的 C++ 模板,包含:辗转相除法(求 gcd / lcm) 试除法的质因数分解(适合小到中等 n) 使用埃氏筛预生成素数(并用于分解) 线性筛(线性时间生成素数,并可得到每个数的最小质因子…...
第九届交通工程与运输系统国际学术会议(ICTETS 2025)
第九届交通工程与运输系统国际学术会议(ICTETS 2025) 2025 9th International Conference on Traffic Engineering and Transportation System (ICTETS 2025) 第九届交通工程与运输系统国际学术会议(ICTETS 2025)将由大连理工大学主办,大连理工大学建设工程学院交通运输系…...
小红书开源 FireRedTTS-2;全栈开源应用+嵌入式+电路设计:BUDDIE AI 语音交互方案丨日报
开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 技术 」、「有亮点的 产品 」、「有思考的 文章 」、「有态度的 观点 」、「有看点的 活动 」,但内容仅代表编辑…...
深度解析 ADC 偶联技术:从随机偶联到定点偶联,如何平衡抗肿瘤 ADC 的活性、稳定性与均一性?
抗体药物偶联物(ADC)作为 “精准抗癌利器”,通过重组单克隆抗体的靶向性与小分子细胞毒性药物的高活性结合,实现了 “靶向递送、精准杀伤” 的治疗理念 —— 既降低了小分子毒素对正常细胞的毒副作用,又提升了肿瘤治疗的疗效,已成为近年来抗肿瘤药物研发的核心赛道。然而…...
豆包P图大更新,网友们已经玩嗨了。
https://news.cnblogs.com/n/800390/最近,大伙有没有感觉互联网被 AI 图片占领了。不是刷到朋友圈的大伙自己搓的手办。。神秘锅哥就是刷到大伙和各路大佬的偶遇合照。。怎么像有人一夜之间给地球开了个创造模式,想创啥就创啥,就我连篇稿都创不出来是吧?关注 AI 圈的差友应…...
【初赛】无向图度数性质 - Slayer
无向图度数性质 一、基础概念:顶点度数定义 在无向图 $ G = (V, E) $ 中:顶点 $ v \in V $ 的度数 $ \deg(v) $ 指关联于该顶点的边的数量 特殊情况:环($ (v, v) $)对 $ \deg(v) $ 贡献 2 孤立点度数为 0 非环边 $ (u, v) $ 对 $ \deg(u) $ 和 $ \deg(v) $ 各贡献 1二、核…...
$p\oplus q=r$
很牛很好的题,但不知道为什么 qoj 这么多踩。 原题/原题 简介题面:问题 A:构造两个长度为 \(n\) 的包含 \(0\sim n-1\) 的排列 \(p,q\),使得 \(r=p\oplus q\) 也是个排列。首先特判 \(n=1\)。然后发现 \(\oplus_{i=0}^{n-1} r_i=\oplus_{i=0}^{n-1} q_i\oplus_{i=0}^{n-1}q…...
2025年金融行业API安全最佳实践:构建纵深防御体系
2025年金融行业API安全最佳实践:构建纵深防御体系金融行业数字化转型深度依赖API技术,开放业务模式也带来新的安全挑战。构建有效的API安全防护体系需覆盖全生命周期,结合管理要求、技术工具和运营机制,并借鉴行业实践案例。提出关键实践方法与实施框架金融行业数字化转型深…...
Jack-of-All-Trades
Jack-of-All-Trades 一、信息收集先使用nmap扫一下Ip看看开放了哪些端口,这里很奇怪,22端口上面却是部署着http协议,看了一下wp这里要使用浏览器绕过这个限制nmap -sS -A -Pn 10.10.196.165 这里使用firefox,在导航栏里面搜索about config然后再出来的搜索框这里再搜索netw…...
Matlab的交通标志定位实现
基于Matlab的交通标志定位实现方案,结合颜色分割、形态学处理和轮廓分析技术一、代码 %% 参数设置 imgPath = traffic_sign.jpg; minArea = 500; % 最小区域面积 maxArea = 10000; % 最大区域面积 colorThreshold = 0.8; % 颜色相似度阈值%% 图像预处理 img = imread(imgP…...
怎样在 Salesforce Flow 中获取当前 Salesforce 组织的 URL
可以在 Flow 中配置一个 Formula 类型的变量: LEFT($Api.Partner_Server_URL_260, FIND( /services, $Api.Partner_Server_URL_260))...
reLeetCode 热题 100-3 最长连续序列扩展 排序算法 - MKT
reLeetCode 热题 100-3 最长连续序列扩展 排序算法...
vuejs3.0 从入门到精通【左扬精讲】—— 从原生到原子化:一文梳理现代 CSS 技术体系(2025 版)
vuejs3.0 从入门到精通【左扬精讲】—— 从原生到原子化:一文梳理现代 CSS 技术体系(2025 版)作为前端开发的核心技术之一,CSS(层叠样式表)早已不是 “写几行样式” 那么简单。随着项目规模扩大和工程化需求升级,CSS 技术栈也衍生出众多分支 —— 从解决复用性的预处理器…...
java中JSON字符串处理的踩坑
在处理JSON字符串的时候,读取的数据原封不动每一行是,前后有两个引号"{\"cuidC54E92418CA1CD099A5AFC4D2F322015|VECB5VMT4\": {\"REFINED_APPLIST_TIMESTAMP\": 1756716480, \"DEVICE_INFO\": {\"SOURCE\": 131072, \"br…...
11111
1111111111...
TIA Portal中S7-1500F CPU与ET200SP安全模块的配置例程(转载)
ET200SP 分布式 IO 系统除 ET200SP 标准模块外,还包含故障安全模块。具有 PROFINET接口的安全 CPU 与配有故障安全模块的 ET200SP IO设备可以实现安全功能。配置过程与标准系统中一样,通过在硬件组态中进行网络连接,并在在线状态下分配从站的设备名称。ET200SP上的故障安全模…...
获取第一个运行的Word应用程序实例
获取第一个运行的COM应用程序实例1 using System;2 using System.Collections.Generic;3 using System.Runtime.InteropServices;4 using System.Runtime.InteropServices.ComTypes;5 6 /// <summary>7 /// 提供查询运行中COM对象的改进方案8 /// </summary>9 publ…...
S7-1500 TRACE功能组态 (转载)
1、TRACE配置介绍1.1、新建TRACE配置 在TIA博途软件中,双击项目树相应PLC站点下的“Traces”,展开后来实现TRACE的各项功能,TRACE在线视图如图2所示。图2. 创建TRACE ①点击“添加新Trace”,用于新建Trace配置;② 为目前离线文件和CPU已装载有相同名称的TRACE; 为目前仅…...
如何在Proxmox VE中使用fdisk命令行扩展LVM存储池 - 若
前言 在Proxmox VE(PVE)的使用过程中,为虚拟机和容器添加额外的存储空间是一项常见任务。当你为服务器新增了一块硬盘后,Web管理界面有时可能无法提供所有所需的图形化操作选项。本篇教程将介绍如何完全通过命令行,使用经典的 fdisk 和 lvm 工具,安全地将一块新硬盘添加到…...
垃圾AV覆盖defender
免责声明 本文章涉及的内容仅用于安全研究、学习和技术讨论,不针对任何杀毒软件进行虚假陈述或恶意操作。文中所提及的软件及操作均不旨在贬低、攻击或损害其品牌、名誉及正常功能。请读者仅在合法、受控的环境下进行实验和测试,作者对因使用本文内容而产生的任何后果不承担责…...
SAP-PO:怎么控制传输的内容在单数据情况下是数组格式还是单对象格式
像下图,没设置前是对象格式 设置后是数组格式 可以在CC的Custom XML/JSON Comversion Rules下进行设置 sender通道是在general第一个页签 receiver是在dataformat页签 如果字段为空则在结构中就不会显示该字段,需要在函数中可以配置一下...
开源新基建:数字中国创新发展的底层密码与生态实践
开源新基建:数字中国创新发展的底层密码与生态实践 在全球数字化转型加速的背景下,开源技术已从开发者工具演变为国家数字战略的核心基础设施。中国正通过构建自主可控的开源生态体系,打造数字经济发展的安全基座,这一战略布局正在重塑产业创新范式并创造显著的商业价值。最…...
员工离职停用Salesforce帐号?这11个“坑”千万别踩!
在Salesforce里,用户离职很常见,看似只是一个“停用账号”的小操作。但如果涉及到管理员(Admin)账号,情况就完全不同。管理员往往绑定了大量的报表、自动化流程和系统配置,一旦处理不当,轻则报表失效、流程中断,重则影响关键业务。 本文结合实践,总结了停用Salesforce…...
Linux的运行模式
在 Linux 系统中,getenforce 是一个用于查询 SELinux(Security-Enhanced Linux)当前运行模式的命令。 SELinux 是一种强制访问控制(MAC)安全机制,它有三种主要运行模式: Enforcing(强制模式):SELinux 会强制执行所有安全策略,违反策略的操作会被阻止并记录日志 Perm…...
Spring Boot + MybatisX,效率翻倍!
1.什么是MybatisX? 2.使用MybatisX的好处 3.如何使用MybatisX?1.什么是MybatisX? MybatisX 是一款基于 IDEA 的快速开发插件,方便在使用mybatis以及mybatis-plus开始时简化繁琐的重复操作,提高开发速率。 2.使用MybatisX的好处节省大量持久层代码开发时间 强大的功能为业务…...
条码控件Aspose.BarCode教程:使用 Java 自动生成 DotCode 条形码
DotCode 是一种二维条码符号,广泛应用于制造业和制药业等行业。这种条码简化了创建机器可读代码的流程,从而提升了物流效率。借助Aspose.BarCode for Java,我们可以构建一个工具,以 Java 编程方式自动生成 DotCode 条码。此 Java SDK 允许您自定义属性并将条码导出为图像格…...
AI 玩转网页自动化无压力:基于函数计算 FC 构建 Browser Tool Sandbox
从 Web 1.0 到 Web2.0,再到单页应用(SPA)和 React/Vue 等前端框架时代,再到当下的 AI Agent 时代。每个阶段都有当时的浏览器自动化的王者。作者:计缘 浏览器自动化的前世今生 从 Web 1.0 到 Web2.0,再到单页应用(SPA)和 React/Vue 等前端框架时代,再到当下的 AI Agen…...
AI时代的全栈框架:独立开发者的机会与挑战
独立开发者终于能全干了?AI帮忙还愁生态?一体性是王道,但选什么框架还得自己纠结。AI写代码真靠谱?前言 本文本来只是 DjangoStarter v3.2.1 新版本发布博客里的一段思考,不过越写越长,干脆拆分成一篇独立的文章得了。😄全栈这个词已经被喊烂了,但在 AI 时代,它的含义…...