SpringMVC学习笔记
SpringMVC学习笔记
ssm: MyBatis + Spring + SpringMVC
1. 回顾MVC
1.1 什么是 MVC
- MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
- 是将业务逻辑、数据、显示分离的方法来组织代码。
- MVC 主要作用是降低了视图与业务逻辑间的双向耦合。
- MVC 不是一种设计模式,MVC 是一种架构模式,当然不同的 MVC 存在差异。
Model(模型):数据类型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或 JavaBean 组件(包含数据和行为)。不过现在一般都分离开来: Value Object(数据 Dao)和服务层(行为 Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变)。处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
最典型的MVC 就是 JSP + Servlet + JavaBean 的模式
1.2 Model1时代
-
在web早期的开发中,通常采用的是 Model1。
-
Model1中,主要分为两层,视图层和模型层
Model1 的优点:架构简单,比较适合小型项目的开发。
Model1 的缺点:JSP 职责不单一,职责过重,不便于维护。
1.3 Model2时代
Model2 把一个项目分成三部分,包括视图、控制、模型。
职责分析:
Controller:控制器
-
获取表单数据
-
调用业务逻辑
-
转向指定的页面
Model:模型
- 业务逻辑
- 保存数据的状态
View:视图
- 显示页面
Model1 和 Model2 的对比:
Model2 这样不仅提高了代码的复用率与项目的拓展性,而且大大降低了项目的维护成本。
Model1模式的实现比较简单,适用于快速开发小规模项目,Model1 中的 JSP 页面身兼 View 和 Controller 两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的拓展性和维护的难度。Model2 消除了 Model1 的缺点。
1.4 回顾Servlet
-
新建一个 Maven 工程当作父工程,并在 pom.xml 中导入需要的依赖
<dependencies> <!-- junit:测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- SpringMvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- 开启Servlet支持 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- 开启jsp支持 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> </dependencies>
-
建立一个 Module:SpringMVC-01-Servlet ,添加 Web app的支持
-
编写一个Servlet类:HelloServlet
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取前端参数 String method = req.getParameter("method"); if ("add".equals(method)){ req.getSession().setAttribute("msg","执行了add方法"); } if ("delete".equals(method)){ req.getSession().setAttribute("msg","执行了delete方法"); } // 调用业务层 // 视图转发或重定向 req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
在 web.xml 中配置 servlet
<servlet> <servlet-name>hello</servlet-name> <servlet-class>servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
配置 Tomcat 服务器,并将工程项目打包发布
-
直接用 url 请求 Servlet
http://localhost:8888/hello?method=add http://localhost:8888/hello?method=delete
MVC 框架要做哪些事情
- 将 url 映射到 java 类 或 java 方法
- 封装用户提交的数据
- 处理请求,调用相关的业务处理,封装响应数据
- 将响应的数据进行渲染 jsp/html 等表示层数据
说明:
常见的服务端 MVC 框架: Struts、Spring MVC、ASP.NET MVC、Zend Frameword、JSF;
常见前端 MVC 框架:vue、angularjs、react、backbone;
由 MVC 演化出了另外一些模式入:MVP、MVVM 等等
2. 什么是Spring MVC
2.1 概述
Spring MVC 是 Spring Framework 的一部分,是基于 Java 实现 MVC 的轻量级 Web 框架。
官方文档地址:https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/web.html#spring-web
我们为什么要学习 Spring MVC呢?
Spring MVC 的特点:
- 轻量级、简单易学
- 高效、基于请求响应的 MVC 框架
- 与 Spring 兼容性好,无缝结合
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
正是因为 SpringMVC 好,简单,边界,易学,天生和 Spring 无缝集成(使用 SpringIoC 和 Aop),使用约定优于配置,能够进行简单的 junit 测试,支持 Restful 风格,异常处理,本地化,国际化,数据验证,类型转换,拦截器等等,所以我们要学习。
最重要的一点还是用的人多,使用的公司多。
2.2 中心控制器
Spring 的 web 框架围绕 DispatcherServlet【调度Servlet】设计
DispatcherServlet 的作用是将请求分发到不同的处理器。从 Spring 2.5 开始,使用 Java 5 或者以上版本的用户可以采用基于注解形式进行开发,十分简洁,用户可以采用基于注解的 controller 声明方式。
Spring MVC 框架像许多其它 MVC 框架一样,以请求为驱动,围绕一个中心 Servlet 分派请求及提供其它功能,DispatcherServlet 是一个实际的 Servlet (它继承自 HttpServlet 基类)。
(图片引自博客 https://blog.csdn.net/licwzy/article/details/81875635)
SpringMVC 的原理如下图所示:
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器。再将结果返回给请求者。
2.3 SpringMVC 原理
(图引自狂神说公众号)
图为 SpringMVC 的一个较完整的流程图,实线表示 SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
需要分析执行流程
-
DispatcherServlet 表示前置控制器,是整个 SpringMVC 的控制中心。用户发出请求,DispatcherServlet 接收请求并拦截请求。
我们假设请求的url为:http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080:服务器域名
SpringMVC: 部署在服务器上的 web 站点
hello: 表示控制器
通过分析,如上url表示为:请求位于http://localhost:8080 上的 SpringMVC 站点的 hello 控制器
-
HandlerMapping 为处理器映射。DispatcherServlet 调用HandelerMapping,HandlerMapping 根据请求 url 查找 Handler。
-
HandlerExecution表示具体的 Handler,其主要作用是根据 url 查找控制器,如上 url 被查找控制器为 :hello。
-
HandlerExecution 将解析后的信息传递给 DispatcherServlet,如解析控制器映射等。
-
HandlerAdapter 表示处理器适配器,其按照特定的规则去执行 Handler。
-
Handler 让具体的 Controller 执行。
-
Controller 将具体的执行信息返回给 HandlerAdapter ,如 ModelAndView。
-
HandlerAdapter 将视图逻辑名或模型传递给 DispatcherServlet。
-
DispatcherServlet 调用视图解析器(ViewResolver)来解析 HandlerAdapter 传递的逻辑视图名。
-
视图解析器将解析的逻辑视图名传给 DispatcherServlet。
-
视图解析器将解析的逻辑视图结果,调用具体的视图。
-
最总视图呈现给用户。
3. 第一个SpringMVC项目
3.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>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 关联一个SpringMVC配置文件:【servlet-name】 springmvc-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springmvc-servlet.xml</param-value> </init-param> <!-- 启动级别:1 --> <load-on-startup>1</load-on-startup> </servlet> <!-- / :匹配所有的请求(不包括.jsp) --> <!-- /* :匹配所有的请求(包括.jsp) --> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
编写我们要操作的业务 Controller,要么实现 Controller 接口,要么增加注解;需要返回一个 ModelAndView ,装数据,封视图
public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { // 创建 ModelAndView 对象 ModelAndView mv = new ModelAndView(); // 封装对象,放在 ModelAndView 中,Model mv.addObject("msg","HelloSpringMVC!"); // 封装要跳转的视图,放在 ModelAndView 中,相当于 /WEB-INF/jsp/hello.jsp mv.setViewName("hello"); return mv; } }
-
编写 SpringMVC 配置文件:springmvc-servlet.xml
- 添加处理器映射器
- 添加处理器适配器
- 添加视图解析器
- 将自己的类交给 SpringIoC 容器,注册 Bean
<?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 https://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" id="internalResourceViewResolver"> <!-- 前缀 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 后缀 --> <property name="suffix" value=".jsp"/> </bean> <!-- 跳转的视图 --> <bean id="/hello" class="controller.HelloController"/> </beans>
-
创建需要跳转的 jsp 页面,并显示 ModelAndView 中存放的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
-
配置 Tomcat 并启动测试
在 url 地址栏中输入 http://localhost:8080/hello
可能遇到的问题,访问出现404,排查步骤:
- 查看控制台输出,看一下是不是缺少了什么 jar 包。
- 如果 jar 包存在,显示无法输出,就在 IDEA 的项目发布中,添加 lib文件夹,并添加对应的依赖
- 重启 Tomcat即可解决
上面配置版的 SpringMVC项目,能更好地理解 SpringMVC 的原理。但是,在我们实际开发中并不会这么写。而是使用注解进行开发!
3.2 注解版
-
新建一个Moudle:SpringMVC-03-Hello-Annotation,添加 web 支持
-
导入依赖,并解决 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>
-
创建 SpringMVC 配置文件
在 resource 目录下创建个 config 文件夹,并添加springmvc-servlet.xml 配置文件,配置的形式与 Spring 容器配置基本类似,为了支持基于注解的 IoC,配置了自动扫描包的功能,
配置步骤:
- 让 IoC 的注解生效
- 静态资源过滤: HTML、JS、CSS、图片、视频 等
- MVC 的注解驱动
- 配置视图解析器
具体配置信息如下:
<?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.xp.controller"/> <!-- 让 SpringMVC 处理静态资源 --> <mvc:default-servlet-handler/> <!-- 支持mvc注解驱动 在 Spring 中一般采用 @RequestMapping 注解来完成映射关系 要想使 @RequestMapping 注解生效 必须向上下文中注册 DefaultAnnotationHandlerMapping 和一个 AnnotationMethodHandlerAdapter实例 这两个实例分别在类级别和方法级别处理 而 annotation-driver 配置帮助我们自动完成上述两个实例的注入 --> <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/
目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问 -
配置 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"> <!-- 注册 DispatcherServlet注解 --> <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:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <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
- 映射路径为 / 【不要用/*,会404】
-
创建视图层 hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
-
创建 Controller
编写一个 Java 控制类:HelloController
@Controller @RequestMapping("hello") public class HelloController { // 真实的访问地址:项目名/hello/h1 @RequestMapping("h1") public String hello(Model model){ // 向模型中添加属性msg与值,可以在JSP页面中取出并渲染 model.addAttribute("msg","HelloSpringMVCAnnotation!"); // WEB-INF/jsp/hello.jsp return "hello"; } }
-
@Controller 是为了让 Spring IoC容器初始化时自动扫描到并作为 Spring 中的一个组件
-
@RequestMapping 时为了映射请求路径,这里时因为类与方法上都有映射,所以访问的是项目名/hello/h1
-
方法中声明 Model 类型的参数是为了把 Action 中的数据带到视图中
-
方法返回的结果是视图的名称 hello,加上配置文件中的前后缀变成 WEB-INF/jsp/hello.jsp
-
-
启动 Tomcat 测试
小结
实现步骤其实非常简单:
- 新建一个 web 项目
- 导入相关 jar 包
- 编写 web.xml,注册 DispatcherServlet
- 编写 SpringMVC 配置文件
- 创建对应的控制类 controller
- 最后完善前端视图和 controller 之间的对应
- 测试运行调试
使用 SpringMVC 必须配置的三大件:
处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,这样省去了大段的 xml 配置
4. RESTful 和 Controller
4.1 控制器 Controlle
控制器 Controller
- 控制器负责提供访问应用程序的行为,通常通过接口定义或者注解定义两种实现方式。
- 控制器负责解析用户的请求并将其转黄为一个模型。
- 在 SpringMVC 中一个控制器类可以包含多个方法。
- 在 SpringMVC 中,对于 Controller 的配置方式又很多种
Controller 有两种实现方式
- 实现 Controller 接口
- 使用注解
两种方式的对比:
- 实现接口 Controller 定义控制器是较老的方法
- 实现接口的缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个 Controller;定义的方式比较麻烦
- 使用注解能够减少配置,更简单
将类当作组件交由 Spring 托管的注解
@Component // 普通类注解
@Controller // controller层注解
@Repository // dao层注解
@Service // service层注解
4.2 @RequestMapping
- @RequestMapping 注解用于映射 url 到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@Controller
@RequestMapping("hello")
public class HelloController {
@RequestMapping("h1")
public String hello(Model model){
model.addAttribute("msg","HelloSpringMVCAnnotation!");
return "hello";
}
}
上面代码hello方法的路径是 项目名/hello/h1
4.3 RESTful 风格
概念
RESTful 就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制。
功能
资源:互联网所有的事物都可以被抽象为资源。
资源操作:使用 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
在原来的配置基础上,增加一个 Controller 类
使用 @PathVariable 注解,让方法参数的值对应绑定到一个 URI 模板变量上
@Controller
public class HelloController {
@RequestMapping("/add/{a}/{b}")
public String hello(@PathVariable int a,@PathVariable int b, Model model){
int res = a+b;
model.addAttribute("msg","结果为:"+res);
return "hello";
}
}
浏览器 url 访问及结果
所有地址栏请求默认都是 HTTP GET 类型的。
方法级别的注释变体有如下几个
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
5. 数据处理及跳转
5.1 结果跳转方式
ModelAndView
设置 ModelAndView 对象,根据 view 的名称,和视图解析器跳到指定的页面。
页面:{视图解析器前缀} + viewName +{视图解析器后缀}
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
对应的 Controller 为:
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 创建 ModelAndView 对象
ModelAndView mv = new ModelAndView();
// 封装对象,放在 ModelAndView 中,Model
mv.addObject("msg","HelloSpringMVC!");
// 封装要跳转的视图,放在 ModelAndView 中,相当于 /WEB-INF/jsp/hello.jsp
mv.setViewName("hello");
return mv;
}
}
5.2 ServletAPI
通过设置 ServletAPI,不需要视图解析器
- 通过 HttpServletResponse 进行输出
- 通过 HttpServletResponse 实现重定向
- 通过 HttpServletResponse 实现转发
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throwsException {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
5.3 通过 SpringMVC 来实现转发和重定向
5.3.1 无需视图解析器
测试前,需要将视图解析器注释掉
默认地址是转发地址,加了前缀 foward:/
也是转发,加了 redirect:/
前缀代表则是重定向。
@Controller
public class TestController {
@RequestMapping("test1")
public String test1(Model model){
model.addAttribute("msg","test1");
// 转发
return "/WEB-INF/jsp/test.jsp";
}
@RequestMapping("test2")
public String test2(Model model){
model.addAttribute("msg","test2");
// 转发
return "forward:/WEB-INF/jsp/test.jsp";
}
@RequestMapping("test3")
public String test3(){
// 重定向
return "redirect:/index.jsp";
}
}
重定向时需要注意一个点。重定向是客户端的,转发是服务端内部的。重定向是让客户端去访问重定向的地址。客户端是无权访问 WEB-INF
目录下资源。
若需要重定向到 WEB-INF
下,可以先定向到一个地址,然后由服务器内部去跳转
@RequestMapping("test3")
public String test3(){
// 重定向到转发的url
return "redirect:/toWebInf";
}
@RequestMapping("toWebInf")
public String toWebInf(){
// 通过转发,可以访问WEB-INF目录下的资源
return "/WEB-INF/jsp/test.jsp";
}
5.3.2 有视图解析器:
@Controller
@RequestMapping("/t2")
public class TestController2 {
@RequestMapping("/test1")
public String test1(Model model){
model.addAttribute("msg","test1");
// 转发
return "test";
}
@RequestMapping("/test2")
public String test2(Model model){
model.addAttribute("msg","test2");
// 转发,使用 forward 必须使用全限定名
return "forward:/WEB-INF/jsp/test.jsp";
}
@RequestMapping("/test3")
public String test3(){
// 重定向
return "redirect:/t2/toWebInf";
}
@RequestMapping("/toWebInf")
public String toWebInf(){
// 通过转发访问 WEB-INF 目录下的资源
return "test";
}
}
使用 forward:/
进行转发和使用 redirect:/
重定向 时,后面必须是全限定名
5.4 数据显示到前端
第一种:通过ModelAndView
和一开始的讲原理时的代码一样
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","test");
mv.setViewName("test");
return mv;
}
}
第二种:通过ModelMap
ModelMap
@Controller
@RequestMapping("user")
public class UserController {
// 通过 http://localhost:8080/user/test1?name=xp 传递给后端
@RequestMapping("test1")
public String test1(String name, ModelMap model){
// 接收前端对象
System.out.println("name->"+name);
// 返回数据给前端
model.addAttribute("msg",name);
// 跳转视图
return "test";
}
}
第三种:通过Model
Model
@Controller
@RequestMapping("user")
public class UserController {
// 通过 http://localhost:8080/user/test2?name=xp 传递给后端
@RequestMapping("test2")
public String test1(String name, Model model){
// 接收前端对象
System.out.println("name->"+name);
// 返回数据给前端
model.addAttribute("msg",name);
// 跳转视图
return "test";
}
}
对比
就对于新手而言,简单来说使用区别就是:
- Model 只有寥寥几个方法,只适用于存储数据,简化了新手对于 Model 对象的操作和理解
- ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性
- ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转
当然,以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解
5.5 中文乱码问题
测试环境
-
创建一个 encoding.jsp,用来提交表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>解决乱码问题</title> </head> <body> <form action="${pageContext.request.contextPath}/encoding/test1" method="post"> <input type="text" name="name"/> <input type="submit" value="提交"/> </form> </body> </html>
-
编写控制器 EncodingController 来接收前端提交的表单
@Controller @RequestMapping("encoding") public class EncodingController { @PostMapping("test1") public String test1(String name, Model model){ // 查看获取的前端提交的表单数据是否为乱码,方便定位问题 System.out.println("name->"+name); // 获取前端提交的表单数据 model.addAttribute("msg",name); return "test"; } }
-
提交表单,查看控制台和浏览器页面输出
可以看出,在前端页面提交表单到后台接收前端表单数据的过程中,乱码就已经产生了!
解决办法
在 web.xml 中配置 SpringMVC 自带的编码过滤器,并重启 Tomcat 服务器
<!-- 编码过滤器:解决中文乱码问题 -->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是我们发现,有些极端情况下,这个过滤器对 GET 的支持不好
处理方法:
-
修改 Tomcat 配置文件,设置编码
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
-
自定义过滤器
package com.xp.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; /** * 解决get和post请求 全部乱码的过滤器 */ public class GenericEncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //处理response的字符编码 HttpServletResponse myResponse = (HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8"); // 转型为与协议相关对象 HttpServletRequest httpServletRequest = (HttpServletRequest) request; // 对request包装增强 HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } } //自定义request对象,HttpServletRequest的包装类 class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; //是否编码的标记 private boolean hasEncode; //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰 public MyRequest(HttpServletRequest request) { super(request);// super必须写 this.request = request; } // 对需要增强方法 进行覆盖 @Override public Map getParameterMap() { // 先获得请求方式 String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { // post请求 try { // 处理post乱码 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { // get请求 Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { // 确保get手动编码逻辑只运行一次 for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { // 处理get乱码 values[i] = new String(values[i] .getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true; } return parameterMap; } return super.getParameterMap(); } //取一个值 @Override public String getParameter(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null) { return null; } return values[0]; // 取回参数的第一个值 } //取所有值 @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
6. JSON
引入 Jackson 依赖
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
6.1 什么是JSON?
什么是JSON?
- JSON(JavaScript Object Notation),翻译过来就是 JS 对象标记。是一种轻量级的数据交换格式,目前使用特别广泛。
- 采用完全独立于编程语言的文本格式来存储和表示数据。
- 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在 JavaScript 语言中,一切都是对象。因此,任何 JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。
6.2 JSON的格式
JSON 的格式:
- 对象表示为键值对,数据由逗号分隔。
- 花括号保存对象。
- 方括号保存数组。
JSON 键值对 是用来保存 JavaScript 对象的一种方式,和 JavaScript 对象的写法也大同小异,键值对组合中的键名卸载前面,并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值:
{"name": "zhangsan"}
{"age": "18"}
{"sex": "男"}
很多人搞不清楚 JSON 和 JavaScript 对象的关系,甚至谁是谁都不清楚。其实,可以这么理解:
JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
var obj = {name:"zhangsan",age:18,sex:"男"};
var json = ‘{"name","zhangsan","age":18,"sex":"男"}’;
6.3 JSON 和 JavaScript 对象互转
要实现从 JSON 字符串转换为 JavaScript 对象,使用 JSON.parse()
方法:
var obj = JSON.parse(‘{"name","zhangsan","age":18,"sex":"男"}’);
要实现从 JavaScript 对象转换为 JSON 字符串,使用 JSON.stringify()
方法:
var json = JSON.stringify({name:"zhangsan",age:18,sex:"男"});
测试环境搭建
-
新建一个 module: SpringMVC-05-JSON,并添加web支持
-
在 web 目录下新建一个 json1.html,编写测试内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSON</title> </head> <body> <script type="text/javascript"> //编写一个js的对象 var user = { name:"张三", age:18, sex:"男" }; //将js对象转换成json字符串 var str = JSON.stringify(user); console.log(str); //将json字符串转换为js对象 var user2 = JSON.parse(str); console.log(user2.age,user2.name,user2.sex); </script> </body> </html>
-
在IDEA中使用浏览器打开,查看控制台输出
6.4 Controller返回JSON数据
Jackson: 目前比较好的 JSON 解析工具
当然工具不止这一种,比如还有阿里的 fastjson ,谷歌的 gson 等等
我们这里使用 Jackson,具体操作如下:
-
导入 Jackson 的依赖
<!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency>
-
配置 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"> <!-- 注册DispatcherServlet --> <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:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
-
创建 springmvc-servlet.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" 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"> <!-- 开启注解支持 --> <context:component-scan base-package="com.xp.controller"/> <!-- 静态资源过滤 --> <mvc:default-servlet-handler/> <!-- 开启mvc注解驱动支持 --> <mvc:annotation-driven/> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀 --> <property name="prefix" value="/WEB-INF/jsp"/> <!-- 后最 --> <property name="suffix" value=".jsp"/> </bean> </beans>
-
编写实体类 User
@Data @NoArgsConstructor @AllArgsConstructor public class User { public String name; public String age; public String sex; }
-
编写控制器 JsonController
@Controller public class JsonController { @RequestMapping(path = "/json1", produces = "application/json;charset=utf-8") @ResponseBody // 添加ResponseBody注释,它就不会走视图解析器,会直接返回一个字符串 public String json1() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); User user = new User("zhangsan", "18", "男"); return objectMapper.writeValueAsString(user); } }
@ResponseBody :该注解可以添加在方法或类上,添加该注解后,该类或该方法就不会走视图解析器,会直接返回一个字符串。
在前后端分离开发中,一般都使用 @ResponseBody 注解,十分方便。
如果在 @RequestMapping 注解中不配置 produces 属性的话,就会产生中文乱码。
当然,还有一种解决乱码一劳永逸的方法
在 springmvc-servlet.xml 配置文件中增加如下配置
<!-- 开启mvc注解驱动支持 --> <mvc:annotation-driven> <!-- 解决jackson乱码问题 --> <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>
使用这种方法解决乱码问题后,就不需要在 @RequestMapping 注解中配置 produces 属性
输出集合
@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
User user1 = new User("张三1", "18", "男");
User user2 = new User("张三2", "18", "男");
User user3 = new User("张三3", "18", "男");
User user4 = new User("张三4", "18", "男");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
String str = objectMapper.writeValueAsString(users);
return str;
}
输出时间对象
@RequestMapping("/json4")
public String json4() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sdf.format(date);
return objectMapper.writeValueAsString(format);
}
6.5 fastjson
fastjson.jar 是阿里开发的一款专门用于 Java 开发的包。可以方便的实现 json 对象与 JavaBean 对象的转换,实现 JavaBean 对象与 json 字符串的转换,实现 json 对象与 json 字符串的转换。
导入 fastjson 依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
</dependencies>
fastjson 三个主要的类:
JSONObject 代表 json 对象
- JSONObject 实现了 Map 接口
- JSONObject 对应 json 对象,通过各种形式的 get() 方法可以获取 json 对象中的数据,也可以利用诸如 size(),isEmpty() 等方法获取键值对的个数和判断是否未空。其本质是通过实现 Map 接口并调用接口中的方法完成的。
JSONArray 代表 json 对象数组
- 内部是有 List 接口中的方法来完成操作
JSON 代表 JSONObject 和 JSONArray 的转化
- JSON 类源码分析与使用
- 仔细观察这些方法,主要是实现 json 对象,json 对象数组,javabean 对象,json 字符串之间的相互转化
代码测试
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);
}
}
7. AJAX
7.1 简介
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
- AJAX 时一种无需重新加载整个网页的情况下,能够更新部分网页的技术。
- AJAX 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术。
- 在 2005 年,Google 通过其 Google Suggest 使得 AJAX 变得流行起来,Google Suggest 能够自动帮你完成搜索单词。
- Google Suggest 使用 AJAX 创造出动态性极强的 web 界面;当你在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
- 就和国内百度的搜索框一样。
- 传统的网页(不用 AJAX 技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。而网页的很多内容是并不需要改变的。
- 使用 AJAX 技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新整个网页。
- 使用 AJAX ,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的 Web 用户界面。
使用AJAX可以做:
- 注册时,输入用户名自动检测用户是否已经存在。
- 登陆时,提示用户名密码错误
- 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面 DOM 中将数据行也删除
- 等等。。
7.2 iframe标签
iframe,可以算是伪造的 AJAX,也可以实现页面局部刷新。虽然,iframe 标签的使用比 AJAX 要简单得多,但其安全性以及用户体验性并不如人意。
下面,我们来使用 iframe 标签实现页面的局部刷新
-
创建一个新的工程项目:SpringMVC-06-AJAX,添加 web 框架
-
在 index.jsp 中编写 iframe 标签
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>iframeTest</title> <script> // iframe 加载url function loadUrl(url) { var iframe = document.getElementById("iframe"); iframe.src = url; } </script> </head> <body> <%-- 点击按钮后,iframe 加载页面 --%> <div> <button id="iframeBtn" onclick="loadUrl('iframe.jsp')">点我加载 iframe.jsp</button> </div> <iframe src="" id="iframe"></iframe> </body> </html>
-
在 web 目录下创建 iframe.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>这是 iframe.jsp</h1> </body> </html>
-
测试是否页面局部加载成功
如果我们让
loadUrl()
的参数是百度网址时,也可以跳转到百度的页面<div> <button id="iframeBtn" onclick="loadUrl('http://www.baidu.com')">点我加载 iframe.jsp</button> </div>
并且我们按 f12 打开开发者工具的时候,会发现页面中,只有 iframe 内部的 #document 在改变,而整个页面并没有刷新。所以 iframe 标签是实现页面局部刷新的,可以看成是一个伪 AJAX
7.2 原生AJAX
XMLHttpRequest 是 AJAX 的基础。
所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)
XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以不重新加载整个网页的情况下,对网页的某部分进行更新
7.3 jQuery AJAX
jQuery AJAX 简介
- jQuery 是一个优秀的js框架,对 js 原生的 Ajax 进行了封装。
- 在封装后的 AJAX 的操作更加简洁,功能更加强大
- 使用 jQuery AJAX ,避免重复造轮子
Ajax 的核心是 XMLHttpRequest 对象(XHR),XHR 为向服务器发送请求和解析服务器响应提供了接口,能够以异步方式从服务器获取新数据。
jQuery 提供多个与 AJAX 有关的方法。
通过 jQuery AJAX 方法,我们能够使用 HTTP GET 和 HTTP POST 从远程服务器上请求文本、HTML、XML 或 JSON。同时我们能够把这些外部数据直接载入网页的被选元素中。
jQuery Ajax本质就是 XMLHttpRequest,对他进行了封装,方便调用!
jQuery.ajax(...)
部分参数:
url:请求地址
type:请求方式,GET、POST(1.9.0之后用method)
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒)
beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局)
accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
"xml": 将服务器端返回的内容转换成xml格式
"text": 将服务器端返回的内容转换成普通文本格式
"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
"json": 将服务器端返回的内容转换成相应的JavaScript对象
"jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
SpringMVC使用AJAX
-
配置 springmvc-servlet.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" 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"> <!-- 扫描包,开启注解支持 --> <context:component-scan base-package="com.xp.controller"/> <!-- 静态资源过滤 --> <mvc:default-servlet-handler/> <!-- 开启注解驱动支持 --> <mvc:annotation-driven/> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀 --> <property name="prefix" value="/WEB=INF/html/"/> <!-- 后缀 --> <property name="suffix" value=".html"/> </bean> </beans>
-
配置 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"> <!-- 注册 DispatcherServlet --> <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-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 编码过滤器:解决中文乱码问题 --> <filter> <filter-name>encodingFilter</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>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
-
编写控制器 AJAXController,假设数据库中账号和密码分别是 admin 和 12345
package com.xp.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class AJAXController { // produces 解决乱码问题 @RequestMapping(path = "/ajax/userName" ,produces="text/html;charset=UTF-8;") @ResponseBody public String ajaxUserName(String userName){ return ("admin".equals(userName))?"正确":"错误"; } @RequestMapping(path = "/ajax/password" ,produces="text/html;charset=UTF-8;") @ResponseBody public String ajaxPassword(String password){ return ("12345".equals(password))?"正确":"错误"; } }
-
编写前端页面和 AJAX 请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX</title> <!-- 引入 jQuery --> <script src="../js/jquery-3.4.1.min.js"></script> <script type="text/javascript"> function userNameBlur() { $.post({ url: "/ajax/userName", data: "userName="+$("#userName").val(), dataType: "text", success: function (data) { var userNameAjax = $("#userNameAjax"); if ("正确" === data){ userNameAjax.css("color","green"); }else { userNameAjax.css("color","red"); } userNameAjax.html(data); } }) } function passwordBlur() { $.post({ url: "/ajax/password", data: "password="+$("#password").val(), dataType: "text", success: function (data) { var passwordAjax = $("#passwordAjax"); if ("正确" === data){ passwordAjax.css("color","green"); }else { passwordAjax.css("color","red"); } passwordAjax.html(data); } }); } </script> </head> <body> <div id="test"> <label>用户名:<input type="text" id="userName" onblur="userNameBlur()"/></label> <span id="userNameAjax"></span> </div> <div> <label>密码:<input type="text" id="password" onblur="passwordBlur()"/></label> <span id="passwordAjax"></span> </div> </body> </html>
-
启动 Tomcat 服务器,测试
测试时,可以发现,当我们鼠标点击输入框后再点击其它地方失去焦点时,右边的span标签内的内容会发生变化。这样,我们就学会了 SpringMVC 中使用 AJAX。
8. Thymeleaf模板引擎
8.1 简介
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎,类似 JSP, Velocity, FreeMaker 等。它可以轻易与 Spring MVC 等 Web 框架进行集成作为 Web 应用的模板引擎。是 SpringBoot 官方使用的模板引擎。
8.2 特点
- 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行。
- 开箱即用:它提供标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、OGNL 表达式效果。
- 多方言支持:Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模板,可以快速地实现表单绑定、属性编辑器、国际化等功能。
- 与 SpringBoot 完美整合,SpringBoot 提供了 Thymeleaf 的默认配置,并且为 Thymeleaf 设置了视图解析器,可以像以前操作 jsp 一样来操作 Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。
8.3 简单使用
-
新建一个module:Springmvc-07-Thymeleaf,添加 web 支持
-
配置 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"> <!-- 注册 DispatcherServlet --> <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:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <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>
-
导入 Themeleaf 依赖
<dependencies> <!-- Thymeleaf 支持Spring方言 --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.11.RELEASE</version> </dependency> <!-- Thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> </dependencies>
-
配置springmvc-servlet.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" 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"> <context:component-scan base-package="com.xp.controller"/> <mvc:default-servlet-handler/> <mvc:annotation-driven/> <!-- 模板解析器 --> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver" id="templateResolver"> <property name="prefix" value="/WEB-INF/html"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML"/> <property name="characterEncoding" value="utf-8"/> <property name="cacheable" value="false"/> </bean> <!-- 模板引擎 --> <bean class="org.thymeleaf.spring5.SpringTemplateEngine" id="templateEngine"> <property name="templateResolver" ref="templateResolver"/> <property name="enableSpringELCompiler" value="true"/> </bean> <!-- 视图解析器 --> <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine"/> <property name="characterEncoding" value="utf-8"/> </bean> </beans>
-
编写控制器
@Controller public class ThymeleafController { @RequestMapping("/thymeleaf") public String thymeleaf(Model model){ model.addAttribute("thymeleaf","Hello,Thymeleaf!"); return "thymeleaf"; } }
-
编写视图层
注:要使用 Thymeleaf 模板引擎的页面,必须要在 html 标签上加上
xmlns:th="http://www.thymeleaf.org"
index.xml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Thymeleaf</title> </head> <body> <h1><a href="/thymeleaf">Hello,Thymeleaf!</a></h1> </body> </html>
thymeleaf.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--/*@thymesVar id="thymeleaf" type="java.lang.String"*/--> <div th:text="${thymeleaf}"></div> </body> </html>
-
测试
我们可以发现 SpringMVC 整合 Thymeleaf 模板引擎,只是导入依赖后,在 springmvc-servlet.xml 配置文件中加入了模板解析器、模板引擎和视图解析器三个 bean,并不需要修改 web.xml 中的配置。我们就可以在 html 中像 jsp 一样使用 el 表达式了。下面,我们来看下常用的 Thymeleaf 语法以及标签。
8.4 常用语法
Thymeleaf 的主要作用是把 model 中的数据渲染到html 中,因此其语法主要是如何解析 model 中的数据。从以下方面来学习:
- 变量、方法、条件判断、循环、运算【逻辑运算、布尔运算、比较运算、条件运算】
- 其它
-
Thymeleaf 通过
${...}
来获取 model 中的变量,语法和 el 表达式差不多,但它是 ognl 表达式。<!--/*@thymesVar id="thymeleaf" type="java.lang.String"*/--> <div th:text="${thymeleaf}"></div>
-
Themeleaf 通过
th:object
自定义变量,可以通过*{...}
取出对应的属性<!--/*@thymesVar id="user" type="com.xp.entity.User"*/--> <div th:object="${user}"> <h2 th:text="*{name}"></h2> <h2 th:text="*{age}"></h2> <!--/*@thymesVar id="friend" type="com.xp.entity.Friend"*/--> <h2 th:text="*{friend.name}"></h2> </div>
-
ognl 表达式本身就支持方法调用,但需要注意的是必须使用注释指明该变量是哪个类的
<!--/*@thymesVar id="user" type="com.xp.entity.User"*/--> <!--/*@thymesVar id="name" type="java.lang.String"*/--> <!--/*@thymesVar id="age" type="java.lang.Integer"*/--> <div th:object="${user}"> <h2 th:text="*{name.hashCode()}"></h2> <h2 th:text="*{age.hashCode()}"></h2> <!--/*@thymesVar id="friend" type="com.xp.entity.Friend"*/--> <h2 th:text="*{friend.name.hashCode()}"></h2> </div>
Thymeleaf 中提供了一些内置对象,并且这些对象中提供了一些方法,方便我们调用、获取这些对象,需要使用
#对象名
来调用-
一些环境相关的对象
对象 作用 #ctx 获取 Thymeleaf 自己的 Context 对象 #request 如果是 web 程序,可以获取 HttpServletRequest 对象 #respone 如果是 web 程序,可以获取 HttpServletResponse 对象 #session 如果是 web 程序,可以获取 HttpSession 对象 #servletContext 如果是web 程序,可以获取 HttpServletContext 对象 -
Thymeleaf 提供的全局对象
对象 作用 #datas 处理 java.util.date 的工具对象 #calendars 处理 java.util.calendar 的工具对象 #numbers 用来对数字格式的方法 #strings 用来处理字符串的方法 #bools 用来判断布尔值的方法 #arrays 用来护理数组的方法 #lists 用来处理 List 集合的方法 #sets 用来处理 Set 集合的方法 #maps 用来处理 Map 集合的方法 例如:
<div th:text="${#dates.format(data,'yyyy-MM-dd HH:mm:ss')}"></div>
<div th:Object="${#session.getAttribute('user')}"> <h1 th:text="*{name}"></h1> <h1 th:text="*{age}"></h1> <h1 th:text="*{friend.name}"></h1> </div>
-
-
字面值
-
字符串字面值:使用一对 '' (单引号)引用的内容就是字符串的字面值了
<div th:text="'字符串字面值'"></div>
-
数字字面值:不需要任何特殊语法,写的是是什么就是什么,可以进行算术运算
<div th:text="2020"></div> <div th:text="2018+2"></div>
-
布尔字面值:只有 true 或 false
<div th:if="true">布尔值:true</div>
-
-
字符串拼接
-
我们经常使用得普通字符串拼接方法
<div th:text="'欢迎 '+${user.name}+‘ !’"></div>
-
Thymeleaf 使用一对 | 拼接
<div th:text="|欢迎 +${user.name} !|"></div>
-
-
运算
-
算术运算
支持的运算符: + - * / %
<div th:text="${user.age}%2"></div>
-
比较运算运算
支持的比较运算: >,<,>=,<=,但是 >,< 不能直接使用,因为 html 会解析为标签,要使用别名
注意 == 和 != 不仅可以比较数值,类似于 equals 的功能
可以使用的别名:gt(>), lt(<), ge(>=) , le(<=), not(!), eq(==), neq/ne(!=)
-
条件运算
-
三元运算
<div th:text="${user.isAdmin}?'管理员':'普通会员'"></div>
-
默认值
有的时候,我们取一个值可能为空,这个时候需要做非空判断,可以使用表达式 ?: 默认值简写
<span th:text="${user.name} ?: '二狗'"></span>
-
-
-
Thymeleaf 通过
th:each
实现循环<div th:each="list:${lists}"> <h1 th:text="${list}"></h1> </div>
遍历的结合可以是以下类型
- Iterable,实现了Iterable接口的类
- Enumeration,枚举
- Interator,迭代器
- Map,遍历得到的是Map.Entry
- Array,数组及其它一切符合数组结果的对象
-
Thymeleaf 使用
th:if
或者if:unless
来进行逻辑判断<div th:if="${user.age} >= 18"> <h1>成年人</h1> </div>
如果表达式的值为 true,则标签会渲染到页面,否则不进行渲染。
以下情况会被认为 true
- 表达式值为 true
- 表达式值为非0数值
- 表达式值为非0字符
- 表达式值为字符串,但不是“false”、“no”,“off”
- 表达式不是布尔、字符串、数字、字符中的任何一种
其它情况包括 null 都被认定为 false
-
Thymeleaf 使用
th:switch
和th:case
来进行分支控制<div th:switch="${user.role}"> <p th:case="'admin'">用户是管理员</p> <p th:case="'manager'">用户是经理</p> <p th:case="*">用户是别的玩意</p> </div>
需要注意的是,一旦有一个
th:case
成立,其它的则不再判断。与 java 中的 switch 是一样的另外
th:case="*"
表示默认,放在最后 -
Thymeleaf 使用
th:inline="javascript"
来声明该 script 标签的脚本是需要特殊处理的 js 脚本<script th:inline="javascript"> var user = /*[[${user}]]*/ {}; var age = /*[[${user.age}]]*/ 20; console.log(user); console.log(age) </script>
var user = /*[[Thymeleaf表达式]]*/
因为 Thymeleaf 被注释起来,因此即便是静态环境下,js 代码也不会报错,而是采用表达式后面跟着的默认值。且 User 对象会直接处理为 json 格式
9. 拦截器
9.1概述
SpringMVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现待定的功能。
过滤器与拦截器的区别:拦截器时 AOP 思想的具体应用。
过滤器
- Servlet 规范中的一部分,任何 JavaWeb 工程都可以使用
- 再 url-pattern 中配置了 /* 之后,可以对所有要访问的资源进行拦截
拦截器
- 拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能使用
- 拦截器只会拦截访问的控制器方法,如果访问得事 jsp/html/css/image/js 是不会进行拦截的
9.2 自定义拦截器
那如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
-
新建一个 Moudule:Spring-08-Interceptor ,添加 web 支持
-
配置 springmvc-servlet.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" 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"> <context:component-scan base-package="com.xp.controller"/> <mvc:default-servlet-handler/> <mvc:annotation-driven/> <!-- 拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.xp.config.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors> <!-- 模板解析器 --> <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/html/"/> <property name="suffix" value=".html"/> <property name="cacheable" value="false"/> <property name="characterEncoding" value="utf-8"/> <property name="templateMode" value="HTML"/> </bean> <!-- 模板引擎 --> <bean class="org.thymeleaf.spring5.SpringTemplateEngine" id="templateEngine"> <property name="templateResolver" ref="templateResolver"/> <property name="enableSpringELCompiler" value="true"/> </bean> <!-- 视图解析器 --> <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine"/> <property name="characterEncoding" value="utf-8"/> </bean> </beans>
-
配置 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"> <!-- 注册DispatcherServlet --> <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:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <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>
-
编写实体类 User
@Data @AllArgsConstructor @NoArgsConstructor public class User { private String account; private String password; }
-
创建控制器 InterceptorController
@Controller @RequestMapping("/user") public class InperceptorController { @RequestMapping("/toMain") public String toMain(){ return "main"; } @RequestMapping("/toLogin") public String toLogin(){ return "login"; } @RequestMapping("/login") public String login(User user, HttpSession session){ // 模拟从数据库中查询数据后判断账号密码是否正确,正确则设置session,否则返回登录页面 if ("admin".equals(user.getAccount()) && "12345".equals(user.getPassword())){ session.setAttribute("user",user); return "main"; }else { return toLogin(); } } @RequestMapping("/logout") public String logout(HttpSession session){ session.removeAttribute("user"); return toLogin(); } }
-
编写视图层
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2><a href="/user/toMain">首页</a></h2> <h2><a href="/user/toLogin">登录</a></h2> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="/user/login"> <label>用户名:<input type="text" name="account"/></label> <label>密码:<input type="text" name="password"/></label> <input type="submit" value="登录"/> </form> </body> </html>
main.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首页</h1> <!-- 有session则显示该div --> <div th:object="${#session.getAttribute('user')}" th:if="Object"> <span th:text="*{account}"></span> <a href="/user/logout">退出</a> </div> </body> </html>
-
创建拦截器 MyInterceptor ,实现 HandlerInterceptor 接口,并重写 preHandle() 方法
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String url = request.getRequestURI(); System.out.println(url); // 导航页放行 if ("/".equals(url)){ return true; } // 导航页放行 if (url.contains("index")){ return true; } // 登录请求放行 if (url.contains("login")){ return true; } // 跳转登录页面请求放行 if (url.contains("toLogin")){ return true; } // 登录后有session的放行 if (request.getSession().getAttribute("user") != null){ return true; } // 拦截后跳转到登录页面 response.sendRedirect("/user/toLogin"); // 拦截 return false; } }
-
启动 Tomcat 测试
测试没登录前能不能进入首页、测试登录后从导航页是否能进入首页、测试登录后退出后能否再进入首页。
测试成功则代表拦截器配置成功
10.文件的上传下载
10.1 准备工作
文件上传是项目开发中最常见的功能之一 ,SpringMVC 可以很好的支持文件上传,但是 SpringMVC 上下文中默认没有装配 MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置 MultipartResolver。
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。
对表单中的 enctype 属性做个详细的说明:
- application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为 "+" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>
一旦设置了 enctype 为 multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation 发布了开源的 Commons FileUpload 组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
- Servlet3.0 规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
- 而Spring MVC 则提供了更简单的封装。
- Spring MVC 为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC 使用 Apache Commons FileUpload 技术实现了一个 MultipartResolver 实现类:
- CommonsMultipartResolver。因此,SpringMVC 的文件上传还需要依赖 Apache Commons FileUpload 的组件。
10.2 文件上传
-
导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;
<!--文件上传--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> <!--servlet-api导入高版本的--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
-
配置bean:multipartResolver
【注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!】
<!--文件上传配置--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --> <property name="defaultEncoding" value="utf-8"/> <!-- 上传文件大小上限,单位为字节(10485760=10M) --> <property name="maxUploadSize" value="10485760"/> <property name="maxInMemorySize" value="40960"/> </bean>
CommonsMultipartFile 的 常用方法:
- String getOriginalFilename():获取上传文件的原名
- InputStream getInputStream():获取文件流
- void transferTo(File dest):将上传文件保存到一个目录文件中
我们去实际测试一下
-
编写前端页面
<form action="/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"> </form>
-
编写控制器
@Controller public class FileController { //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象 //批量上传CommonsMultipartFile则为数组即可 @RequestMapping("/upload") public String fileUpload(@RequestParam("file") CommonsMultipartFile file ,HttpServletRequest request) throws IOException { //获取文件名 : file.getOriginalFilename(); String uploadFileName = file.getOriginalFilename(); //如果文件名为空,直接回到首页! if ("".equals(uploadFileName)){ return "redirect:/index.jsp"; } System.out.println("上传文件名 : "+uploadFileName); //上传路径保存设置 String path = request.getServletContext().getRealPath("/upload"); //如果路径不存在,创建一个 File realPath = new File(path); if (!realPath.exists()){ realPath.mkdir(); } System.out.println("上传文件保存地址:"+realPath); InputStream is = file.getInputStream(); //文件输入流 OutputStream os = new FileOutputStream(new File(realPath,uploadFileName));//文件输出流 //读取写出 int len=0; byte[] buffer = new byte[1024]; while ((len=is.read(buffer))!=-1){ os.write(buffer,0,len); os.flush(); } os.close(); is.close(); return "redirect:/index.jsp"; } }
-
测试上传文件,OK!
采用file.Transto 来保存上传的文件
-
编写Controller
@RequestMapping("/upload2") public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException { //上传路径保存设置 String path = request.getServletContext().getRealPath("/upload"); File realPath = new File(path); if (!realPath.exists()){ realPath.mkdir(); } //上传文件地址 System.out.println("上传文件保存地址:"+realPath); //通过CommonsMultipartFile的方法直接写文件(注意这个时候) file.transferTo(new File(realPath +"/"+ file.getOriginalFilename())); return "redirect:/index.jsp"; }
-
前端表单提交地址修改
-
访问提交测试,OK!
10.3 文件下载
文件下载步骤:
-
设置 response 响应头
-
读取文件 -- InputStream
-
写出文件 -- OutputStream
-
执行操作
-
关闭流 (先开后关)
代码实现
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request)throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "基础语法.jpg";
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path,fileName);
//2、 读取文件--输入流
InputStream input=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
前端
<a href="/download">点击下载</a>
测试,文件下载OK
11. SSM框架整合
环境配置
-
创建数据库并创建对应的表,并插入数据
# 创建数据库 ssmproject CREATE DATABASE IF NOT EXISTS ssmproject; # 创建表 DROP TABLE IF EXISTS book; CREATE TABLE IF NOT EXISTS book( book_id INT(10) PRIMARY KEY auto_increment COMMENT '书id', book_name VARCHAR(100) NOT NULL COMMENT '书名', book_counts INT(11) NOT NULL COMMENT '数量', detail VARCHAR(200) COMMENT '描述' ); # 插入数据 INSERT INTO book (book_name,book_counts,detail) VALUES ('Java',1,'从入门到放弃'), ('MySQL',10,'从删库到排路'), ('Linux',5,'从入门到进牢');
-
在 pom.xml 中导入依赖并配置静态资源过滤
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xp</groupId> <artifactId>SSMProject</artifactId> <version>1.0-SNAPSHOT</version> <!-- 依赖 --> <dependencies> <!-- junit 测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- SpringMVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- 织入包 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> <!-- servlet支持 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- jackson 解析json --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> <!-- spring-jdbc JDBC驱动支持 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!-- c3p0 数据库连接池 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency> <!-- mybatis-Spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <!-- thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>LATEST</version> </dependency> <!-- Spring 整合 thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>LATEST</version> </dependency> <!-- log4j 日志 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>2.12.1</version> </dependency> </dependencies> <!-- 静态资源过滤问题 --> <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> </project>
-
添加 web 支持,并把包目录结构先建立起来
-
编写实体类 Book
/** * 书 实体类 * @author xp */ @Data @AllArgsConstructor @NoArgsConstructor public class Book { /** * 书id */ private Integer bookId; /** * 书名 */ private String bookName; /** * 书数量 */ private Integer bookCounts; /** * 描述 */ private String detail; }
-
编写 mapper 接口
/** * BookMapper * * @author xp */ public interface BookMapper { /** * 增加一本书 * * @param book 待增加的书籍信息 * @return 影响行数 */ int addBook(Book book); /** * 根据id删除一本数 * * @param id 被删除的书的id * @return 影响行数 */ int deleteBookById(@Param("bookId") int id); /** * 修改一本数 * * @param book 修改后的书籍信息 * @return 影响行数 */ int updateBook(Book book); /** * 根据id查询书 * * @param id 书的id * @return 查询后的结果 */ Book queryBookById(@Param("bookId") int id); /** * 查询所有书 * * @return 存储书籍信息的集合 */ List<Book> queryAllBook(); /** * 根据书籍名字查询书籍 * * @param name 书籍名字 * @return 存储书籍信息的集合 */ List<Book> queryBookByName(@Param("bookName") String name); }
-
编写 Service 接口和其实现类
Service 接口
/** * 书本业务层 * * @author xp */ public interface BookService { /** * 增加一本书 * * @param book 待增加的书籍信息 * @return 影响行数 */ int addBook(Book book); /** * 根据id删除一本数 * * @param id 被删除的书的id * @return 影响行数 */ int deleteBookById(int id); /** * 修改一本数 * * @param book 修改后的书籍信息 * @return 影响行数 */ int updateBook(Book book); /** * 根据id查询书 * * @param id 书的id * @return 查询后的结果 */ Book queryBookById(int id); /** * 查询所有书 * * @return 存储书籍信息的集合 */ List<Book> queryAllBook(); /** * 根据书籍名字查询书籍 * * @param name 书籍名字 * @return 存储书籍信息的集合 */ List<Book> queryBookByName(String name); }
Service 实现类
/** * Service 实现类 * @author xp */ public class BookServiceImpl implements BookService { private BookMapper bookMapper; public void setBookMapper(BookMapper bookMapper) { this.bookMapper = bookMapper; } @Override public int addBook(Book book) { return bookMapper.addBook(book); } @Override public int deleteBookById(int id) { return bookMapper.deleteBookById(id); } @Override public int updateBook(Book book) { return bookMapper.updateBook(book); } @Override public Book queryBookById(int id) { return bookMapper.queryBookById(id); } @Override public List<Book> queryAllBook() { return bookMapper.queryAllBook(); } @Override public List<Book> queryBookByName(String name) { return bookMapper.queryBookByName(name); } }
-
创建数据库文件 db.properties
jdbc.drive=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmproject?useTimezone=true&serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=UTF-8&useSSL=false jdbc.username=root jdbc.password=root
-
编写 MyBatis 核心配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 开启驼峰映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 开启log4j日志 --> <setting name="logImpl" value="LOG4J"/> </settings> <!-- 别名 --> <typeAliases> <package name="com.xp"/> </typeAliases> <!-- 注册 mapper --> <mappers> <mapper resource="mapper/bookMapper.xml"/> </mappers> </configuration>
-
配置 log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=【%c】-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/xp.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
创建 spring-dao.xml,配置dao层,并交由 Spring 托管
<?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"
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">
<!-- 关联数据库文件 -->
<context:property-placeholder location="classpath:properties/db.properties"/>
<!-- 连接池
dbcp:半自动化操作,不能自动连接
c3p0:自动化操作(自动化地加载配置文件,并且可以自动设置到对象中)
druid、hikari
-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="driverClass" value="${jdbc.drive}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- SqlSessionFactory -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:config/mybatis-config.xml"/>
</bean>
<!-- 配置dao接口扫描包,动态实现了Dao接口可以注入Spring容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 要扫描的包 -->
<property name="basePackage" value="com.xp.mapper"/>
<!-- 注入SqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
-
在 mapper 包下创建 bookMapper.xml,编写映射器映射的sql语句
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xp.mapper.BookMapper"> <insert id="addBook" parameterType="book"> insert into book (book_name, book_counts, detail) values (#{bookName},#{bookCounts},#{detail}); </insert> <delete id="deleteBookById" parameterType="_int"> delete from book where book_id = #{bookId}; </delete> <update id="updateBook" parameterType="book"> update book set book_name = #{bookName},book_counts=#{bookCounts},detail=#{detail} where book_id=#{bookId}; </update> <select id="queryBookById" parameterType="_int"> select * from book where book_id = #{bookId}; </select> <select id="queryAllBook"> select * from book; </select> <select id="queryBookByName" parameterType="String" resultType="book"> select * from book where book_name = #{bookName}; </select> </mapper>
-
编写 spring-service.xml,将 Service 层注入到 Spring 容器中,并配置相对应的事务
<?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" 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"> <!-- 扫描service包下的类 --> <context:component-scan base-package="com.xp.service"/> <!-- 将我们所有的业务类,注入到Spring,可以通过配置或注解实现,这里使用配置实现 --> <bean id="bookService" class="com.xp.service.impl.BookServiceImpl"> <property name="bookMapper" ref="bookMapper"/> </bean> <!-- 配置声明式事务 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- AOP 事务支持 --> </beans>
-
创建 springmvc-servlet.xml ,将 Controller 层注入到 Spring 中
<?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"> <!-- 扫描controller下的包 --> <context:component-scan base-package="com.xp.controller"/> <!-- 静态资源处理 --> <mvc:default-servlet-handler/> <!-- 注解驱动支持 --> <mvc:annotation-driven> <!-- 解决Jackson乱码问题 --> <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> <!-- 模板解析器 --> <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/html/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML5" /> <property name="cacheable" value="false" /> <property name="characterEncoding" value="UTF-8"/> </bean> <!-- 模板引擎 --> <bean class="org.thymeleaf.spring5.SpringTemplateEngine" id="templateEngine"> <property name="templateResolver" ref="templateResolver"/> <property name="enableSpringELCompiler" value="true"/> </bean> <!-- 视图解析器 --> <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="characterEncoding" value="utf-8"/> <property name="templateEngine" ref="templateEngine"/> </bean> </beans>
-
创建 applicationContext.xml ,使用 import 标签将刚刚配置的文件导入到这个主配置中
<?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"> <!-- 整合配置文件 --> <import resource="springmvc-servlet.xml"/> <import resource="spring-dao.xml"/> <import resource="spring-service.xml"/> </beans>
-
配置 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"> <!-- 配置 DispatcherServlet --> <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:config/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <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>
-
编写控制器 BookController
@Controller public class BookController { private static final String TO_ALL_BOOK = "/toAllBook"; // Controller 层调用 Service 层 @Autowired private BookService bookService; public void setBookService(BookService bookService) { this.bookService = bookService; } @RequestMapping(TO_ALL_BOOK) public String toAllBook() { return "allBook"; } @RequestMapping("/allBook") @ResponseBody public String allBook() throws JsonProcessingException { List<Book> books = bookService.queryAllBook(); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(books); } }
-
编写主页 index.html
<!DOCTYPE html> <html lang="en"> <head> <title>$Title$</title> <style> a{ text-decoration: none; color: black; font-size: 38px; } h3{ width: 300px; height: 80px; margin: 200px auto; text-align: center; line-height: 80px; background-color: #7afdf1; border-radius: 5px; } </style> </head> <body> <h3><a href="${pageContext.request.contextPath}/book/toAllBook">进入书籍页面</a></h3> </body> </html>
到这里,我们的三层架构就已经搭建完成了。
启动Tomcat,简单测试一下配置是否有误。
配置无误后,就开始编写前端页面和控制器。
-
allBook.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>书籍展示</title> <script src="../../static/js/jquery-3.4.1.min.js"></script> <script src="../../static/js/bootstrap.min.js"></script> <link type="text/css" rel="stylesheet" href="../../static/css/bootstrap.min.css"/> <script type="text/javascript"> // init 初始化页面,加载后端的数据 $.get({ url: "/book/allBook", dataType: "json", success: function (data) { booksJsonToString(data); } }); </script> </head> <body> <div class="container"> <!-- 标题 --> <div class="row clearfix"> <div class="col-md-12 column"> <div class="page-header"> <h1> <small>书籍展示</small> </h1> </div> </div> </div> <div class="row clearfix"> <!-- 增加按钮 --> <div class="col-md-4 column"> <button class="btn btn-primary" onclick="clickToHref('/book/toAddBook')">增加</button> <!-- 显示所有书籍按钮 --> <button class="btn btn-primary" onclick="clickToHref('/book/toAllBook')">显示所有书籍</button> </div> <!-- 搜索框 --> <div class="col-md-8 column" > <form class="form-inline" id="searchBookForm" style="float: right"> <span id="searchError" style="color:red"></span> <input type="text" class="form-control" name="name" placeholder="请输入查询的书籍名称"/> <input id="searchBtn" class="btn btn-secondary" type="button" value="搜索" onclick="searchBtnClick()"/> </form> </div> </div> <!-- 表格 --> <div class="row clearfix"> <div class="col-md-12 column"> <table class="table table-hover table-striped table-responsive-sm"> <thead> <tr> <td>书籍编号</td> <td>书籍名称</td> <td>书籍数量</td> <td>书籍描述</td> <td>操作</td> </tr> </thead> <tbody id="tbody"> </tbody> </table> </div> </div> </div> <script> // 点击后跳转链接 function clickToHref(href) { window.location.href=href; } function deleteClick(id) { var msg = "确认删除吗?"; if (confirm(msg) === true){ clickToHref('/book/deleteBook?id='+id) } else { return false; } } // 更新按钮点击跳转 function updateClick(href,id) { clickToHref(href+"?id="+id); } // 搜索按钮点击跳转 function searchBtnClick() { $.get({ url: "/book/searchBook", data: $("#searchBookForm").serialize(), dataType: "json", success: function (data) { var json = JSON.stringify(data); var searchError = $("#searchError"); searchError.empty(); // 判断数据是否为空 if (json==="[]"){ searchError.html("搜索不到该书籍"); refreshTBody(""); }else { booksJsonToString(data); } } }) } </script> <script> // 将book集合的json格式转换成字符串 function booksJsonToString(data) { var html = ''; for (var i=0;i<data.length;i++){ html += bookJsonToString(data[i]); } refreshTBody(html); } // 将book的json格式转换成字符串 function bookJsonToString(book) { var bookId = book.bookId; var html = ""; html += "<tr><td>" + bookId + "</td><td>" + book.bookName + "</td><td>" + book.bookCounts + "</td><td>" + book.detail + "</td><td><button class='btn btn-sm btn-info' onclick=" + "updateClick('/book/toUpdateBook'," + bookId + ")" + " >修改</button>" + " <button class='btn btn-danger btn-sm' onclick=deleteClick(" + bookId + ")" + " >删除</button></td></tr>"; return html; } // 刷新表格中的内容 function refreshTBody(html) { var tbody = $("#tbody"); tbody.empty(); tbody.html(html); } </script> </body> </html>
-
addBook.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>增加书籍</title> <script src="../../static/js/jquery-3.4.1.min.js"></script> <script src="../../static/js/bootstrap.min.js"></script> <link type="text/css" rel="stylesheet" href="../../static/css/bootstrap.min.css"/> </head> <body> <div class="container"> <!-- 标题 --> <div class="row clearfix"> <div class="col-md-12 column"> <h1> <small>增加书籍</small> </h1> </div> </div> <!-- 增加书籍的表单 --> <div class="row clearfix"> <div class="col-md-8 column"> <form action="/book/addBook"> <div class="form-row"> <label>书籍名称<input type="text" name="bookName" class="form-control input-group mb-3" required/></label> . </div> <div class="form-row"> <label>书籍数量<input type="text" name="bookCounts" class="form-control input-group mb-3" required/> </label> </div> <div class="form-row"> <label>书籍描述<input type="text" name="detail" class="form-control input-group mb-3" required/></label> </div> <div class="form-row"> <input type="submit" class="btn btn-primary form-control" onclick="return addClick()" value="添加" style="width: 200px"/> </div> </form> </div> </div> </div> <script> // 防止误点操作 function addClick(){ var msg = "确认增加?"; return confirm(msg); } </script> </body> </html>
-
update.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>修改书籍</title> <script src="../../static/js/jquery-3.4.1.min.js"></script> <script src="../../static/js/bootstrap.min.js"></script> <link type="text/css" rel="stylesheet" href="../../static/css/bootstrap.min.css"/> </head> <body> <div class="container"> <!-- 标题 --> <div class="row clearfix"> <div class="col-md-12 column"> <h1> <small>修改书籍</small> </h1> </div> </div> <!--/*@thymesVar id="book" type="com.xp.entity.Book"*/--> <!-- 更新的表单 --> <div class="row clearfix"> <div class="col-md-6"> <form action="/book/updateBook"> <input type="hidden" name="bookId" th:value="${book.bookId}"> <div class="form-row input-group mb-3"> <label>书籍名称<input type="text" name="bookName" class="form-control" th:value="${book.bookName} "/></label> </div> <div class="form-row input-group mb-3"> <label>书籍数量<input type="text" name="bookCounts" class="form-control" th:value="${book.bookCounts}"/></label> </div> <div class="form-row input-group mb-3"> <label>书籍描述<input type="text" name="detail" class="form-control" th:value="${book.detail}"/></label> </div> <div class="form-row"> <input type="submit" class="btn btn-primary form-control" onclick="return updateClick()" value="修改" style="width: 200px"/> </div> </form> </div> </div> </div> <script> // 防止误点 function updateClick(){ var msg = "确认修改?"; return confirm(msg) === true; } </script> </body> </html>
-
BookController
package com.xp.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.xp.entity.Book; import com.xp.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller @RequestMapping("/book") @SuppressWarnings("all") public class BookController { private static final String TO_ALL_BOOK = "/toAllBook"; // Controller 层调用 Service 层 @Autowired private BookService bookService; public void setBookService(BookService bookService) { this.bookService = bookService; } @RequestMapping(TO_ALL_BOOK) public String toAllBook() { return "allBook"; } @RequestMapping("/allBook") @ResponseBody public String allBook() throws JsonProcessingException { List<Book> books = bookService.queryAllBook(); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(books); } @RequestMapping("/toAddBook") public String toAddBook() { return "addBook"; } @RequestMapping("/addBook") public String addBook(Book book) { return "redirect:/book" + TO_ALL_BOOK; } @RequestMapping("/deleteBook") public String deleteBook(int id) { bookService.deleteBookById(id); return "redirect:/book" + TO_ALL_BOOK; } @RequestMapping("/updateBook") public String updateBook(Book book) { bookService.updateBook(book); return "redirect:/book" + TO_ALL_BOOK; } @RequestMapping("/toUpdateBook") public String toUpdateBook(int id, Model model) { Book book = bookService.queryBookById(id); model.addAttribute("book", book); return "updateBook"; } @RequestMapping("/searchBook") @ResponseBody public String searchBook(String name) throws JsonProcessingException { List<Book> book = bookService.queryBookByName(name); return new ObjectMapper().writeValueAsString(book); } }
到这里,我们的 SSM 框架才算完全整合。在以后再次写 SSM 项目时,可以将这个作为模板来进行编写。