SSM框架原理畅谈之SpringMVC

news2024/9/29 23:38:08

SpringMVC

  • 一、Java SE Servlet标准
    • 1.1 Servlet 接口
    • 1.2 HttpServletRequest 接口
    • 1.3 HttpServletResponse 接口
    • 1.4 Cookie 对象
    • 1.5 Filter 接口
    • 1.6 HttpSession 接口
  • 二、SpringMVC
    • 2.1 Spring MVC核心概念
    • 2.2 DispatcherServlet
    • 2.3 DispatcherServlet.init()
    • 2.4 DispatcherServlet.onRefresh()
    • 2.5 DispatcherServlet.service()
    • 2.6 DispatcherServlet.doDispatch()
    • 2.7 一个请求执行流程

一、Java SE Servlet标准

Servlet 标准是 Java Web 应用程序开发的基础。

Servlet 是一种 Java 程序,它可以在 Web 服务器(Tomcat、WebLogic等)上运行,用于处理 Http 请求和响应。Servlet 标准定义了 Servlet API,这是一组 Java 类和接口,用于开发 Web 应用程序。

Servlet API 包括 javax.servlet 和 javax.servlet.http 两个包。

  • javax.servlet:定义了 Servlet 的基本接口和类。如常见的Filter、Servlet、ServletConfig、ServletContext、ServletRequest、ServletResponse等。
  • javax.servlet.http:扩展了 javax.servlet 包,提供了处理 Http 请求和响应的类和接口,便捷了开发。如常见的HttpServlet、HttpFilter、HttpServletRequest、HttpServletResponse等。

Servlet 标准的核心是 Servlet 容器。Servlet 容器(Tomcat、Jetty、WebLogic Server等)是 Web 服务器的一部分,它负责加载、初始化和管理 Servlet。Servlet 容器还提供了 Servlet 的生命周期管理、线程安全和请求分发等功能。

Servlet 标准还提供了一些高级功能,如过滤器、监听器和会话管理。

  • 过滤器Filter:拦截请求和响应,可以对齐进行处理或修改。
  • 监听器:监听 Servlet 容器中的事件,主要监听对象有Request、Session、ServletContext。
监听对象监听接口监听事件
ServletRequestServletRquestListener
ServletRquestAttributeListener
ServletRequestEvent
ServletRquestAttributeEvent
HttpSessionHttpSessionListener
HttpSessionActivationListener
HttpSessionAttributeListener
HttpSessionBindingListener
HttpSessionEvent

HttpSessionBingEvent
ServletContextServletContextListener
ServletContextAttributeListener
ServletContextEvent
ServletContextAttributeEvent
  • 会话管理:跟踪用户会话状态,以便在多个请求之间共享数据。主要涉及类有Cookie、Session。

以下是一些Java SE Servlet规范中的关键概念、接口和类:

1.1 Servlet 接口

所有 Servlet 都必须实现的接口,它定义了 Servlet 的生命周期方法,包括初始化、服务处理和销毁。

public interface Servlet {
	// 初始化执行
    void init(ServletConfig servletConfig) throws ServletException;

    ServletConfig getServletConfig();
	// 处理请求:任何请求初始都会进入 service 方法,由 service 方法进行请求的分发和处理
    void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;

    String getServletInfo();
	// 销毁时执行
    void destroy();
}

**HttpServlet类:**抽象类,扩展自GenericServlet类,简化了处理HTTP请求和响应的操作。开发人员通常继承HttpServlet类来实现自定义的Servlet。

在这里插入图片描述
创建一个自定义 HttpServlet:

public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 执行GET请求
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 执行POST请求
        super.doPost(req, resp);
    }
}

添加 HttpServlet 配置:

