前言
在 Spring Boot 中,一个简单的接口@RestController,就能轻松地实现复杂的Web服务。当我们在浏览器中访问http://localhost:8080/user/all,返回一个简单的字符串"all user",背后究竟发生了什么?
从Spring Boot启动时的自动配置,到内嵌的Servlet容器启动,再到DispatcherServlet的初始化,以及请求如何一步步被映射、执行,最终生成响应。
本文将以一个简单的示例接口为切入点,聊一聊Spring Boot从接收请求到生成响应的完整处理流程。
一、SprinngBoot启动阶段
1.1WebMvc自动配置
1.1.1自动配置类路径
1.1.2自动配置类注解
-
类上注解
-
// SpringMVC的配置在DispatcherServlet、任务执行器、校验器等基础配置完成后加载,保证依赖关系 @AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
-
// 条件注解,当前应用是基于Servlet的Web应用时,才会激活这个自动配置类 @ConditionalOnWebApplication(type = Type.SERVLET)
-
// 条件注解,类路径下有Servlet, DispatcherServlet, WebMvcConfigurer时才激活这个自动配置类 // Servlet是Servlet API的核心类,DispatcherServlet, WebMvcConfigurer是Spring MVC的核心 Servlet和扩展配置接口 @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
-
// 条件注解,容器中不存在WebMvcConfigurationSupport这个bean时,才激活这个自动配置类 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
-
// 确保WebMvcAutoConfiguration在绝大多数自动配置类之前加载,但仍为DispatcherServlet等基础配置留出更高的优先级。 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
-
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebMvcAutoConfiguration {
// 省略部分代码...
}
-
自动配置类中静态内部类EnableWebMvcConfiguration
-
EnableWebMvcConfiguration继承了WebMvcConfigurationSupport,WebMvcConfigurationSupport中@Bean注解的bean也会处理
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
// 省略部分代码...
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
// 省略部分代码...
}
// WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
// 省略部分代码...
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer pathConfig = getPathMatchConfigurer();
if (pathConfig.getPatternParser() != null) {
mapping.setPatternParser(pathConfig.getPatternParser());
} else {
mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
}
Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
if (pathConfig.getPathPrefixes() != null) {
mapping.setPathPrefixes(pathConfig.getPathPrefixes());
}
return mapping;
}
// 省略部分代码...
}
1.1.3RequestMappingHandlerAdapter适配器
详细职责
- 适配控制器方法
- 不同类型的处理器(如控制器方法、返回值等)需要不同的适配逻辑。
- RequestMappingHandlerAdapter专门适配使用@RequestMapping标注的方法。
- 参数解析
- 提供一组HandlerMethodArgumentResolver,解析控制器方法参数:
- @RequestParam、@PathVariable注解的参数。
- HTTP请求体(如JSON)解析成对象。
- 当前的HttpServletRequest、HttpSession等内置参数。
- 提供一组HandlerMethodArgumentResolver,解析控制器方法参数:
- 返回值处理
- 提供一组HandlerMethodReturnValueHandler,处理控制器方法的返回值:
- 将对象序列化为JSON。
- 返回View对象进行渲染。
- 提供一组HandlerMethodReturnValueHandler,处理控制器方法的返回值:
- 调用控制器方法
- 根据解析好的参数,执行控制器方法。
- 将返回值通过适配器处理并返回给客户端。
参数解析器及返回值解析器从哪儿来
-
// RequestMappingHandlerAdapter实现了InitializingBean。 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {}
-
重写的afterPropertiesSet方法会在SpringBoot启动时,对实例requestMappingHandlerAdapter进行初始化阶段执行。
-
afterPropertiesSet中添加了默认的参数解析器及返回值解析器
1.1.4RequestMappingHandlerMapping请求映射
详细职责
- 扫描并注册映射关系
- 启动时扫描所有标注了@RequestMapping的控制器类和方法。
- 解析为映射信息(RequestMappingInfo),并存储到内部的数据结构中。
- 匹配映射关系
- 当请求到达时,根据URL路径、HTTP方法等信息,从缓存中快速匹配对应的控制器方法(HandlerMethod)。
扫描及注册
- RequestMappingHandlerMapping间接实现了InitializingBean,所以在RequestMappingHandlerMapping初始化时也会执行afterPropertiesSet方法。
- afterPropertiesSet会调用父类AbstractHandlerMethodMapping的afterPropertiesSet方法
- processCandidateBean方法会遍历候选bean,把带有@Controller或者@RequestMapping注解类中带有@RequestMapping注解的方法筛选出来。
- 处理方法映射注册
1.2DispatcherServlet自动配置
1.2.1自动配置类路径
1.2.2自动配置类注解
-
类上注解
-
// 优先级最高,最先处理该配置类 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
-
// 在ServletWebServerFactoryAutoConfiguration处理后进行自动配置处理 @AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
-
// 条件注解,当前应用是基于Servlet的Web应用时,才会激活这个自动配置类 @ConditionalOnWebApplication(type = Type.SERVLET)
-
// 类路径下有DispatcherServlet时才激活这个自动配置类 @ConditionalOnClass(DispatcherServlet.class)
-
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {
// 省略部分代码...
}
内部静态配置类DispatcherServletConfiguration
- 创建和配置DispatcherServlet实例。
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
内部静态配置类DispatcherServletRegistrationConfiguration
- 注册一个DispatcherServlet的bean,设置初始配置,熟悉的loadOnStartup。Servlet启动时,servlet的启动顺序。
- 这个阶段还在Spring容器中。
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
- 当tomcat容器(默认情况下)启动时,Servlet容器会调用所有ServletContextInitializer实现的onStartup方法。
- onStartup方法
// RegistrationBean
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
- 将DispatcherServlet注册到Servlet容器
// ServletRegistrationBean
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
return servletContext.addServlet(name, this.servlet);
}
二、接口访问阶段
2.1tomcat处理传入的请求
// StandardWrapperValve
public void invoke(Request request, Response response) throws IOException, ServletException {
// servlet实例不可用时,分配一个servlet实例处理请求
servlet = wrapper.allocate();
}
2.1.1初始化servlet
// StandardWrapper
public Servlet allocate() throws ServletException {
initServlet(instance);
}
// HttpServletBean
public final void init() throws ServletException {
// 省略部分代码...
// 这里是留给子类(FrameworkServlet)做初始化的
initServletBean();
}
- 控制台日志开始的地方
web应用上下文已经在SpringBoot启动阶段创建,这里只是负责进行一些初始化工作
// FrameworkServlet
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
// 省略部分代码...
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
// 省略部分代码...
}
// 省略部分代码...
if (!this.refreshEventReceived) {
// 手动触发刷新(做一些初始化工作)
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
2.1.2干了哪些初始化工作
- 初始化 MultipartResolver,用于处理文件上传
- 初始化 LocaleResolver,用于处理国际化
- 初始化 ThemeResolver,用于处理主题
- 初始化 HandlerMappings,用于处理请求映射
- 初始化 HandlerAdapters,用于处理请求适配
- 初始化 HandlerExceptionResolvers,用于处理异常解析
- 初始化 RequestToViewNameTranslator,用于将请求翻译为视图名称
- 初始化 ViewResolvers,用于解析视图
- 初始化 FlashMapManager,用于处理 Flash 映射
// DispatcherServlet
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
2.1.2.1初始化HandlerMappings
- 查找ApplicationContext中的所有处理器映射(包括父容器的)
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
// 省略部分代码...
// 如果没有找到其他映射,通过注册一个默认的HandlerMapping来确保至少有一个HandlerMapping。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
// 如果至少有一个HandlerMapping使用了路径模式,解析请求路径的功能将被启用
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
- 所有的处理器映射
2.1.2.2初始化HandlerAdapters
- 查找ApplicationContext中的所有处理器适配器(包括父容器的)
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
// 省略部分代码...
//如果没有找到其他适配器,通过注册默认的HandlerAdapters来确保至少有一些HandlerAdapters。
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
2.1.3开始请求
// StandardWrapperValue
public void invoke(Request request, Response response) throws IOException, ServletException {
filterChain.doFilter(request.getRequest(), response.getResponse());
}
2.1.3.1拦截器链调用
- 如果有拦截器,先调用拦截器
- 处理完拦截器,跳出拦截器连后,调用servlet实例(servlet的service方法)
// ApplicationFilterChain
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 存在拦截器,则调用
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() &&
"false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[] { req, res, this };
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// 跳出拦截器链,调用servlet实例
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[] { req, res };
SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);
} else {
// servlet的service方法
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
- servlet的service方法
// FrameworkServlet
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
} else {
super.service(request, response);
}
}
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doService(request, response);
}
2.1.3.2正式通过DispatcherServlet处理请求
// DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
doDispatch(request, response);
}
- 确定handler
- 确定handler adapter
// DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 省略部分代码...
// 确定当前请求的处理器
mappedHandler = getHandler(processedRequest);
// 确定当前请求的处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 实际调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 省略部分代码...
}
- getHandler
- getHandlerAdapter
- handle
三、总结
3.1Spring Boot启动阶段的准备工作
3.1.1Web容器和DispatcherServlet的自动配置
Spring Boot 提供了许多自动配置类来简化开发,以下是与Web请求处理相关的重要类:
- DispatcherServletAutoConfiguration:完成DispatcherServlet的注册和初始化。
- WebMvcAutoConfiguration:为Spring MVC提供默认配置。
3.1.2DispatcherServlet 的注册
- 在Spring Boot中,DispatcherServlet是Web应用的核心处理器,它的注册由DispatcherServletAutoConfiguration完成。
- 默认注册的名称为
dispatcherServlet
。 - 映射路径为
/
,拦截所有 HTTP 请求。
3.1.3WebMvc 配置
WebMvcAutoConfiguration会在没有用户自定义配置时,提供一套默认的Spring MVC配置,包括:
- 注册RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
- 配置默认的视图解析器、静态资源处理器等。
3.1.4内嵌 Servlet 容器的启动
Spring Boot内置了Tomcat、Jetty等Servlet容器,以下是关键步骤:
- 内置容器启动:Spring Boot在启动时会通过EmbeddedWebServerFactory启动内置的Tomcat。
- Servlet注册:DispatcherServlet被注册为内置容器中的默认Servlet,用于接收和处理所有请求。
3.2请求到达Spring Boot的Servlet容器
3.2.1HTTP 请求进入Tomcat
当用户通过浏览器发送请求http://localhost:8080/user/all:
- 连接到Tomcat:
Tomcat的Http11Processor会接收请求并解析为HTTP协议对象。 - 请求分发:
请求被转发到DispatcherServlet,它是由Spring MVC注册的默认Servlet。
3.3DispatcherServlet的请求分发处理
3.1DispatcherServlet 的初始化
DispatcherServlet的init()方法会在第一次接收到请求时执行,它会完成以下准备工作:
- 加载HandlerMapping(请求到处理器的映射)。
- 加载HandlerAdapter(处理器的适配器)。
3.2核心组件的加载
- RequestMappingHandlerMapping:扫描所有标注了@RequestMapping的方法,建立URL和处理器方法的映射关系。
- RequestMappingHandlerAdapter:负责解析方法参数,调用控制器方法,并处理返回值。
3.4请求处理的完整流程
3.4.1URL到控制器方法的映射
DispatcherServlet调用RequestMappingHandlerMapping的getHandler()方法,根据请求的URL和HTTP方法匹配到对应的 HandlerMethod(这里对应 UserController#getAllUser 方法)。
3.4.2参数解析与方法调用
DispatcherServlet将请求传递给RequestMappingHandlerAdapter:
- 解析控制器方法的参数,例如@RequestParam或JSON请求体等。
- 调用控制器方法 getAllUser(),获得返回值"all user"。
3.5返回值处理
3.5.1处理返回值
- 返回值通过 HandlerMethodReturnValueHandler 处理:
- 如果是字符串(如
"all user"
),则直接写入到 HTTP 响应中。
- 如果是字符串(如
3.6最终生成响应
DispatcherServlet 将处理结果封装为 HTTP 响应,并交给 Tomcat。
Tomcat 将响应返回给客户端,最终浏览器显示 "all user"
。