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。
监听对象 | 监听接口 | 监听事件 |
---|---|---|
ServletRequest | ServletRquestListener ServletRquestAttributeListener | ServletRequestEvent ServletRquestAttributeEvent |
HttpSession | HttpSessionListener HttpSessionActivationListener HttpSessionAttributeListener HttpSessionBindingListener | HttpSessionEvent HttpSessionBingEvent |
ServletContext | ServletContextListener 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大核心对象:
- MultipartResolver:用于处理文件上传
- LocaleResolver:用于处理国际化配置(SpringMVC中有具体的拦截器LocaleChangeInterceptor)
- ThemeResolver:用于设置主题Theme,类似于我们手机更换主题,不同的 UI,css 等
- HandlerMappers:映射器,用来将 Http 请求与 Controller 进行映射
- HandlerAdapters:处理适配器,主要包含 Http 请求处理器适配器,简单控制器处理器适配器,注解方法处理器适配器
- HandlerExceptionResolvers:异常处理器,基于 HandlerExceptionResolver 接口的异常处理
- RequestToViewNameTranslator:当 Controller 处理器方法没有返回一个 View 对象或逻辑视图名称,并且在该方法中没有直接往 Response 的输出流里面写数据的时候,Spring 将会采用约定好的方式提供一个逻辑视图名称
- ViewResolvers:视图处理器,根据 ModelAndView 选择对应的视图进行渲染
- 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()
中,存在几个重要步骤:
mappedHandler = getHandler(processedRequest);
:获取此请求的HandlerExecutionChain。HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
:返回此处理程序对象的HandlerAdapter。mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
:执行当前请求对应的方法,返回一个 ModelAndView。processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
:处理程序调用结果(异常也会被解析为一个ModelAndView)。