<servlet>
	<!--名称-->
	<servlet-name>myServlet</servlet-name>
	<!--类全路径-->
	<servlet-class>com.lzq.web.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
	<!--名称-->
	<servlet-name>myServlet</servlet-name>
	<!--Servlet与URL模式之间的映射关系-->
	<url-pattern>/**</url-pattern>
</servlet-mapping>

1.2 HttpServletRequest 接口

HttpServletRequest 接口是 Java Servlet 规范中定义的接口,用于表示服务器接收到的 HTTP 请求。它提供了一组方法,用于获取请求的各种属性和内容。
在这里插入图片描述
主要涉及方法如下:

  • 获取请求信息:
    • String getMethod(): 获取HTTP请求方法,如GET、POST等。
    • String getRequestURI(): 获取请求的URI(统一资源标识符)。
    • String getQueryString(): 获取请求的查询字符串部分。
    • String getHeader(String name): 获取指定名称的请求头的值。
    • Enumeration<String> getHeaderNames(): 获取所有请求头的名称。
  • 获取请求参数:
    • String getParameter(String name): 获取指定名称的请求参数的值。
    • Map<String, String[]> getParameterMap(): 获取所有请求参数的映射,键为参数名,值为参数值数组。
    • Enumeration<String> getParameterNames(): 获取所有请求参数的名称。
    • String[] getParameterValues(String name): 获取指定名称的请求参数的值数组。
  • 获取请求内容:
    • BufferedReader getReader(): 获取用于读取请求内容的BufferedReader对象。
    • InputStream getInputStream(): 获取用于读取请求内容的InputStream对象。
  • 获取客户端信息:
    • String getRemoteAddr(): 获取客户端的IP地址。
    • String getRemoteHost(): 获取客户端的主机名。
    • String getRemoteUser(): 获取经过身份验证的客户端用户。
  • 获取会话相关信息:
    • HttpSession getSession(): 获取与请求关联的会话对象。
    • HttpSession getSession(boolean create): 获取与请求关联的会话对象,如果不存在则根据参数决定是否创建新的会话。

1.3 HttpServletResponse 接口

HttpServletResponse 接口是 Java Servlet 规范中的一个子接口,继承自 ServletResponse 接口,它提供了更多与 HTTP 协议相关的方法和功能,用于处理HTTP请求的响应。
在这里插入图片描述
主要涉及方法如下:

  • setStatus() / getStatus():设置 / 获取响应的状态码,如200、404、500等。
  • setHeader() / getHandler() / getHandlers:设置 / 获取 http 响应头信息。
  • setContentType() / getContentType():设置 / 获取响应内容类型。
  • setCharacterEncoding():设置响应的字符编码。
  • sendRedirect():发送一个重定向响应到指定的URL。
  • ServletOutputStream getOutputStream():获取输出流,用于写入二进制数据到客户端。
  • PrintWriter getWriter():获取输出流,用于写入字符数据到客户端。
  • void addCookie(Cookie cookie):添加 Cookie。

1.4 Cookie 对象

javax.servlet.http.Cookie 类是 Java Servlet API 中的一部分,提供了对 HTTP Cookie 的封装和操作。它用于创建、设置和解析 Cookie,并与HTTP请求和响应一起使用。

主要涉及方法如下:

  • new Cookie(String name, String value):创建一个 Cookie,并指定name和value。
  • getMaxAge() / setMaxAge(int expiry):获取 / 设置 Cookie 的有效期(单位:秒,为-1时,表示永久)。
  • setPath(String uri) / getPath():设置 / 获取 Cookie 路径。
  • setSecure(boolean flag):设置是否仅通过安全协议传输Cookie。
  • setHttpOnly(boolean httpOnly) / isHttpOnly():设置 / 判断是否仅通过HTTP协议访问Cookie。

1.5 Filter 接口

javax.servlet.Filter 接口是 Java Servlet API 中的一部分,用于实现过滤器(Filter)组件。过滤器是在 Servlet 容器中对 HTTP 请求和响应进行预处理和后处理的组件,用于对请求和响应进行修改、验证、日志记录等操作。

过滤器在Servlet容器中按照配置的顺序对请求进行过滤,并可以修改请求或响应的内容、头信息或状态。过滤器可以用于实现一些通用的逻辑,如身份验证、日志记录、字符编码转换等。

创建一个过滤器:

public class MyFilter implements Filter {
    @Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
	        throws IOException, ServletException {
	    // 对请求进行预处理
	    // 进行逻辑处理,如验证、日志记录等
	    // 调用FilterChain的doFilter()方法继续处理链中的下一个过滤器或Servlet
	    chain.doFilter(request, response);
	    // 对响应进行后处理
	    // 进行逻辑处理,如修改响应内容、头信息等
	}
}

添加过滤器:

<filter>
	<!--过滤器名称-->
    <filter-name>myFilter</filter-name>
	<!--过滤器类名-->
    <filter-class>com.example.MyFilter</filter-class>
</filter>
<filter-mapping>
	<!--过滤器名称-->
    <filter-name>myFilter</filter-name>
	<!--过滤器与URL模式之间的映射关系-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

1.6 HttpSession 接口

HttpSession 是 Java Servlet API 中的一个接口,用于在 Web 应用程序中跟踪用户的会话状态。它提供了一种在多个 HTTP 请求之间存储和检索用户特定信息的机制。

HttpSession的特点是它在服务器端存储会话数据,而客户端只是通过会话ID来标识会话。会话数据可以是任意类型的Java对象,并可以在多个请求之间共享。

HttpSession是一种有状态的机制,用于跟踪用户的会话状态,提供了在Web应用程序中保持用户状态和数据的便捷方式。通过使用HttpSession,开发人员可以存储和检索用户特定的数据,以实现登录状态管理等功能。
在这里插入图片描述

主要方法如下:

  • HttpSession session = request.getSession();:获取当前请求的 HttpSession 对象。如果不存在会话,则创建一个新的会话。
  • HttpSession session = request.getSession(false);:获取当前请求的 HttpSession 对象。如果不存在会话,则返回null,不创建新的会话。
  • setAttribute() / getAttribute() / getAttributeNames:设置 / 获取会话属性:
  • getMaxInactiveInterval() / setMaxInactiveInterval(int interval): 获取 / 设置会话的最大非活动时间间隔(单位:秒)。
  • session.invalidate():使当前会话无效。
  • session.getId():获取会话的唯一标识符(ID)。
  • session.isNew():检查会话是否是新创建的。

二、SpringMVC

Spring MVC 是基于 Java 的 Web 应用程序开发框架,是 Spring Framework 的一部分。它提供了一种模型-视图-控制器(Model-View-Controller,MVC)的架构模式来开发灵活、可维护和可扩展的Web应用程序。

Spring MVC的主要特点和功能如下:

  • MVC架构模式:Spring MVC 采用经典的 MVC 架构模式,将应用程序分为模型(Model)、视图(View)和控制器(Controller)三层,以实现松耦合、可测试和可维护的应用程序。
  • 注解驱动:Spring MVC 提供了注解驱动的方式来定义和处理请求映射、表单验证、数据绑定等,使开发人员可以使用简单的注解来声明请求处理方法和参数绑定。
  • 强大的请求映射:Spring MVC 通过 @RequestMapping 注解提供了灵活而强大的请求映射机制,可以根据URL、请求方法、请求参数等条件来映射到相应的处理方法。
  • 数据绑定和表单处理:Spring MVC 支持将请求参数绑定到方法参数、JavaBean或命令对象中,还提供了表单验证和错误处理的机制。
  • 视图解析和渲染:Spring MVC 支持多种视图技术,如 JSP、Thymeleaf、Freemarker 等,并提供了视图解析器来根据逻辑视图名解析为具体的视图模板。
  • 拦截器和过滤器:Spring MVC 提供了拦截器(Interceptor)和过滤器(Filter)的机制,可以在请求处理前后进行预处理和后处理操作。
  • 国际化和本地化:Spring MVC 提供了国际化和本地化的支持,可以方便地实现多语言和地区的Web应用程序。
  • RESTful风格:Spring MVC 对于构建 RESTful 风格的 Web 服务提供了良好的支持。

Spring MVC 是一个成熟且广泛使用的Web应用程序框架,它结合了 Spring Framework 的依赖注入和面向切面编程的特性,为开发人员提供了强大而灵活的工具和功能来构建现代化的Web应用程序。通过使用 Spring MVC,开发人员可以采用清晰的架构模式,将应用程序的关注点分离开,提高代码的可读性、可测试性和可维护性。

2.1 Spring MVC核心概念

模型(Model):业务逻辑实体类,如POJO等。

视图(View):将模型的数据渲染成用户可见的输出。它可以是 JSP、Thymeleaf、Freemarker 等模板引擎,也可以是 JSON、XML 等格式的数据。

控制器(Controller):控制器负责处理用户请求并返回响应数据给客户端。通过处理HTTP请求,执行业务逻辑处理,最后通过视图进行渲染和返回。

处理器映射器(Handler Mapping):负责将 Http 请求映射到相应的控制器处理方法。

视图解析器(View Resolver):负责将逻辑视图名称解析为具体的视图实现。

中央调度器(DispatcherServlet):DispatcherServlet 是 Spring MVC 的核心组件,它接收所有的HTTP请求,并将请求分发给适当的处理器映射器和控制器进行处理,还负责管理整个请求-响应周期的生命周期和流程

2.2 DispatcherServlet

DispatcherServlet 是 Spring MVC 的核心组件,它接收所有的HTTP请求,并将请求分发给适当的处理器映射器和控制器进行处理,还负责管理整个请求-响应周期的生命周期和流程。类结构图如下:

类结构图

由类结构图可以知道信息如下:

  • EnvironmentAware:获取系统环境属性对象Environment。
  • ApplicationContextAware:获取Spring应用上下文对象ApplicationContext。
  • HttpServlet:Servlet API标准,处理所有Http请求。

DispatcherServlet 默认是懒加载的,系统会在接收到客户端第一个请求后,进行加载,执行Java SE Servlet 标准的 init() 方法进行初始化。

2.3 DispatcherServlet.init()

由 DispatcherServlet 的类结构图进行分析,在其父类 HttpServletBean 中找到了方法 javax.servlet.http.HttpServlet#init() 的具体实现,如下:

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
@Override
	public final void init() throws ServletException {

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// 初始化ServletBean
		initServletBean();
	}
}

initServletBean() 方法为一个抽象方法,最终会调用子类 FrameworkServlet#initServletBean() 中,如下:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
	protected final void initServletBean() throws ServletException {
		// 省略非核心方法.....
		try {
			// 重要方法:初始化一个Web应用上下文对象
			this.webApplicationContext = initWebApplicationContext();
			// 初始化FrameworkServlet对象:当前方法为一个空实现
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}
		// 省略非核心方法.....
	}

	protected void initFrameworkServlet() throws ServletException {
	}

	protected WebApplicationContext initWebApplicationContext() {
		// 根据Servlet上下文对象,获取一个root Web应用上下文
		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;
			/**
			 * 判断是否为一个可配置的Web应用上下文对象:如
			 * 				XmlWebApplicationContext / XmlServletWebApplicationContext
			 * 				AnnocationConfigWebApplicationContext  / AnnocationConfigServletWebApplicationContext
			 */
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				// 判断当前上下文是否活跃,在ssm框架中,会先初始化Spring上下文,active=true
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					// 配置和刷新Web应用程序上下文
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		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;
	}
}

