SpringMVC的基本概念
三层架构和MVC
-
三层架构概述
- (1)表现层:也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。 - (2)业务层:也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。
- (3)持久层:也就是我们是常说的 dao 层。负责数据持久化,和数据库做交互。
- (1)表现层:也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。
-
model1模式介绍
- 此模式十分简单,页面显示,控制分发,业务逻辑,数据访问全部通过jsp实现
-
model2模式介绍
- 此模式通过两部分去实现,即jsp与Servlet,jsp负责页面显示,servlet负责控制分发,业务逻辑以及数据访问
-
MVC模式介绍
- MVC模式则分为三大块,即视图(View),控制器(Control),模型(Model),视图是jsp负责页面显示,控制器则是servlet负责的控制分发,模型包括Service和Dao两个部分,分别负责业务逻辑和数据访问
-
三种模式对比
- Model1 模式的优缺点
优点:架构简单,比较适合小型项目开发
缺点:JSP职责不单一,职责过重,不便于维护 - Model2 模式的优缺点:
优点:职责清晰,较适合于大型项目架构
缺点:不适合小型项目开发 - MVC模式的优缺点:
优点:分工明确,各司其职,互不干涉,适用于大型项目架构,有利于组件的重构
缺点:增加了系统开发的复杂度
- Model1 模式的优缺点
SpringMVC概述
-
SpringMVC介绍
- SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
- MVC思想的良好实现,轻量级(容易使用,对环境要求不高)web框架(处理请求,做出相应 运行在web服务器中)低耦合框架设计
- MVC的思想实现:Struts1(apache), webwork(webwork), webwork--->apache(struts2)--->SpringMVC
-
SpringMVC在三层架构中的位置
- springMVC位于三层架构中的表现层,作用是接收请求响应数据,响应的数据通过视图、模板展示给用户。
-
SpringMVC的优势
- 清晰的角色划分(springMVC设计了一堆工具类,每个类各司其职) :
前端控制器(DispatcherServlet)
请求到处理器映射(HandlerMapping)
处理器适配器(HandlerAdapter)
视图解析器(ViewResolver)
处理器或页面控制器(Controller)
验证器(Validator)
命令对象(Command 请求参数绑定到的对象就叫命令对象)
表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。 - 分工明确,而且扩展点相当灵活,可以很容易扩展,虽然不需要
- 由于命令对象就是一个POJO,无需继承框架特定API,可以使用命令对象直接作为业务对象
- 和Spring其他框架无缝集成,是其他Web框架所不具备的
- 可适配,通过HandlerAdapter可以支持任意类作为处理器
- 可定制性,HandlerMapping、ViewResolver 等能够非常简单的定制。
- 功能强大的数据验证、格式化、绑定机制
- 利用Spring提供的Mock对象能够非常简单的进行Web层单元测试
- 本地化、主题的解析的支持,更容易进行国际化和主题切换
- 强大的JSP标签库,使JSP编写更容易
- RESTful风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配
置支持等等
- 清晰的角色划分(springMVC设计了一堆工具类,每个类各司其职) :
-
SpringMVC流程图
SpringMVC入门
SpringMVC入门案例
1、入门案例需求分析
构建页面index.jsp发起请求,在服务器端处理请求,控制台打印处理请求成功,跳转main.jsp成功页面;
2、构建maven项目并添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
3、web.xml中配置核心控制器DispatcherServlet
<servlet>
<servlet-name>springmvc01</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc01</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
4、配置springMVC的配置文件springmvc.xml
<!--扫描注解包-->
<context:component-scan base-package="com.offcn.controller"></context:component-scan>
<!--处理器映射器:根据请求路径匹配映射路径找到对应的执行器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!--处理器适配器:根据处理器映射器返回的执行器对象,去执行执行器对象-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<!--视图解析器:解析视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
5、构建页面发起请求
<a href="/hello/test1">hello</a>
6、编写控制器并使用注解配置
@Controller
@RequestMapping("hello")
public class HelloController {
@RequestMapping("test1")
public String test1(){
System.out.println("正在处理请求");
return "main";
}
}
7、启动服务器测试
pom.xml文件中服务器插件配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!--指定编码格式-->
<uriEncoding>utf-8</uriEncoding>
<!--指定项目启动后的访问路径-->
<path>/</path>
<!--指定访问端口号-->
<port>8888</port>
</configuration>
</plugin>
</plugins>
</build>
使用html页面充当前端技术的时候访问404问题:
产生问题的原因:
tomcat默认配置 web.xml:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
DefaultServlet作用:
我们向服务器发送请求: css js 图片 html,Default会根据我们请求的资源找到具体的内容给客户端做出响应
当前的servlet的url-pattern是 /
http://localhost:8080/css/login.css(jquery.js/image/q.jpg)
JspServlet作用:
***.jsp--->转换(***.java)--->***.class(jsp就是一种特殊形式的servlet)
url-pattern是 *.jsp
SpringMVC: 配置了前端控制器
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
当前处理SpringVC请求的servlet的url-pattern 覆盖服务器上的DefaultServlet的url-pattern,所有我们发送到 **.html的请求就被DispatcherServlet处理.DispatcherServlet无法处理该请求所以抛出了404异常
解决:
1:
a:更换DispatcherServlet的url-pattern
<url-pattern>*.do</url-pattern>
b:修改控制器方法中的@RequestMapping("/showInfo.do")
存在问题:
上述方案可以解决我们的请求路径的问题,但是不符合springmvc编码的风格同时无法使用其他的技术restful
2:在springmvc配置文件中设定服务器DefaultServlet处理的请求(静态资源)
<mvc:default-servlet-handler/>
SpringMVC执行过程及原理分析
-
案例的执行过程
- 浏览器客户端发起请求,请求到达服务器tomcat,tomcat将请求相关信息参数封装到对象request和response中,再将request和response对象交给service方法处理,在service方法中会根据请求路径将请求交给对应的controller执行器处理。
-
SpringMVC的请求响应过程
- 浏览器发送请求,被DispatcherServlet捕获,DispatcherServlet没有直接处理请求,而是将请求交给HandlerMapping处理器映射器,处理器映射器根据请求路径去controller控制层中匹配对应的执行器,并将匹配结果返回给DispatcherServlet,由DispacherServlet调用HandlerAdapter处理器适配器来执行控制层执行器方法;
执行器方法执行后的返回结果,由DispatcherServlet交给视图解析器ViewResolver来处理,找到对应的结果视图,渲染视图,并将结果响应给浏览器。
- 浏览器发送请求,被DispatcherServlet捕获,DispatcherServlet没有直接处理请求,而是将请求交给HandlerMapping处理器映射器,处理器映射器根据请求路径去controller控制层中匹配对应的执行器,并将匹配结果返回给DispatcherServlet,由DispacherServlet调用HandlerAdapter处理器适配器来执行控制层执行器方法;
SpringMVC常用组件介绍
1、DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
2、HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3、Handler:处理器(Controller)
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。
4、HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。注解形式的适配器。
5、View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6、View:视图(jsp html freemark jsf thymeleaf...)
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
7、mvc:annotation-driven标签说明:
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。使用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter (处 理 适 配 器 ),可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代处理映射器和适配器的配置(一般开发中都需要该标签)。注意:我们只需要编写处理具体业务的控制器以及视图。
<mvc:annotation-driven> 标签相当于以下配置:
<!-- HandlerMapping处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerM
apping"></bean>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
RequestMapping注解
-
用于定义映射路径,建立请求url和控制层方法之间的对应关系
-
RequestMapping注解的描述
- 注解位置:
a.类上:定义一级映射路径
b.方法上:定义二级映射路径 - 注解属性:
value:用于指定映射路径URL,value和path属性作用一样
method:用于指定请求的方式
params:用于指定限制请求参数的条件,它支持简单的表达式。要求请求参数的key和value必须和配置一模一样
eg:params={“username”},表示请求参数必须有username;
@RequestParam:param属性
headers:用于指定限制请求消息头的条件
@RequestHeader
注意:多个属性之间是有关系的
- 注解位置:
SpringMVC中请求参数绑定
绑定说明
-
绑定机制
servlet接收表单数据: <input type="text" name="age"> http://....?name=age String name = request.getParameter("name"); 数据类型转换 beanutils.jar apache推出的工具 <form> <input type="text" name="age"> <input type="text" name="name"> <input type="text" name="bir"> </form> class Person{ age name bir } bean.populate(request.getParameterMap(),new Person); 我们都知道,表单中请求参数都是基于 key=value 的。SpringMVC绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
-
支持的数据类型
- 基本类型参数:包括基本类型和Spring类型
- POJO类型参数:包括实体类,以及关联的实体类
- 数组和集合类型参数:包括List结构和Map结构的集合(包括数组)
-
使用要求
-
基本类型或者 String 类型:要求参数名称必须和控制器中方法的形参名称保存一致
-
POJO类型或它的关联对象:要求表单中参数名称和POJO类的属性名称保存一致,并控制器方法的参数类型是POJO类型
-
集合类型
- 要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同。给 List 集合中的元素赋值,使用下标。给 Map 集合中的元素赋值,使用键值对。
- 接收的请求参数是 json 格式数据。需要借助一个注解实现。
-
参数绑定示例
-
基本类型和Sring类型作为参数
(1)页面定义请求: <form action="/hello/test2" method="post"> 用户名:<input name="userName" type="text"> 年龄:<input name="age" type="text"> <input type="submit" value="提交"> </form> (2)执行器方法绑定参数: @RequestMapping("test2") public String test2(String userName,int age){ System.out.println("用户名:"+userName); System.out.println("年龄:"+age); return "main"; }
-
POJO类型作为参数
(1)页面定义请求: <form action="/hello/test3" > 用户名:<input name="pname" type="text"> 年龄:<input name="age" type="text"> 车名称:<input name="car.cname" type="text"> 车价格:<input name="car.cprice" type="text"> <input type="submit" value="提交"> </form> (2)执行器方法绑定参数: @RequestMapping("test3") public String test3(Person person){ System.out.println(person); return "main"; }
-
POJO类中包含集合类型参数
(1)页面定义请求: <form action="/hello/test4" > 用户名:<input name="pname" type="text"> 年龄:<input name="age" type="text"> 车名称:<input name="car.cname" type="text"> 车价格:<input name="car.cprice" type="text"> list集合车1:车名称<input name="carList[0].cname" type="text"> list集合车1:车价格<input name="carList[0].cprice" type="text"> list集合车2:车名称<input name="carList[1].cname" type="text"> list集合车2:车价格<input name="carList[1].cprice" type="text"> map集合车1:车名称<input name="map['x'].cname" type="text"> map集合车1:车价格<input name="map['x'].cprice" type="text"> map集合车2:车名称<input name="map['y'].cname" type="text"> map集合车2:车价格<input name="map['y'].cprice" type="text"> <input type="submit" value="提交"> </form> (2)执行器方法绑定参数: @RequestMapping("test4") public String test4(Person person){ System.out.println(person); return "main"; } set集合 private Set<Dog> dogSet; public Person(){ dogSet = new HashSet<>(); dogSet.add(new Dog()); dogSet.add(new Dog()); } <form action="/hello/test4" > dogSet:<input name="dogSet[0].dname" type="text"><br> dogSet:<input name="dogSet[1].dname" type="text"><br> <input type="submit" value="提交"> </form>
-
数组类型参数
(1)页面定义请求: <form action="/hello/test5" > 爱好1:<input name="hobbies" type="text"> 爱好2:<input name="hobbies" type="text"> 爱好3:<input name="hobbies" type="text"> <input type="submit" value="提交"> </form> (2)执行器方法绑定参数: @RequestMapping("test5") public String test5(String[] hobbies){ for(String hobby:hobbies){ System.out.println(hobby); } return "main"; }
参数绑定示例(参数位置使用servletAPI)
(1)引入servletAPI的依赖jar包:(注意jar包作用范围provided:不参与项目部署)
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
(2)执行器方法绑定参数:
@RequestMapping("test6")
public void test6(HttpServletRequest request, HttpServletResponse response,HttpSession session) throws ServletException, IOException {
System.out.println("我在请求转发");
request.getRequestDispatcher("/hello/test1").forward(request,response);
}
注意: SrpingMVC是对servlet技术的扩展
请求乱码
tomacat 对 GET 和 POST 请求处理方式是不同的:
(1)GET 请求的编码问题,要改 tomcat 的 server.xml配置文件:
<Connector connectionTimeout="20000" port="8080"protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
(2)POST 请求的编码问题,要在web.xml文件中配置编码过滤器:
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
自定义参数处理
-
使用场景
- SpringMVC不能自动识别参数转换为我们需要的数据类型,浏览器报400错误,类型转换异常;
- 400:表单提交数据和后台控制器代码接收数据的格式不匹配
(1)定义类型转换器; public class MyDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date=simpleDateFormat.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } } (2)配置类型转换器; <bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <bean class="com.offcn.util.MyDateConverter"></bean> </property> </bean> <!-- 在我们映射器和适配器的简化的配置中 调用我们自定义的转换器工具类 --> <mvc:annotation-driven conversion-service="formattingConversionService"/> (3)引用类型转换器; <form action="/hello/test7" > 入职日期:<input name="hireDate" type="date"> <input type="submit" value="提交"> </form> @RequestMapping("test7") public String test7(Date hireDate){ System.out.println(hireDate); return "main"; }
SpringMVC注解
RequsetParms
-
RequsetParam注解介绍
- 使用在方法入参位置,用于指定请求参数名称,将该请求参数绑定到注解参数位置。
- 属性:name:指定要绑定的请求参数名称;
required:指定请求参数是否必传;默认值是true
defaultValue:指定当没有传入请求参数时的默认取值;
@RequestMapping("test1") public String test1(@RequestParam(name = "pname",required = true,defaultValue = "李四")String name){ System.out.println(name); return "main"; }
RequestHeader
-
RequestHeader注解介绍
- 注解在方法入参位置,用于获取请求头信息
request.getHeader("名字");
@RequestMapping("test2") public String test2(@RequestHeader("Upgrade-Insecure-Requests")String data){ System.out.println(data); return "main"; }
- 注解在方法入参位置,用于获取请求头信息
CookieValue
-
CookieValue注解介绍
-
用于方法入参位置,把指定cookie名称的值传入控制器方法参数。
Cookie[] cks = request.getCooke();@RequestMapping("test5") public String test5(@CookieValue("JSESSIONID") String data){ System.out.println(data); return "main"; }
-
RequestBody
-
RequestBody注解介绍
- 用于方法入参位置,获取请求体内容。直接使用得到是 key=value&key=value...结构的数据。get请求方式不适用。通常用于将json格式字符串绑定到bean对象中;
(1)直接获取请求体内容: <form action="/annotation/test3" method="post"> 用户名:<input name="userName" type="text"> 年龄:<input name="age" type="text"> <input type="submit" value="提交"> </form> @RequestMapping("test3") public String test3(@RequestBody String data){ System.out.println(data); return "main"; } (2)将json格式请求参数绑定到指定对象bean中: <button onclick="sendCar()">发起ajax请求</button> <script> function sendCar() { $.ajax({ type: "POST", url: "/annotation/test4", data: '{"cname":"宝马","cprice":"20"}', contentType:"application/json", success: function(msg){ alert( "Data Saved: " + msg ); } }); } </script> @RequestMapping("test4") public String test4(@RequestBody Car car){ System.out.println(car); return "main"; } 注意:容易415异常 1: 因为我们使用springmvc整合json,使用json操作数据之前需要引入json第三方的包 <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.4</version> </dependency> 2:服务对jackson的支持需要使用tomcat8及以上服务器版本
ModelAttribute
-
ModelAttribute注解介绍
- 该注解是SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。
出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
出现在参数上,获取指定的数据给参数赋值。
request.setAttribute(key,value) 请求域中添加共享的数据
- 该注解是SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。
-
ModelAttribute注解使用案例
(1)注解在方法上: @ModelAttribute("shareParam") public String test6(){ System.out.println("我是公共方法"); return "公共参数"; } (2)注解在参数位置: @RequestMapping("test7") public String test7(@ModelAttribute("shareParam")String data){ System.out.println(data); return "main"; }