一、概述
1.1、什么是MVC
- MVC是模型(dao、service) 视图(jsp) 控制层(Servlet),是一种软件设计规范。
- 是将业务逻辑、数据、显示分离的⽅法来组织代码。
- MVC主要作⽤是降低了视图与业务逻辑间的双向偶合。
- MVC不是⼀种设计模式,MVC是⼀种架构模式。当然不同的MVC存在差异。
Model(模型):数据模型,提供要展示的数据,因此包含数据和⾏为,可以认为是领域模型或 JavaBean组件(包含数据和⾏为),不过现在⼀般都分离开来:Value Object(数据Dao) 和 服务层 (⾏为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图):负责进⾏模型的展示,⼀般就是我们⻅到的⽤户界⾯,客户想看到的东⻄。
Controller(控制器):接收⽤户请求,委托给模型进⾏处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的⼯作。
最典型的MVC就是JSP + servlet + javabean的模式。
1.2、Model1时代
jsp的本质就是一个Servlet。
- 在web早期的开发中,通常采⽤的都是Model1。
- Model1中,主要分为两层,视图层和模型层。

Model1优点:架构简单,⽐较适合⼩型项⽬开发;
Model1缺点:JSP职责不单⼀,职责过重,不便于维护;
1.3、Model2时代
Model2把⼀个项⽬分成三部分,包括视图、控制、模型。
- ⽤户发请求
- Servlet接收请求数据,并调⽤对应的业务逻辑⽅法
- 业务处理完毕,返回更新后的数据给servlet
- servlet转向到JSP,由JSP来渲染⻚⾯
- 响应给前端更新后的⻚⾯
职责分析:
Controller:控制器
- 取得表单数据
- 调⽤业务逻辑
- 转向指定的⻚⾯
Model:模型
- 业务逻辑
- 保存数据的状态
View:视图
- 显示⻚⾯
Model2这样不仅提⾼的代码的复⽤率与项⽬的扩展性,且⼤⼤降低了项⽬的维护成本。Model 1模式的 实现⽐较简单,适⽤于快速开发⼩规模项⽬,Model1中JSP⻚⾯身兼View和Controller两种⻆⾊,将控 制逻辑和表现逻辑混杂在⼀起,从⽽导致代码的重⽤性⾮常低,增加了应⽤的扩展性和维护的难度。
Model2消除了Model1的缺点。
1.4、回顾Servlet
步骤一:创建Maven工程,添加pom依赖
<!-- 导入依赖 --><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.2</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency></dependencies>
步骤二:删除src文件,创建一个新Moudle,添加Web Application支持
步骤三:导⼊servlet 和 jsp 的 jar 依赖
可能会继承父项目的依赖,但是为了安全起见,可以先添加,后期再优化时候再删掉。
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.2</version></dependency></dependencies>
步骤四:编写⼀个Servlet类,⽤来处理⽤户的请求
package com.learn.servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取前端参数String method = req.getParameter("method");if (method.equals("add")) {req.getSession().setAttribute("msg", "执⾏了add⽅法");}if (method.equals("delete")) {req.getSession().setAttribute("msg", "执⾏了delete⽅法");}//2.调用业务层//3.视图转发或者重定向req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);//转发// resp.sendRedirect();//重定向}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
步骤五:编写Hello.jsp,在WEB-INF⽬录下新建⼀个jsp的⽂件夹,新建hello.jsp
Web目录:公共的页面 WEB-INF⽬录:用户不可见
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body>${msg}</body></html>
步骤六:在web.xml中注册Servlet
<?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><servlet-name>hello</servlet-name><servlet-class>com.learn.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><!-- 超时时间 --><session-config><session-timeout>15</session-timeout></session-config><!-- 默认欢迎页面 --><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>
步骤七:配置Tomcat,并启动测试
- http://localhost:8080/springMVC_01_servlet_war_exploded/hello?method=delete
- http://localhost:8080/springMVC_01_servlet_war_exploded/hello?method=add
点击 + , 选择Tomcat Server local

点击Fix

