如上图,Spring MVC 程序的完整执行流程如下:
用户通过浏览器发送请求,请求会被 Spring MVC 的『前端控制器』DispatcherServlet 接收。
DispatcherServlet 拦截到请求后,会调用『处理器映射器』HandlerMapping 。
『处理器映射器』根据请求 URL 找到具体的『处理器』Handler ,生成『处理器』对象(如果有,还会生成拦截器对象)并返回给 DispatcherServlet 。
DispatcherServlet 根据返回信息(Handler)选择合适的『处理器适配器』HandlerAdapter 。
HandlerAdapter 会调用并指定 Handler 。此处和上述所说的处理器 Handler ,就是我们所编写的 Controller 类。
Controller 执行完成后,会返回一个 ModelAndView 对象,该对象中会包含『视图名』和『模型对象』。
HandlerAdapter 将 ModelAndView 返回给 DispatcherServlet 。
DispatcherServlet 会根据返回信息(ModelAndView)选择一个合适的『视图解析器』ViewResolver 。
视图解析器 ViewResolver 解析视图后,会向 DispatcherServlet 返回具体的 View 对象。
DispatcherServlet 对 View 进行渲染。即,将『模型数据』填充至『视图』中。
DispatcherServlet 将渲染后的结果返回、发送给客户端浏览器。
在上述执行过程中,DispatcherServlet、HandlerMapping、HandlerAdapter 和 ViewResolver 对象的工作都是在框架内部执行的,开发人员并不需要关心这些对象内部实现过程。
和程序员有关的内容只有 Handler(即,代码中的 Controller),和 ModelAndView 对象。
Spring Web 的『模型 - 视图 - 控制器』(MVC)框架是围绕 DispatcherServlet 设计的,它处理所有的 HTTP 请求和响应。
以下是对应于到 DispatcherServlet 的传入 HTTP 请求的事件顺序:
在接收到 HTTP 请求后,DispatcherServlet 会查询 HandlerMapping,并通过 Adapter 调用相应的 Controller 。
Controller 接受请求并根据使用的 GET 或 POST 方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给 DispatcherServlet 。
DispatcherServlet 将从 ViewResolver 获取请求的定义视图。当视图完成,DispatcherServlet 将模型数据传递到最终的视图,并在浏览器上呈现。
需要通过使用 web.xml 文件中的 URL 映射来映射希望 DispatcherServlet 处理的请求。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
...
<!-- 配置 SpringMVC 前端控制器 -->
<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
...
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>*.do</url-pattern> <!-- 后缀拦截 -->
</servlet-mapping>
</web-app>
SpringMVC 有两次加载 Spring 配置文件的时机:
首先在 SpringMVC 项目启动时,会依据 web.xml 配置文件中所配置的监听器:ContextLoaderListener 去加载对应位置下的 Spring 配置文件。
Spring 配置文件版:web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-dao.xml,
classpath:spring-service.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Spring 代码配置版:web.xml
<context-param>
<param-name>contextClass</param-name>
<param-value> <!-- 相较于 XML 配置,Java 代码配置『多』一个这个 -->
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.example.config.SpringServiceConfig,
com.example.config.SpringDaoConfig
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
无论你有没有利用第一次加载时机(配置 ContextLoadListener),那么 Spring MVC 都会继续进入第二个加载配置文件时机:根据 DispatcherServlet 的初始化配置(<init-param>)加载对应位置的 Spring 配置。
Spring 配置文件版:web.xml
<servlet>
<servlet-name>sampleServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
...
</servlet-mapping>
Spring 代码配置版:web.xml
<servlet>
<servlet-name>sampleServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param><!-- 相较于 XML 配置,Java 代码配置『多』一个这个 -->
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.example.config.SpringWebConfig</param-value>
</init-param>
</servlet>
<servlet-mapping>
...
</servlet-mapping>
如果是 XML 配置文件,这里的 Spring 配置文件的路径如果是以 classpath: 开始,则表示配置文件是在 classpath 路径下。否则,就是在项目的工程根目录(webapp)>下。
SpringMVC 首先加载的是 <context-param> 配置的内容,而并不会去初始化 servlet。只有进行了网站的跳转,经过了 DispatcherServlet 的导航的时候,才会初始化 Servlet,从而加载 <init-param> 中的内容。
一般而言,
<context-param> 配置的 Spring 配置文件,习惯性叫 applicationContext ,或 webApplicationContext ,表示全局性 Spring 配置。
<init-param> 配置的 Spring 配置文件可以叫 SpringWebApplicationContext ,表示 spirng-webmvc(web 层)相关的 Spring 配置。
现在来看看 SpringWebConfig.java 文件的必需配置:
@Configuration
@EnableWebMvc
@ComponentScan("com.example.inlet.controllers")
public class SpringWebConfig implements WebMvcConfigurer {
...
}
Spring MVC 配置类的关键,除了实现 WebMvcConfigurer 接口之外,还有就是需要标注 @EnableWebMvc 注解。
因为 @EnableWebMvc 注解的存在,Spring MVC 会自动创建、激活使用 19 个 Bean,这 19 个 Bean 在各个场景、环境中默默地支持着 Spring MVC 框架的的工作。
当然,如果也可以不使用 @EnableWebMvc 注解,这样的话,你需要直接(或间接地)去声明这 19 个 Bean(或其等价的替代品)。否则,Spring MVC 就不会以你想当然地方式运行。
另外,你也无须担心这默认的 19 个 Bean 是否符合你的需求,在合适的常见中,当你觉得其中的某个所提供的功能并非你所需要,你可以自定义 Bean 去覆盖、替代它(们)。
DispatcherServlet 将请求委派给控制器以执行特定于其的功能。 @Controller 注释指示特定类充当控制器的角色。@RequestMapping 注释用于将 URL 映射到整个类或特定处理程序方法。
@Controller
public class HelloController {
@RequestMapping(value="/hello", method=RequestMethod.GET)
public ModelAndView printHello() {
ModelAndView mav = new ModelAndView();
mav.addObject("message", "Hello Spring MVC Framework!");
mav.setViewName("/WEB-INF/jsp/hello.jsp");
return mav;
}
}
@Controller 注释将类定义为 Spring MVC 控制器。
@RequestMapping 的第一个用法表示此控制器上的所有处理方法都与 /hello
路径相关。
@RequestMapping(method = RequestMethod.GET) 用于声明 printHello 方法作为控制器的默认服务方法来处理 HTTP GET 请求。可以定义另一个方法来处理同一 URL 的任何 POST 请求。
value 属性指示处理程序方法映射到的 URL,method 属性定义处理 HTTP GET 请求的服务方法。关于以上定义的控制器,需要注意以下几点:
在服务方法中定义所需的业务逻辑。可以根据需要在此方法内调用其他方法。
基于定义的业务逻辑,将在此方法中创建一个模型。可以设置不同的模型属性,这些属性将被视图访问以呈现最终结果。此示例创建且有属性 message 的模型。
定义的服务方法可以返回一个 String,它包含要用于渲染模型的视图的名称。此示例将 "hello"
返回为逻辑视图名称。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Spring MVC 支持许多类型的视图用于不同的表示技术。包括 JSP,HTML,PDF,Excel 工作表,XML,Velocity 模板,XSLT,JSON,Atom 和 RSS 源,JasperReports 等。这里使用的是 JSP 模板,并在 /WEB-INF/hello/hello.jsp 中写一个简单的 hello 视图:
<html>
<head>
<title>Hello Spring MVC</title>
</head>
<body>
<h2>${message}</h2>
</body>
</html>
这里 ${message}
是在 Controller 中设置的属性。可以在视图中显示多个属性。
另一张图,表达了同样的逻辑:
更多【spring-SpringMVC基础之工作流程】相关视频教程:www.yxfzedu.com