在 DispatcherServlet 进行初始化时,执行顺序如下:

HttpServletBean#init() -> FrameworkServlet#initServletBean() -> FrameworkServlet#initWebApplicationContext() -> DispatcherServlet#onRefresh()

最终会调用到 DispatcherServlet 的 onRefresh() 方法中,执行刷新动作。

2.4 DispatcherServlet.onRefresh()

刷新上下文,在 DispatcherServlet 初始化时,或监听到 Spring ContextRefreshedEvent 事件后,都会执行当前方法DispatcherServlet.onRefresh(),刷新 DispatcherServlet。

public class DispatcherServlet extends FrameworkServlet {
	@Override
	protected void onRefresh(ApplicationContext context) {
		// 初始化SpringMVC的9大核心对象
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
}

SpringMVC 9大核心对象:

  1. MultipartResolver:用于处理文件上传
  2. LocaleResolver:用于处理国际化配置(SpringMVC中有具体的拦截器LocaleChangeInterceptor)
  3. ThemeResolver:用于设置主题Theme,类似于我们手机更换主题,不同的 UI,css 等
  4. HandlerMappers:映射器,用来将 Http 请求与 Controller 进行映射
  5. HandlerAdapters:处理适配器,主要包含 Http 请求处理器适配器,简单控制器处理器适配器,注解方法处理器适配器
  6. HandlerExceptionResolvers:异常处理器,基于 HandlerExceptionResolver 接口的异常处理
  7. RequestToViewNameTranslator:当 Controller 处理器方法没有返回一个 View 对象或逻辑视图名称,并且在该方法中没有直接往 Response 的输出流里面写数据的时候,Spring 将会采用约定好的方式提供一个逻辑视图名称
  8. ViewResolvers:视图处理器,根据 ModelAndView 选择对应的视图进行渲染
  9. FlashMapManager:用于管理FlashMap,FlashMap用于在redirect重定向中传递参数

自此,SpringMVC 完成了初始化动作,当客户端发起请求后,DispatcherServlet 对其进行处理,调用 javax.servlet.http.HttpServlet#service()

2.5 DispatcherServlet.service()

根据 Java SE Servlet 标准,客户端 Http 请求最初会进入 javax.servlet.http.HttServlet#service() 放,改方法被子类 FrameworkServlet 重写,如下:

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
	@Override
	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);
		}
	}
}