结果:
1.5、MVC框架要做哪些事情
- 将url映射到java类或java类的⽅法 .
- 封装⽤户提交的数据 .
- 处理请求—调⽤相关的业务处理—封装响应数据 .
- 将响应的数据进⾏渲染 . jsp / html 等表示层数据 .
说明:
常⻅的服务器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF; 常⻅前端MVC框架:vue、angularjs、react、backbone;由MVC演化出了另外⼀些模式如:MVP、 MVVM 等等….
MVVM:M V VM(ViewModel:双向绑定)
二、初识SpringMVC
2.1 概述
Spring MVC是Spring Framework的⼀部分,是基于Java实现MVC的轻量级Web框架。
查看官⽅⽂档:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web 或者老版本:https://docs.spring.io/spring-framework/docs/4.3.24.RELEASE/spring-framework-reference/html/mvc.html
我们为什么要学习SpringMVC呢?
Spring MVC的特点:
- 轻量级,简单易学
- ⾼效 , 基于请求响应的MVC框架
- 与Spring兼容性好,⽆缝结合
- 约定优于配置
- 功能强⼤:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。
DispatcherServlet的作⽤是将请求分发到不同的处理器。从Spring 2.5开始,使⽤Java 5或者以上版本 的⽤户可以采⽤基于注解形式进⾏开发,⼗分简洁;
正因为SpringMVC好 , 简单 , 便捷 , 易学 , 天⽣和Spring⽆缝集成(使⽤SpringIoC和Aop) , 使⽤约定优 于配置 . 能够进⾏简单的junit测试 . ⽀持Restful⻛格 .异常处理 , 本地化 , 国际化 , 数据验证 , 类型转换 , 拦截器 等等……所以我们要学习。
最重要的⼀点还是⽤的⼈多 , 使⽤的公司多 。
2.2 中心控制器
Spring的web框架围绕**DispatcherServlet**设计。DispatcherServlet的作⽤是将**请求分发**到不同的处理器。从Spring 2.5开始,使⽤Java 5或者以上版本的⽤户可以采⽤基于注解的controller声明⽅式。<br /> Spring MVC框架像许多其他MVC框架⼀样, 以请求为驱动 , **围绕⼀个中⼼Servlet分派请求及提供其他功能,DispatcherServlet是⼀个实际的Servlet (它继承⾃HttpServlet基类)**。<br />
2.3 SpringMVC执行原理
SpringMVC的原理如下图所示:
当发起请求时被前置的控制器拦截到请求,根据请求参数⽣成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中⼼控制器,控制器使⽤模型与视图渲染视图结果,将结果返回给中⼼控制器,再将结果返回给请求者。
下图为SpringMVC的⼀个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
简要分析执⾏流程
- DispatcherServlet表示前置控制器,是整个SpringMVC的控制中⼼。⽤户发出请求, DispatcherServlet接收请求并拦截请求。
我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。 - HandlerMapping为处理器映射。DispatcherServlet调⽤HandlerMapping,HandlerMapping根据请求url查找Handler。
- HandlerExecution表示具体的Handler,其主要作⽤是根据url查找控制器,如上url被查找控制器为: hello。
- HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
- HandlerAdapter表示处理器适配器,其按照特定的规则去执⾏Handler。
- Handler让具体的Controller执⾏。
- Controller将具体的执⾏信息返回给HandlerAdapter,如ModelAndView。
- HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
- DispatcherServlet调⽤视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
- 视图解析器将解析的逻辑视图名传给DispatcherServlet。
- DispatcherServlet根据视图解析器解析的视图结果,调⽤具体的视图。
- 最终视图呈现给⽤户。
在这⾥先听⼀遍原理,不理解没有关系,我们⻢上来写⼀个对应的代码实现⼤家就明⽩了,如果不明⽩,那就写10遍,没有笨⼈,只有懒⼈!
三、SpringMVC入门案例
3.1 配置版(了解即可,用注解版实现)
目录结构如下:
1、新建一个Moudle , springmvc-02-hello , 添加web的支持!
2、确定导入了SpringMVC 的依赖!
3、配置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:这是SpringMVC的核心:请求分发器|前端控制类--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--DispatcherServlet要绑定Spring的配置文件--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><!--启动级别:1--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!--/和 /*的区别:/ : 只匹配所有的请求,不会去匹配jsp/* : 匹配所有的请求,包括匹配jsp--><url-pattern>/</url-pattern></servlet-mapping></web-app>
4、编写SpringMVC 的 配置⽂件!名称:springmvc-servlet.xml : [servletname]-servlet.xml 说明,这⾥的名称要求是按照官⽅来的
5、添加 处理映射器
6、添加 处理器适配器
7、添加 视图解析器
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--添加 处理映射器--><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/><!--添加 处理器适配器--><bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/><!--视图解析器:DispatcherServlet给他的ModelAndView--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!--前缀--><property name="prefix" value="/WEB-INF/jsp/"/><!--后缀--><property name="suffix" value=".jsp"/></bean><!--Handler--><bean id="/hello" class="com.learn.controller.HelloController"/></beans>
8、编写我们要操作业务Controller ,要么实现Controller接⼝,要么增加注解;需要返回⼀个 ModelAndView,装数据,封视图;
package com.learn.controller;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 {public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {ModelAndView modelAndView = new ModelAndView();//业务代码String result = "HelloSpringMVC";modelAndView.addObject("msg",result);//视图跳转modelAndView.setViewName("hello");return modelAndView;}}
9、将⾃⼰的类交给SpringIOC容器,注册bean
<!--Handler--><bean id="/hello" class="com.kuang.controller.HelloController"/>
10、写要跳转的jsp⻚⾯,显示ModelandView存放的数据,以及我们的正常⻚⾯;
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body>${msg}</body></html>
可能遇到的问题:访问出现404,排查步骤:
- 查看控制台输出,看⼀下是不是缺少了什么jar包。
- 如果jar包存在,显示⽆法输出,就在IDEA的项⽬发布中,添加lib依赖!
- 重启Tomcat 即可解决!

⼩结:看这个估计⼤部分同学都能理解其中的原理了,但是我们实际开发才不会这么写,不然就疯了, 还学这个玩意⼲嘛!我们来看个注解版实现,这才是SpringMVC的精髓。
3.2 注解版
步骤一:新建⼀个Moudle,springmvc-03-hello-annotation 。添加web⽀持!
步骤二:在pom.xml⽂件引⼊相关的依赖:主要有Spring框架核⼼库、Spring MVC、servlet , JSTL 等。我们在⽗依赖中已经引⼊了!
步骤三:由于Maven可能存在资源过滤的问题,我们将配置完善
Maven项目都可以加以下代码
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>
步骤四:配置web.xml
<?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"><!--1.注册servlet--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--通过初始化参数指定SpringMVC配置⽂件的位置,进⾏关联--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><!-- 启动顺序,数字越⼩,启动越早 --><load-on-startup>1</load-on-startup></servlet><!--所有请求都会被springmvc拦截 --><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
注意点:
/ 和 /* 的区别:
< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进⼊spring的 DispatcherServlet类 。
< url-pattern > / </ url-pattern > 会匹配 .jsp,会出现返回 jsp视图 时再次进⼊spring的DispatcherServlet 类,导致找不到对应的 controller所以报404错。
- 注意web.xml版本问题,要最新版!
- 注册DispatcherServlet
- 关联SpringMVC的配置⽂件
- 启动级别为1
-
步骤五:添加Spring MVC配置⽂件
在resource⽬录下添加springmvc-servlet.xml配置⽂件,配置的形式与 Spring容器配置基本类似,为了⽀持基于注解的IOC,设置了⾃动扫描包的功能,具体配置信息如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 --><context:component-scan base-package="com.learn.controller"/><!-- 让Spring MVC不处理静态资源 --><mvc:default-servlet-handler/><!--支持mvc注解驱动在spring中一般采用@RequestMapping注解来完成映射关系要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例这两个实例分别在类级别和方法级别处理。而annotation-driven配置帮助我们自动完成上述两个实例的注入。--><mvc:annotation-driven/><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/"/><!-- 后缀 --><property name="suffix" value=".jsp"/></bean></beans>
在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
让IOC的注解生效
- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
- MVC的注解驱动
-
步骤六:创建Controller
编写一个Java控制类:com.learn.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) {//1.封装数据//向模型中添加属性msg与值,可以在JSP页面中取出并渲染model.addAttribute("msg", "hello,SpringMVC");//2.返回视图//web-inf/jsp/hello.jspreturn "hello";}}
@Controller是为了让Spring IOC容器初始化时⾃动扫描到;
- @RequestMapping是为了映射请求路径,这⾥因为类与⽅法上都有映射所以访问时应该是/HelloController/hello;
- ⽅法中声明Model类型的参数是为了把Action中的数据带到视图中;
- ⽅法返回的结果是视图的名称hello,加上配置⽂件中的前后缀变成WEB-INF/jsp/hello.jsp。
步骤七:创建视图层
在WEB-INF/ jsp⽬录中创建hello.jsp , 视图可以直接取出并展示从 Controller带回的信息;
可以通过EL表示取出Model中存放的值,或者对象;<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body>${msg}</body></html>
步骤八:配置Tomcat运⾏
总结
使⽤springMVC必须配置的三⼤件:
处理器映射器、处理器适配器、视图解析器 通常,我们只需要⼿动配置视图解析器,⽽处理器映射器和处理器适配器只需要开启注解驱动即可, ⽽省去了⼤段的xml配置
_@_Controller
- @Controller注解类型用于声明Spring类的实例是一个控制器(在讲IOC时还提到了另外3个注解);
Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描。
<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.kuang.controller"/>
增加一个ControllerTest2类,使用注解实现;
//@Controller注解的类会自动添加到Spring上下文中@Controllerpublic class ControllerTest2{//映射访问路径@RequestMapping("/t2")public String index(Model model){//Spring MVC会自动实例化一个Model对象用于向视图中传值model.addAttribute("msg", "ControllerTest2");//返回视图位置return "test";}}
运行tomcat测试

可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
注解方式是平时使用的最多的方式!
_@_RequestMapping
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
只注解在方法上面
@Controllerpublic class TestController {@RequestMapping("/h1")public String test(){return "test";}}
同时注解类与方法
@Controller@RequestMapping("/admin")public class TestController {@RequestMapping("/h1")public String test(){return "test";}}
访问路径:http://localhost:8080/项目名/admin /h1
需要先指定类的路径再指定方法的路径;<!--/和 /*的区别:/ : 只匹配所有的请求,不会去匹配jsp/* : 匹配所有的请求,包括匹配jsp-->
四、RestFul 风格
4.1 概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
4.2 功能
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
分别对应 添加、 删除、 修改、查询。
传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE
4.3 代码
package com.learn.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.*;@Controllerpublic class RestfulController {//原来的风格:http://localhost:8080/SpringMVC_03_annotation_war_exploded/add?a=1&b=1@RequestMapping("/add")public String test(int a, int b, Model model) {int res = a + b;model.addAttribute("msg", "结果为" + res);return "hello";}//restful风格:http://localhost:8080/SpringMVC_03_annotation_war_exploded/add2/1/2//@RequestMapping(value = "/add2/{a}/{b}",method = RequestMethod.DELETE )@GetMapping("add3/{a}/{b}")public String test2(@PathVariable int a, @PathVariable int b, Model model) {int res = a + b;model.addAttribute("msg", "结果1为" + res);return "hello";}@PostMapping("add3/{a}/{b}")public String test3(@PathVariable int a, @PathVariable int b, Model model) {int res = a + b;model.addAttribute("msg", "结果2为" + res);return "hello";}}
4.4 总结
1、_@_PathVariable 让方法参数的值对应绑定到一个URI模板变量上。 2、所有的地址栏请求默认都会是 HTTP GET 类型的。 3、Spring MVC 的 _@_RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。 4、方法级别的注解变体有如下几个:组合注解
_@_GetMapping 是一个组合注解,平时使用的会比较多!它所扮演的是 _@_RequestMapping(method =RequestMethod.GET) 的一个快捷方式。
五、数据处理及转发
5.1 结果跳转方式
5.1.1 ModelAndView(不建议)
设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .
页面 : {视图解析器前缀} + viewName +{视图解析器后缀}
<!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean>
对应的controller类
public class ControllerTest1 implements Controller {public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {//返回一个模型视图对象ModelAndView mv = new ModelAndView();mv.addObject("msg","ControllerTest1");mv.setViewName("test");return mv;}}
5.1.2 ServletAPI(不建议)
通过设置ServletAPI , 不需要视图解析器 .
1、通过HttpServletResponse进行输出
2、通过HttpServletResponse实现重定向
3、通过HttpServletResponse实现转发
@Controllerpublic class ResultGo {@RequestMapping("/result/t1")public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {rsp.getWriter().println("Hello,Spring BY servlet API");}@RequestMapping("/result/t2")public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {rsp.sendRedirect("/index.jsp");}@RequestMapping("/result/t3")public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {//转发req.setAttribute("msg","/result/t3");req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);}}
5.1.3 SpringMVC(建议!!!)
方式一:通过SpringMVC来实现转发和重定向 - 无需视图解析器;
测试前,需要将视图解析器注释掉
@Controllerpublic class ResultSpringMVC {@RequestMapping("/rsm/t1")public String test1(){//转发return "/index.jsp";}@RequestMapping("/rsm/t2")public String test2(){//转发二return "forward:/index.jsp";}@RequestMapping("/rsm/t3")public String test3(){//重定向return "redirect:/index.jsp";}}
方式二:通过SpringMVC来实现转发和重定向 - 有视图解析器;
重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.
可以重定向到另外一个请求实现 。
@RequestMapping("/t1")public String test(Model model){model.addAttribute("msg","ForwardController");return "hello";}@RequestMapping("/t2")public String test2(Model model){model.addAttribute("msg","ForwardController");return "redirect:/index.jsp";}
转发:return “hello”; 重定向:return “redirect:/index.jsp”;
5.2 数据处理
5.2.1 处理提交数据
(1) 提交的域名称和处理方法的参数名一致
提交数据 :http://localhost:8080/d/t1?name=qizhao
处理方法 :String name
@Controller()@RequestMapping("/d")public class DealData {@RequestMapping("/t1")public String test1(String name, Model model){//1.接受前端参数System.out.println("接收到前端的参数为"+name);//2.返回前端的参数model.addAttribute("msg",name);//3.跳转视图return "hello";}}
(2) 提交的域名称和处理方法的参数名不一致
提交数据 : http://localhost:8080/d/t1?name=qizhao
处理方法 :@RequestParam(“username”) : username提交的域的名称 .
//@RequestParam("username") : username提交的域的名称 .@RequestMapping("/hello")public String hello(@RequestParam("username") String name){System.out.println(name);return "hello";}
后台输出:qizhao
无论名字一不一样,都把@RequestParam(“username”)写上,表明这个参数是从前端接收的,可以和其他参数区别开
(3) 提交的是一个对象
要求提交的表单域和对象的属性名一致 , 参数使用对象即可
实体类:
public class User {private int id;private String name;private int age;//构造//get/set//tostring()}
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency>
lomko可以简便实体类的写法
@Data@AllArgsConstructor@NoArgsConstructorpublic class Student {private int id;private String name;private int age;}
提交数据 : http://localhost:8080/d/t3?id=1&name=qz&age=10
处理方法 :User user
@RequestMapping("/t3")public String test3(User user, Model model){//1.接受前端参数System.out.println("接收到前端的参数为"+user);//2.返回前端的参数model.addAttribute("msg",user);//3.跳转视图return "hello";}
后台输出 :User{id=1, name=’qz’, age=10}
说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。
5.2.2 数据显示到前端
方式一:通过ModelAndView
我们前面一直都是如此 . 就不过多解释
public class ControllerTest1 implements Controller {public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {//返回一个模型视图对象ModelAndView mv = new ModelAndView();mv.addObject("msg","ControllerTest1");mv.setViewName("test");return mv;}}
方式二:通过ModelMap
@RequestMapping("/hello")public String hello(@RequestParam("username") String name, ModelMap model){//封装要显示到视图中的数据//相当于req.setAttribute("name",name);model.addAttribute("name",name);System.out.println(name);return "hello";}
方式三:通过Model
@RequestMapping("/ct2/hello")public String hello(@RequestParam("username") String name, Model model){//封装要显示到视图中的数据//相当于req.setAttribute("name",name);model.addAttribute("msg",name);System.out.println(name);return "test";}
对比:
就对于新手而言简单来说使用区别就是:
Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解; ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性; ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
当然更多的以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解。
5.3 乱码问题
1、写一个表单页面,
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><form action="/e/t1" method="post"><input type="text" name="name"><input type="submit"></form></body></html>
2、EncodingController,将表单提交的信息输出到test.jsp页面
@Controllerpublic class EncodingController {@RequestMapping("/e/t1")public String test1(String name, Model model) {System.out.println(name);model.addAttribute("msg", name);return "test";}}
3、测试发现,页面和控制台中文都乱码。(控制台都乱码,说明是出现在java层面)
解决办法一(自己写过滤器)(了解即可)
1、编写EncodingFilter类实现Filter接口,重写doFilter方法
import javax.servlet.*;import java.io.IOException;public class EncodingFilter implements Filter {public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");//继续往下走filterChain.doFilter(servletRequest,servletResponse);}public void destroy() {}}
2、在web.xml文件中配置过滤器
<!--过滤器--><filter><filter-name>encoding</filter-name><filter-class>com.learn.filter.EncodingFilter</filter-class></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>
解决办法二(使用SpringMVC过滤器)
<!--过滤器--><filter><filter-name>encoding</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>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>
六、JSON
什么是JSON?
- JSON( JavaScript Object Notation, JS 对象标记 ) 是一种轻量级的数据交换格式,目前使用特别广泛。
- 采用完全独立于编程语言的文本格式来存储和表示数据。
- 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
JavaScript 语言中,一切都是对象。因此,任何JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:
对象表示为键值对,数据由逗号分隔
- 花括号保存对象
- 方括号保存数组
JSON 键值对是用来保存 JavaScript 对象的一种方式,和 JavaScript 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 “” 包裹,使用冒号 : 分隔,然后紧接着值:
{"name": "QinJiang"}{"age": "3"}{"sex": "男"}
很多人搞不清楚 JSON 和 JavaScript 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:
JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串
JSON 和 JavaScript 对象互转
要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}');//结果是 {a: 'Hello', b: 'World'}
要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'});//结果是 '{"a": "Hello", "b": "World"}'
JSON的使用
1、新建项目,导入依赖
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.8</version></dependency>
2、配置web.xml和配置文件springmvc-sevlet.xml
<?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"><!--1.注册servlet--><servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--通过初始化参数指定SpringMVC配置文件的位置,进行关联--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><!-- 启动顺序,数字越小,启动越早 --><load-on-startup>1</load-on-startup></servlet><!--所有请求都会被springmvc拦截 --><servlet-mapping><servlet-name>SpringMVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!--过滤器--><filter><filter-name>encoding</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>encoding</filter-name><url-pattern>/</url-pattern></filter-mapping></web-app>
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.learn.controller"/><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean></beans>
3、新建User类和UserController类
@Data@AllArgsConstructor@NoArgsConstructorpublic class User {private String name;private int age;private String sex;}
@Controllerpublic class UserController {@RequestMapping("/j1")@ResponseBody//它就不会走视图解析器,会直接返回一个字符串public String json1() {User user = new User("齐钊1号", 2, "男");return user.toString();}}
5、使用ObjectMapper类的writeValueAsString()方法将对象进行解析
@Controllerpublic class UserController {@RequestMapping("/j1")@ResponseBody//它就不会走视图解析器,会直接返回一个字符串public String json1() throws JsonProcessingException {//创建一个jackson的对象映射器,用来解析数据ObjectMapper mapper = new ObjectMapper();User user = new User("齐钊1号", 2, "男");//将我们的对象解析成为json格式String s = mapper.writeValueAsString(user);return s;}}
6、测试,对象转换成了json格式。
7、通过@RequestMaping的produces属性解决乱码
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
8、代码优化
a.乱码统一解决
上一种方法比较麻烦,如果项目中有许多请求则每一个都要添加,可以通过Spring配置统一指定,这样就不用每次都去处理了!
我们可以在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置!
<mvc:annotation-driven><mvc:message-converters register-defaults="true"><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"><property name="failOnEmptyBeans" value="false"/></bean></property></bean></mvc:message-converters></mvc:annotation-driven>
b.返回json字符串统一解决
在类上直接使用 _@_RestController ,这样子,里面所有的方法都只会返回 json 字符串了,不用再每一个都添加_@_ResponseBody !我们在前后端分离开发中,一般都使用 _@_RestController ,十分便捷!
@RestControllerpublic class UserController {//produces:指定响应体返回类型和编码@RequestMapping(value = "/json1")public String json1() throws JsonProcessingException {//创建一个jackson的对象映射器,用来解析数据ObjectMapper mapper = new ObjectMapper();//创建一个对象User user = new User("秦疆1号", 3, "男");//将我们的对象解析成为json格式String str = mapper.writeValueAsString(user);//由于@ResponseBody注解,这里会将str转成json格式返回;十分方便return str;}}
不同类型的json输出
1、集合类型
@RequestMapping("/j2")public String json2() throws JsonProcessingException {//创建一个jackson的对象映射器,用来解析数据ObjectMapper mapper = new ObjectMapper();List<User> users = new ArrayList<User>();User user1 = new User("齐钊1号", 1, "男");User user2 = new User("齐钊2号", 2, "男");User user3 = new User("齐钊3号", 3, "男");users.add(user1);users.add(user2);users.add(user3);String s = mapper.writeValueAsString(users);return s;//可以写成return new ObjectMapper().writeValueAsString(users);}
2、时间类型
@RequestMapping("/j3")public String json3() throws JsonProcessingException {//创建一个jackson的对象映射器,用来解析数据ObjectMapper mapper = new ObjectMapper();Date date = new Date();String s = mapper.writeValueAsString(date);return s;}
格式化时间,方法一:关闭mapper配置
@RequestMapping("/j4")public String json4() throws JsonProcessingException {ObjectMapper mapper = new ObjectMapper();mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);Date date = new Date();String s = mapper.writeValueAsString(date);return s;}
格式化时间,方法二:使用SimpleDateFormat
@RequestMapping("/j3")public String json3() throws JsonProcessingException {ObjectMapper mapper = new ObjectMapper();//使用SimpleDateFormat格式化时间SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = new Date();String s = mapper.writeValueAsString(sdf.format(date));return s;}
创建工具类
package com.learn.utils;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializationFeature;import java.text.SimpleDateFormat;/*** 工具类*/public class JsonUtils {//方法的重载,不需要传递日期格式public static String getJson(Object object){return getJson(object,"yyyy-MM-dd HH:mm:ss");}//需要传递日期格式public static String getJson(Object object,String dateFormat){ObjectMapper mapper = new ObjectMapper();//不使用时间戳的方式mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);//自定义日期的格式SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);mapper.setDateFormat(sdf);try {return mapper.writeValueAsString(object);} catch (JsonProcessingException e) {e.printStackTrace();}return null;}}
工具类的测试
@RequestMapping("/j5")public String json5() throws JsonProcessingException {Date date = new Date();return JsonUtils.getJson(date);//return JsonUtils.getJson(date,"yyyy-MM-dd HH:mm:ss");}

@RequestMapping("/j6")public String json6() throws JsonProcessingException {List<User> users = new ArrayList<User>();User user1 = new User("齐钊1号", 1, "男");User user2 = new User("齐钊2号", 2, "男");User user3 = new User("齐钊3号", 3, "男");users.add(user1);users.add(user2);users.add(user3);return JsonUtils.getJson(users);}
fastjson(阿里的Json工具类)
fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。
fastjson 的 pom依赖!
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency>
fastjson 三个主要的类:
JSONObject 代表 json 对象
- JSONObject实现了Map接口, 猜想 JSONObject底层操作是由Map实现的。
- JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取”键:值”对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。
JSONArray 代表 json 对象数组
- 内部是有List接口中的方法来完成操作的。
JSON代表 JSONObject和JSONArray的转化
- JSON类源码分析与使用
- 仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化。
代码测试,我们新建一个FastJsonDemo 类
package com.kuang.controller;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.kuang.pojo.User;import java.util.ArrayList;import java.util.List;public class FastJsonDemo {public static void main(String[] args) {//创建一个对象User user1 = new User("秦疆1号", 3, "男");User user2 = new User("秦疆2号", 3, "男");User user3 = new User("秦疆3号", 3, "男");User user4 = new User("秦疆4号", 3, "男");List<User> list = new ArrayList<User>();list.add(user1);list.add(user2);list.add(user3);list.add(user4);System.out.println("*******Java对象 转 JSON字符串*******");String str1 = JSON.toJSONString(list);System.out.println("JSON.toJSONString(list)==>"+str1);String str2 = JSON.toJSONString(user1);System.out.println("JSON.toJSONString(user1)==>"+str2);System.out.println("\n****** JSON字符串 转 Java对象*******");User jp_user1=JSON.parseObject(str2,User.class);System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);System.out.println("\n****** Java对象 转 JSON对象 ******");JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));System.out.println("\n****** JSON对象 转 Java对象 ******");User to_java_user = JSON.toJavaObject(jsonObject1, User.class);System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);}}
这种工具类,我们只需要掌握使用就好了,在使用的时候在根据具体的业务去找对应的实现。和以前的commons-io那种工具包一样,拿来用就好了!


