SpringMVC-02-什么是SpringMVC
cnblogs 2024-07-01 08:09:06 阅读 66
1、概述
Spring MVC是Spring Framework的Web开发部分,是基于Java实现MVC的轻量级Web框架。
- 官方文档:https://docs.spring.io/spring-framework/docs/4.3.24.RELEASE/spring-framework-reference/html/
- 中文官方文档:https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/spring_mvc_documentation_linesh_translation-y2ud27r5.html
- 中文文档下载地址:https://www.jb51.net/books/593599.html
为什么要学习SpringMVC ?
Spring MVC的特点:
- 轻量级,简单易学
- 高效 , 基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。
DispatcherServlet的作用是将请求分发到不同的Handler。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;
正因为SpringMVC好 , 简单 , 便捷 , 易学 , 天生和Spring无缝集成(可以使用IOC和AOP) , 使用约定优于配置 ,能够进行简单的junit测试 ,支持Restful风格 ,异常处理 ,本地化 ,国际化 ,数据验证 ,类型转换 ,拦截器 等等......所以我们要学习。
最重要的一点还是用的人多 , 使用的公司多 。
2、SpringMVC 核心组件
- 前端控制器:DispactherServlet
- 处理器映射器:HandlerMapping
- 处理器适配器:HandlerAdapter
- 处理器:Handler
- 视图解析器:ViewResolver
组件介绍:
- DispactherServlet:用于接收所有的客户端请求并将请求分发给合适的Handler(Controller)进行处理。在原生JavaWeb中,分发请求是由 Tomcat 根据 web.xml 来做的,SpringMVC使用 DispactherServlet 替代了这个功能。(不需要程序员开发)
- HandlerMapping:解析每个请求的URL,找到对应的 Handler。(不需要程序员开发)
- HandlerAdapter:适配器模式,适配调用具体的 Handler。(不需要程序员开发)
- Handler:Controller层组件,对客户端请求进行逻辑处理,替代了原生JavaWeb中的 Servlet 所做的功能。(需要程序员手动开发)
- ViewResolver:将 SpringMVC 中的 逻辑视图名 拼接成具体的视图地址,简化了视图地址的编写。(不需要程序员开发)
Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。
3、SpringMVC 执行原理
简要分析执行流程
- 当用户通过浏览器发起一个HTTP请求,请求直接到前端控制器 DispatcherServlet;
- 前端控制器接收到请求以后调用处理器映射器HandlerMapping,处理器映射器根据请求的URL解析出URI,找到具体的Handler,并将它返回给前端控制器;
- 前端控制器调用处理器适配器HandlerAdapter去适配调用Handler;
- 处理器适配器会根据Handler去调用真正的处理器去处理请求,并且处理对应的业务逻辑;
- 当处理器处理完业务之后,会返回一个ModelAndView对象给处理器适配器,HandlerAdapter再将该对象返回给前端控制器;ModelAndView 中的 Model 是指 被Model层执行业务处理后返回的数据对象,View 是指 将要转向的视图信息。
- 前端控制器DispatcherServlet将返回的ModelAndView对象传给视图解析器ViewResolver进行解析,解析完成之后就会返回一个具体的视图地址给前端控制器;(ViewResolver根据逻辑的View查找具体的View)
- 前端控制器DispatcherServlet转向具体的视图,并进行渲染;
- 渲染完成之后响应给用户(浏览器显示);
4、HelloSpringMVC(举个实例)
4.1、配置版
新建一个Moudle , SpringMVC-02-HelloMVC , 添加web的支持!
确定导入了SpringMVC 的依赖!
配置web.xml , 注册DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
version="4.0">
<!--注册DispatcherServlet-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联一个springmvc的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--启动级别-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置说明:
init-param
:当请求进入此servlet时,会携带的初始参数classpath:
与classpath*:
:前者指的是 当前项目的编译路径,后者代表的是 当前项目及其依赖jar包的所有编译路径load-on-startup
:启动级别,当值为0或者大于0时,表示容器在应用启动时就加载这个servlet,值越小,优先级越高,当值是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。url-pattern 为 ‘/’
:默认匹配,详见 关于tomcat中servlet的url-pattern匹配规则
编写SpringMVC 的 配置文件!名称:spring-mvc.xml,添加处理器映射器、 处理器适配器、 视图解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Handler映射器 -->
<bean />
<!-- Handler适配器 -->
<bean />
<!-- 视图解析器 -->
<bean >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
编写处理器 Handler ,实现Controller接口 或者 增加注解;需要返回一个ModelAndView,装数据,封视图
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg","HelloMVC");
mv.setViewName("hello");
return mv;
}
}
将编写的 Handler 交给SpringIOC容器,注册bean,添加到spring-mvc.xml中。
<!-- Handler -->
<bean />
编写要跳转的jsp页面,显示ModelandView存放的数据,以及正常页面;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>hello</title>
</head>
<body>
${msg}
</body>
</html>
配置Tomcat,启动测试!
可能遇到的问题:访问出现404,排查步骤:
查看控制台输出,看一下是不是缺少了什么jar包。
如果缺少jar包,就在项目结构中添加相应的lib依赖!
重启Tomcat 即可解决!
4.2、注解版
新建一个Moudle,SpringMVC-03-Annotation ,添加web支持!
在pom.xml文件引入相关的依赖:主要有Spring框架核心库、Spring MVC、Servlet、 JSTL等。在父项目中已经引入了!
<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置说明:在上面的配置版讲过了,这里一样,就不说了。
添加Spring MVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- 组件扫描 -->
<context:component-scan base-package="com.moondream.controller"/>
<!-- 使用 default-servlet 处理静态资源 -->
<mvc:default-servlet-handler/>
<!-- 使用mvc注解驱动 -->
<mvc:annotation-driven/>
<bean >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
配置说明:
加入了两个Xml命名空间
context
、mvc
<context:component-scan/>
:组件扫描命名空间 context 下的标签,扫描指定包下所有被模板注解标注了的类,将其bean化并注册到IOC中。
模板注解 即 @component 及其 组合注解(@Controller、@Configuration……),它们大都位于 org.springframework.stereotype 包下,抽象定义为 项目的某个模块,比如 @Controller 代表 项目的 控制层,@Service 代表 项目的 服务层,@Configuration 代表 项目的 配置,@Component 代表 项目的 组件。
不想使用组件扫描,也可以将其手动一个个注册成bean
<bean />
<mvc:default-servlet-handler/>
:使用 default-servlet 来处理请求命名空间 mvc 下的标签,将 匹配到的请求 转发到 Tomcat 内建的 default servlet 中处理。
一般用来处理 静态资源请求(.html、.css、.mp3、.jpg……),使用此标签要注意 配置中多个HandlerMapping的优先级问题,免得 非静态资源请求 被匹配上 default servlet ,那样会报 404 错误。
其本质是
<bean >
<!-- SimpleUrlHandlerMapping 通过urlMap来找相应的Handler -->
<property name="urlMap">
<map>
<entry key="/**" value="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0"/>
</map>
</property>
</bean>
<bean />
<bean />
<mvc:annotation-driven/>
:使用 mvc 注解驱动命名空间 mvc 下的标签,使用注解的方式来处理请求。
其本质是
<bean >
<!-- 使用高优先级 -->
<property name="order" value="0"/>
</bean>
<bean />
(spring3.1之前使用的是 DefaultAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter )
(spring3.1之后替换成如上述所示)
注解方式处理请求的简要流程:
1)Spring容器启动,
RequestMappingHandlerMapping
在bean加载完成后,扫描出所有带有@Controller注解的bean,进而遍历出其中所有带有@RequestMapping注解 的方法,将方法上@RequestMapping注解信息封装成RequestMappingInfo
对象,其包含了请求路径、请求方法、请求参数、请求头等信息,将方法本身封装成HandlerMethod
对象,并以RequestMappingInfo
为键,HandlerMethod
为值,存储在一个Map结构中,以便于后续查找。2)当一个HTTP请求到达
DispatcherServlet
时,DispatcherServlet
会调用RequestMappingHandlerMapping
的getHandler()
方法来查找到相应的 Handler,这里就是HandlerMethod
。3)如果找到了匹配的
HandlerMethod
,DispatcherServlet
就会根据其类型选择合适的HandlerAdapter
(即RequestMappingHandlerAdapter
)来执行它,并返回结果;如果没有找到匹配的HandlerMethod
,DispatcherServlet
就会抛出异常并使用下一个 HandlerMapping 来查找 Handler 或者返回404错误页面。4)如果被封装成
HandlerMethod
的方法 或者 其上的类 被@ResponseBody注解 标注,那么HandlerMethod
的返回结果就是一个单纯的数据字符串,会被当成响应体直接返回给客户端浏览器,否则就是逻辑视图名,会被视图解析器解析后转向具体视图。详见这篇文章的 RequestMappingHandlerMapping 部分:Spring MVC源码解析——HandlerMapping(处理器映射器)
编写Controller层,创建一个Java类:com.moondream.controller.HelloController , 注意编码规范
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/HelloController")
public class HelloController {
//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model){
//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");
// /WEB-INF/jsp/hello.jsp
return "hello";
}
}
注意:
- 标注在类上的 @RequestMapping,相当于前置要求,每个
RequestMappingInfo
在被封装时,都会将类和对应方法上的 @RequestMapping 注解信息 整合注入自己。 - 被封装成
HandlerMethod
的方法,返回值必须是String类型,方法参数会被框架进行数据绑定。如果想在访问的请求上加数据,可以设置一个Model类型的参数,这个参数对象将由框架传入,它会把自身被加入的数据封装到访问的请求上。不要设置ModelAndView类型的参数,它在HandlerMethod
中无效。 - 一个
RequestMappingInfo
匹配的请求范围不能被另一个RequestMappingInfo
完全覆盖,不然程序会报错。简单来说,你要保证一个RequestMappingInfo
至少有一类请求能匹配上。
- 标注在类上的 @RequestMapping,相当于前置要求,每个
创建视图层
在WEB-INF/jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
${msg}
<br/>
<%out.print(request.getRequestURI());%>
</body>
</html>
配置Tomcat运行,开启服务器,访问对应的请求路径!
http://localhost:8080/spring03/HelloController/hello
运行成功!!!
4.3、小结
实现步骤:
- 新建一个web项目
- 导入相关jar包
- 编写web.xml , 注册DispatcherServlet
- 编写springmvc配置文件
- 接下来就是去创建对应的控制类 , controller
- 最后完善前端视图和controller之间的对应
- 测试运行调试.
使用springMVC必须配置的三大件:
- 处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,省去了大段的xml配置。
不知道有没有小伙伴注意到一个问题:
在上述注解版的案例中,我们通过命名空间mvc下的两个标签default-servlet-handler
和annotation-driven
,在SpringIOC中配置了多个HandlerMapping和HandlerAdapter,
那么,当请求到达时,DispatcherServlet该使用哪个HandlerMapping和HandlerAdapter呢?
当IOC中有多个HandlerMapping时:
- DispatcherServlet会优先使用优先级高的HandlerMapping(所有HandlerMapping中,都有一个order属性,代表优先级)
- 如果HandlerMapping优先级一样,DispatcherServlet会使用先注册的
- 如果使用的HandlerMapping根据请求找不到Handler,DispatcherServlet会继续使用下一优先级的HandlerMapping继续找,直到找到Handler
当IOC中有多个HandlerAdapter时:
- DispatcherServlet会遍历所有注册的HandlerAdapter,根据请求的Handler类型来选择对应的HandlerAdapter
- 如果有多个HandlerAdapter都能够支持当前请求的Handler类型,选择第一个适配成功的HandlerAdapter来执行Handler,即选择先注册的HandlerAdapter
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。