经分析,不管是 POST 请求,还是 GET 请求,最终调用执行 DispatcherServlet#doDispatch() 方法,处理并响应客户端。

2.6 DispatcherServlet.doDispatch()

所有 HTTP 方法都由这个方法进行调用处理,并返回结果给客户端。

public class DispatcherServlet extends FrameworkServlet {
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				// 检查是否为文件上传请求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 获取当前Request对应的处理器链
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					// 找不到,直接返回客户端
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 获取当前请求对应的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 执行对应方法,返回一个 ModeAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			} catch (Exception ex) {
				dispatchException = ex;
			} catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			// 处理结果,返回客户端
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		} catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		} catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		} finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			} else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}
}

在方法 DispatcherServlet.doDispatch() 中,存在几个重要步骤:

  1. mappedHandler = getHandler(processedRequest);:获取此请求的HandlerExecutionChain。
  2. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());:返回此处理程序对象的HandlerAdapter。
  3. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());:执行当前请求对应的方法,返回一个 ModelAndView。
  4. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);:处理程序调用结果(异常也会被解析为一个ModelAndView)。

2.7 一个请求执行流程

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711467.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第三章 搜索与图论(一)——深搜,广搜,图的存储与拓扑序列

文章目录 深度优先搜索广度优先搜索树和图的存储图的深搜 拓扑序深搜练习题842. 排列数字843. n-皇后问题 广搜练习题844. 走迷宫845. 八数码 树和图的存储与遍历练习题846. 树的重心847. 图中点的层次 拓扑序练习题848. 有向图的拓扑序列 深度优先搜索 数据结构&#xff1a; …

