一、概述
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 {
@Override
protected 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();//重定向
}
@Override
protected 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/beans
http://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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://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.jsp
return "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上下文中
@Controller
public class ControllerTest2{
//映射访问路径
@RequestMapping("/t2")
public String index(Model model){
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "ControllerTest2");
//返回视图位置
return "test";
}
}
运行tomcat测试
可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
注解方式是平时使用的最多的方式!
_@_RequestMapping
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
只注解在方法上面
@Controller
public 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.*;
@Controller
public 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实现转发
@Controller
public 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来实现转发和重定向 - 无需视图解析器;
测试前,需要将视图解析器注释掉
@Controller
public 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
@NoArgsConstructor
public 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页面
@Controller
public 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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://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
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
@Controller
public class UserController {
@RequestMapping("/j1")
@ResponseBody
//它就不会走视图解析器,会直接返回一个字符串
public String json1() {
User user = new User("齐钊1号", 2, "男");
return user.toString();
}
}
5、使用ObjectMapper类的writeValueAsString()方法将对象进行解析
@Controller
public 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 ,十分便捷!
@RestController
public 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那种工具包一样,拿来用就好了!