常见排序算法详解

文章目录 前言1. 排序算法简介2 算法效率2.1 度量一个程序执行时间两种方法2.2 时间频度2.3 时间复杂度2.4 常见的时间复杂度2.5 平均和最坏时间复杂度 3. 常见排序算法详解3.1 基数排序 (Radix Sort)(1) 算法过程(2)代码实现 3.2 冒泡排序 (Bubble Sort)(1) 算法过程(2) 代码实…

2023年7月2日leetcode每日一题打卡——125.验证回文串

一、题目描述与要求 125. 验证回文串 - 力扣&#xff08;LeetCode&#xff09; 题目描述 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给…

学习系统编程No.28【多线程概念实战】

引言&#xff1a; 北京时间&#xff1a;2023/6/29/15:33&#xff0c;刚刚更新完博客&#xff0c;目前没什么状态&#xff0c;不好趁热打铁&#xff0c;需要去睡一会会&#xff0c;昨天睡的有点迟&#xff0c;然后忘记把7点到8点30之间的4个闹钟关掉了&#xff0c;恶心了我自己…

C语言学习(三十)---枚举、位段、联合体

这几天在往实习的地方弄东西&#xff0c;比较累&#xff0c;因此没有更新&#xff0c;在几天前我们学习了内存操作函数&#xff0c;其与之前学习的字符串操作函数相比&#xff0c;适用范围更加广泛&#xff0c;大家要注意掌握学习&#xff0c;今天我们将学习枚举、位段和联合体…

闲置BROOKSTONE Rover间谍车重生记

22年春节在家&#xff0c;哪也去不了&#xff0c;收拾出来一个多年前的玩具&#xff0c;全名叫BROOKSTONE Rover revolution&#xff0c;长这个样子。 尽管是7年前的产品了&#xff0c;科技感依旧挺足 印象中能手机控制&#xff0c;并且能语音对讲。只是网上找到的安卓版应用已…

xenomai内核解析--xenomai实时线程创建流程

版权声明&#xff1a;本文为本文为博主原创文章&#xff0c;未经同意&#xff0c;禁止转载。如有错误&#xff0c;欢迎指正&#xff0c;博客地址&#xff1a;https://blog.csdn.net/qq_22654551?typeblog 文章目录 问题概述1 libCobalt中调用非实时POSIX接口2 阶段1 linux线程…

02_jQuery与Ajax

jquery jquery的作用 他是js的库 处理html,事件,实现动画效果,方便的为网站提供AJAX交互 命名格式 .ji:体积大,用于学习和debug使用 .min.js:压缩的文件,体积小,用于线上环境使用 使用方法 必须先在页面文件中进行引用 $就是jQuery 注意: jQuery是DOM的封装 jQuery和…

Spring Boot 中的服务网关是什么,原理,如何使用

Spring Boot 中的服务网关是什么&#xff0c;原理&#xff0c;如何使用 在微服务架构中&#xff0c;服务网关是一个非常重要的组件。它可以作为所有微服务的入口&#xff0c;负责路由、负载均衡、安全性和监控等方面的功能。Spring Boot 提供了一系列的服务网关工具&#xff0…

redis-哨兵安装

解决问题 自动故障修复 1.在主从模式的基础上,在主节点添加自己的认证密码即可 2.将代码客户端地址改为哨兵地址 ------------- 主节点配置 daemonize yes port 6379 bind 0.0.0.0 requirepass 123456 save 3600 1 300 100 60 10000dir /usr/local/redis dbfilename dump.r…

Java POI (4)—— Linux环境下文件解析过程出现OOM的问题

Excel文件在进行解析的时候&#xff0c;在Windows环境中&#xff0c;没用报错&#xff0c;但是在Linux环境中&#xff0c;出现了如下的报错&#xff1a; nested exception is javalang.OutofMemoryError: Java heap space &#xff08;OOM&#xff09; 一、内存溢出和栈溢出有什…

主流特征工程平台(一)

一. 目标 对于Feature Store的能力与边界&#xff0c;每家的定义略微不同&#xff0c;《Feature Stores - A Hierarchy of Needs》&#xff09;这篇文章做了很好的总结&#xff0c;大体分为如下几个层次&#xff1a; 特征管理&#xff1a;特征抽取、处理、存储、元数据管理&am…

群晖NAS 安装 MySQL 远程访问连接

目录 1. 安装Mysql 2. 安装phpMyAdmin 3. 修改User 表 4. 本地测试连接 5. 安装cpolar 6. 配置公网访问地址 7. 固定连接公网地址 [TOC] > **转载自cpolar极点云文章&#xff1a;[群晖NAS 安装 MySQL远程访问连接](https://www.cpolar.com/blog/install-mysql-remote-…

Spring 事务使用详解

前言 什么是事务&#xff1f;根据 维基百科事务 介绍&#xff0c;数据库事务&#xff08;简称&#xff1a;事务&#xff09;是数据库管理系统执行过程中的一个逻辑单位&#xff0c;由一个有限的数据库操作序列构成。简单来说&#xff0c;事务就是将一系列操作当成一个不可拆分…

报喜鸟如何触发数字化转型及品牌扩张战略分析

传统服装企业往往面临缺乏创新、盲目扩张、追求低成本、库存和行业周期性等问题。报喜鸟通过深入分析市场需求&#xff0c;明确聚焦主业&#xff0c;提出加强品牌核心竞争力的价值主张。通过实施DTC转型&#xff0c;发力电商平台和线下门店等举措&#xff0c;报喜鸟成功提高品牌…

VSC++=》 指针实数排序

缘由https://bbs.csdn.net/topics/396523482 void 指针实数排序(double* aa, int d) {//缘由https://bbs.csdn.net/topics/396523482double lin 0; int j d, jj 0;while (jj < d) if (--j > jj) if (aa[j] > aa[j - 1])lin aa[j], aa[j] aa[j - 1], aa[j - 1] …

Flask request和requests(客户端服务器)

Flask request和requests 1、Flask request属性2、requests属性3、实现代码 1、Flask request属性 这么多属性什么时候有值什么时候没值&#xff0c;其实完全取决于我们请求头content-type是什么&#xff0c;如果是以表单形式multipart/form-data、application/x-www-form-url…

辅助驾驶功能开发-功能规范篇(21)-3-XP行泊一体方案功能规范

XPilot Parking 自动泊车系统 七、全自动泊车(AutoParking) • 自动泊车辅助(AutoParking Assist)、斜列式车位泊车辅助(Diagonal AutoParking Assist) - 产品定义 基于超声波传感器和环视摄像头对空间和车位的识别,通过自动泊车系统实现全自动泊车入库。 - 功能说…

使用HHDESK图形化功能管理服务器

服务器的管理通常繁琐而枯燥&#xff0c;需要大量的命令行来执行。 所以图形化功能应运而生。 本篇以传输文件为例&#xff0c;简单介绍一下HHDESK的图形化管理功能。 首先需要配置好服务器。 点击连接管理&#xff0c;在连接类型中选择SSH&#xff0c;按照刚才在服务器中配…

方波信号轨迹跟踪(过冲与圆角)

在控制系统中&#xff0c;方波信号轨迹跟踪可能会面临过冲和圆角的问题。过冲是指跟踪信号超过期望值的现象&#xff0c;而圆角是指在方波信号变化时产生平滑的过渡。这些问题主要是因为传统的控制方法无法完美跟踪非线性的方波信号导致的。 过冲通常也称为超调。在方波信号的…