1、回顾MVC
1.1、什么是MVC
参见《MVC三层架构》
- MVC是模型(
Model
)、视图(View
)、控制器(Controller
)的简写,是一种软件设计规范。
- MVC是将业务逻辑、数据、显示分离来组织代码的。
- MVC主要作用是降低了视图与业务逻辑间的双向偶合。
- MVC不是一种设计模式,MVC是一种架构模式。当然不同的
MVC
架构之间也存在一些差异。
**(1)Model(模型):**数据模型,提供要展示的数据,包含数据和行为。我们一般将Service
(业务)层、DAO
(持久)层和JavaBean
统称为Model
。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
**(2)View(视图):**负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
**(3)Controller(控制器):**接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。Controller
的底层都是通过Servlet
来实现的。
最典型的MVC就是JSP
+ servlet
+ javabean
的模式。
1.2、Web开发的两个时代
1.2.1、Model 1 时代
- 在
web
早期的开发中,通常采用的都是Model1
。
Model1
中,主要分为两层,视图层和模型层。
Model1
的优点:架构简单,比较适合小型项目开发;
Model1
的缺点:JSP
职责不单一,职责过重,不便于维护。
1.2.1、Model 2 时代
Model2
把一个项目分成三部分,包括模型(Model
)、视图(View
)、控制(Controller
)。
数据处理过程:
- 用户发请求;
Servlet
接收请求数据,并调用对应的业务逻辑方法;
- 业务处理完毕,返回更新后的数据给
servlet
;
servlet
转向到JSP
,由JSP
来渲染页面;
- 响应给前端更新后的页面。
Model2
不仅提高的代码的复用率与项目的扩展性,且大大降低了项目的维护成本。Model1
模式的实现比较简单,适用于快速开发小规模项目,Model1
中JSP
页面身兼View
和Controller
两种角色,将控制逻辑和表现逻辑混杂在一起,从而导致代码的重用性非常低,增加了应用的扩展性和维护的难度。Model2
消除了Model1
的缺点。
1.3、回顾Servlet
(1)新建一个普通的Maven
项目,(具体步骤参考《Maven详解》),项目名称为SpringMVC-Study
。
(2)删除系统自动生成的src
目录,所得到的空项目就是我们的Maven父项目,我们可以在其中创建多个子项目(即:Module
)。
(3)在父项目的pom.xml
文件中导入父/子工程需要的Maven依赖。
pom.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <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>
|
(4)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-01-servlet
。
(5)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
(6)在子项目的java
目录下新建一个com.atangbiji.servlet
包,并在该包下新建一个HelloServlet
类,用于实现Servlet
接口,并处理用户的请求。
HelloServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.atangbiji.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 { String method = req.getParameter("method"); if (method.equals("add")){ req.getSession().setAttribute("msg","执行了add方法"); } if (method.equals("delete")){ req.getSession().setAttribute("msg","执行了delete方法"); } req.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(req,resp); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
|
(7)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个hello.jsp
文件。
hello.jsp
文件:
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>阿汤笔迹</title> </head> <body> ${msg} </body> </html>
|
注:
- 当
web
页面写在web
目录下时,项目发布后,页面文件用户可见,数据不安全。
- 当
web
页面写在web/WEB-INF
目录下时,项目发布后,页面文件用户不可见,数据安全。
(8)在web.xml
中注册Servlet
,并为其提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>helloServlet</servlet-name> <servlet-class>com.atangbiji.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
|
(9)配置Tomcat服务器;deploy(发布)springMVC-01-servlet:war
包,并为该子项目添加虚拟路径映射:/s1
。(参见《Servlet详解》)
(10)启动测试。在IDEA中启动Tomcat。
- 在浏览器中输入
http://localhost:8080/s1/hello?method=add
,访问结果如下:
- 在浏览器中输入
http://localhost:8080/s1/hello?method=delete
,访问结果如下:
1.4、MVC框架主要做了哪些事?
(1)将url
映射到java
类或java
类的方法。
(2)封装用户提交的数据。
(3)处理请求、调用相关的业务处理、封装响应数据。
(4)将响应的数据进行渲染。jsp/html
等表示层数据。
注:
- 常见的服务器端
MVC
框架有:Struts
、Spring MVC
、ASP.NET MVC
、Zend Framework
、JSF
。
- 常见前端
MVC
框架:vue
、angularjs
、react
、backbone
。
- 由
MVC
可以演化出另外一些模式如:MVP
、MVVM
等。
2、SpringMVC概述
2.1、什么是SpringMVC
Spring MVC
是Spring Framework
的一部分,是基于Java
实现MVC
的轻量级Web
框架。
注:SpringMVC
的底层还是Servlet
。
2.2、Spring MVC的特点
- 轻量级,简单易学。
- 高效 , 基于请求响应的
MVC
框架。
- 与
Spring
兼容性好,无缝结合。(我们只需将SpringMVC
中所有要用到的Bean
,注册到Spring
的IOC
容器中去即可使用!)
- 约定优于配置。
- 功能强大:
RestFul
、数据验证、格式化、本地化、主题等。
- 简洁灵活。(从
Spring 2.5
开始,使用Java 5
或者以上版本的用户可以采用基于注解形式进行开发,十分简洁。)
2.3、为什么要学习SpringMVC
正因为SpringMVC
好用、简单、便捷、易学;天生和Spring
无缝集成(使用SpringIoC
和Aop
);使用约定优于配置;能够进行简单的junit
测试;支持RestFul
风格、异常处理、本地化、国际化、数据验证、类型转换、拦截器 等功能,所以我们要学习。
最重要的一点还是用的人多 , 使用的公司多!
3、中心控制器
Spring MVC
框架像许多其他MVC
框架一样, 以请求为驱动,围绕一个中心Servlet(控制器)分派请求及提供其他功能。
Spring
的web
框架围绕DispatcherServlet【调度Servlet
】设计。DispatcherServlet
的作用是将请求分发到不同的处理器(即:不同的类和方法)。
注:DispatcherServlet
是一个实际的Servlet(它也是继承自HttpServlet
基类),其继承关系如下图所示。
4、SpringMVC运行原理
SpringMVC
的运行原理如下图所示:
当用户发送请求时:
- 前端控制器(Front Controller)【即:
DispatcherServlet
(中心控制器)】首先拦截浏览器的所有请求;
- 然后,处理映射器(如:
BeanNameUrlHandlerMapping
等)和处理器适配器(如:SimpleControllerHandlerAdapter
等)再根据请求的参数(URL)将该请求委托给对应的实际控制器(Controller
)。
- 然后,控制器(
Controller
)调用(Service
层)业务对象对请求进行处理;(Dao层)创建数据模型、访问数据库,并将处理后的模型数据存放在模型和视图(ModelAndView
对象)中返回给前端控制器。
- 然后,通过视图解析器(如:
InternalResourceViewResolver
等)找到要跳转到的页面,并使用模型和视图(ModelAndView
对象)中的数据渲染视图。
- 最后,再通过中心控制器,将结果返回给用户。
5、第一个SpringMVC程序
5.1、搭建MavenWeb项目环境
(1)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-02-helloSpringMVC
。
(2)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。
(3)确定pom.xml
文件中导入了SpringMVC
相关的依赖!
5.2、注册中心控制器
(4)在web.xml
文件中注册DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<servlet> <servlet-name>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> </web-app>
|
注:DispatcherServlet
(请求分发器,又称“前置控制器”、“中心控制器”),它是整个SpringMVC
的控制中心(核心),它也是一个实际的Servlet
。
5.3、配置处理映射器、处理器适配器和视图解析器
(5)在该子项目的resources
目录下新建一个SpringMVC
的配置文件,配置文件的名称为:springmvc-servlet.xml
(即:[servletname]-servlet.xml
),并在其中添加SpringMVC
的核心三要素:处理映射器、处理器适配器和视图解析器。
springmvc-servlet.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
|
注:
SpringMVC
的配置文件本质上就是Spring
的配置文件。
- 在实际项目中,我们可以将视图解析器换成
Thymeleaf
、Freemarker
等模板引擎。
- 若视图层为
html
页面,则只需对视图解析器的前缀和后缀做相应的修改即可。
5.4、通过接口创建Controller
(6)在该子项目的java
目录下新建一个com.atangbiji.controller
包(用于存放我们自己编写的控制器),并在该包下新建一个HelloController
类。
HelloController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.atangbiji.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 request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg","HelloSpringMVC!"); mv.setViewName("hello"); return mv; } }
|
**注:**只要实现(implements
)了Controller
接口的类就是一个控制器,即:获得了控制器功能。
5.5、注册自己编写的处理器
(7)将自己编写的处理器(bean
)在SpringIOC
容器(Spring
的配置文件)中注册。
springmvc-servlet.xml
文件:
1 2
| <bean id="/hello" class="com.atangbiji.controller.HelloController"/>
|
注:id
对应请求路径,class
对应处理请求的类。
5.6、编写前端页面
(8)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个hello.jsp
文件,用于显示ModelandView
中存放的数据,以及我们的正常页面。
hello.jsp
文件:
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>阿汤笔迹</title> </head> <body> ${msg} </body> </html>
|
**注:**我们把所有的视图都存放在/WEB-INF/
目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
5.7、发布与测试
(9)配置Tomcat服务器;deploy(发布)springMVC-02-helloSpringMVC:war
包,并为该子项目添加虚拟路径映射:/mvc
。(参见《Servlet详解》)
(10)启动测试。在IDEA中启动Tomcat,在浏览器中输入http://localhost:8080/mvc/hello
,页面访问成功。访问结果如下:
**注:**通过这个示例程序,估计大部分同学都能理解SpringMVC
运行的原理了。但是我们实际开发才不会这么写,这样写是为了让大家更好地理解SpringMVC
的运行原理!实现项目中,我们会使用注解版进行开发,这才是SpringMVC
的精髓!
6、SpringMVC执行流程
SpringMVC
的一个较完整的执行流程如下图所示,其中:实线表示SpringMVC
框架提供的技术,不需要开发者实现;虚线表示需要开发者实现。
接下来,结合第一个SpringMVC示例程序,分析SpringMVC的执行流程:
(1)用户发出请求,DispatcherServlet
接收请求并拦截浏览器的所有请求。
注:DispatcherServlet
表示前置控制器,它是整个SpringMVC
的控制中心。
如:第一个SpringMVC
示例程序中请求的url
为:http://localhost:8080/mvc/hello
注:url
可拆分成如下三部分:
- 服务器域名(如:
http://localhost:8080
)
- 部署在服务器上的
web
站点(如:/mvc
)
- 控制器(如:
/hello
)
通过分析,上述url
表示为:请求位于服务器localhost:8080
上的mvc
站点的hello
控制器。
(2)DispatcherServlet
调用HandlerMapping
(处理器映射),HandlerMapping
根据请求的url
查找(映射)相应的Handler
(处理器)。
(3)HandlerExecution
根据url
查找控制器(Controller
)。如上述url
被查找的控制器为:hello
。
注:HandlerExecution
表示具体执行的处理器。
(4)HandlerExecution
将解析后的信息传递给DispatcherServlet
。如:解析控制器映射等。
(5)HandlerAdapter
(处理器适配器)按照特定的规则去执行Handler
。
(6)Handler
让具体的Controller
执行。
(7)Controller
将具体的执行信息返回给HandlerAdapter
,即:ModelAndView
。
注:控制器(Controller
)主要调用(Service
层)业务对象对请求进行处理;并通过Dao
层创建数据模型、访问数据库,然后将处理后的模型数据存放在模型和视图(ModelAndView
对象)中返回给前端控制器。
(8)HandlerAdapter
将视图逻辑名或模型数据传递给DispatcherServlet
。
(9)DispatcherServlet
调用视图解析器(ViewResolver
)来解析HandlerAdapter
传递的视图逻辑名。
**注:**视图解析器的主要完成以下功能:
- 获取
ModelAndView
中的数据;
- 解析
ModelAndView
中的视图名称;
- 通过字符串拼(前缀、后缀)接找到对应的视图;
- 将数据渲染到该视图上。
(10)视图解析器将解析的视图逻辑名传给DispatcherServlet
。
(11)DispatcherServlet
根据视图解析器解析的视图结果,调用具体的视图(View
)。
(12)View
把最终视图呈现给用户。
7、使用注解开发SpringMVC(重点)
7.1、搭建MavenWeb项目环境
(1)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-03-annotationSpringMVC
。
(2)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。(详见5.1节)
(3)在父项目的pom.xml
文件中导入父/子工程所需要的SpringMVC
相关的依赖。
pom.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <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>
|
**注:**由于Maven的“约定大于配置”思想,我们以后可能会遇到我们自己写的配置文件(如:我们配置文件*.xml
写在了项目中的Java
目录下)无法被导出或者无法生效的问题。该问题的解决方法是:在pom.xml
的<build>……</build>
中配置resources
,修改Maven约定的过滤条件,来防止我们的资源导出失败。
pom.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
|
7.2、注册中心控制器
(4)在web.xml
文件中注册DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<servlet> <servlet-name>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> </web-app>
|
注:
7.3、添加SpringMVC配置文件(固定写法)
(5)在该子项目的resources
目录下新建一个SpringMVC
的配置文件,配置文件的名称为:springmvc-servlet.xml
(即:[servletname]-servlet.xml
)。并在其中完成以下工作:
①在xml
配置文件的头部引入context
和MVC
约束。
②开启自动扫描包,让指定包下的注解生效,由IOC
容器统一管理。
③过滤静态资源,让Spring MVC
不处理静态资源(如:html
、css
、js
、mp3
、mp4
文件等)。
④开启SpringMVC
注解驱动,自动完成DefaultAnnotationHandlerMapping
和AnnotationMethodHandlerAdapter
实例的注入。
**注:**在spring
中一般采用@RequestMapping
注解来完成映射关系。要想使@RequestMapping
注解生效,必须向上下文中注册一个DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter
实例。这两个实例分别在类级别和方法级别处理注解。而annotation-driven
配置帮助我们自动完成上述两个实例的注入。
⑤配置视图解析器(前缀、后缀)。
springmvc-servlet.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?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.atangbiji.controller"/> <mvc:default-servlet-handler /> <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>
|
注:
SpringMVC
的配置文件本质上就是Spring
的配置文件。
- 处理器映射器、处理器适配器、视图解析器是使用
springMVC
开发必须配置的三大件。当使用注解开发SpringMVC
时,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可!
- 在实际项目中,我们可以将视图解析器换成
Thymeleaf
、Freemarker
等模板引擎。
7.4、通过注解创建Controller
(6)在该子项目的java
目录下新建一个com.atangbiji.controller
包(用于存放我们自己编写的控制器),并在该包下新建一个HelloController
类。
HelloController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/HelloController") public class HelloController { @RequestMapping("/hello") public String sayHello(Model model){ model.addAttribute("msg","Hello,SpringMVC注解开发"); return "hello"; } }
|
注:
@Controller
之所以能够在Spring IOC
容器初始化时被自动扫描到,只因为我们在SpringMVC
配置文件中配置了要自动扫描的包。注解完成自动扫描,就相当于我们已经将自己编写的处理器(bean
)在SpringIOC
容器(Spring
的配置文件)中完成注册了,实现了bean
的自动装配。
- 被**
@Controller
注解的控制器会被视图解析器解析;被@RestController
注解**的控制器不会被图解析器解析,可以直接返回字符串或json
格式的数据。
@RequestMapping
的作用是为了映射请求路径,这里因为类与方法上都映射了请求路径,所以访问时应该访问:/HelloController/hello
。
- 方法中声明
Model
类型的参数是为了把Action
中的数据带到视图中。
- 方法返回的结果是视图的名称
hello
,加上配置文件中的前缀和后缀后,变成WEB-INF/jsp/hello.jsp
。
7.5、创建视图层
(7)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个hello.jsp
文件,用于显示ModelandView
中存放的数据,以及我们的正常页面。
hello.jsp
文件:
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>阿汤笔迹</title> </head> <body> ${msg} </body> </html>
|
**注:**我们把所有的视图都存放在/WEB-INF/
目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
7.6、发布与测试
(8)配置Tomcat服务器;deploy(发布)springMVC-03-annotationSpringMVC:war
包,并为该子项目添加虚拟路径映射:/mvc
。(参见《Servlet详解》)
(9)启动测试。在IDEA中启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/HelloController/hello
,页面访问成功。访问结果如下:
8、Controller的两种实现方式
8.1、控制器概述
Controller
,即:控制器。
Controller
(控制器)负责提供访问应用程序的行为,即:程序接受用户请求后要处理的一些行为,也就是以前的Servlet
。
Controller
(控制器)可以通过实现(implements
)Controller
接口或注解两种方式实现。
Controller
(控制器)负责解析用户的请求,并将其转换为一个模型,返回给视图解析器。
- 在
Spring MVC
中,一个控制器(Controller
)类中可以包含多个方法。
- 在
Spring MVC
中,Controller
的配置方式有很多种。
8.2、方式一:通过接口实现Controller
8.2.1、控制器接口
Controller
是一个控制器接口,在org.springframework.web.servlet.mvc
包下,该接口中只有一个方法;
1 2 3 4 5
| public interface Controller { ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception; }
|
- 只要实现(
implements
)了Controller
接口的类就是一个控制器,即:获得控制器功能。
- 控制器主要处理用户请求,并返回一个模型与视图(
ModelAndView
)对象。
8.2.2、实现步骤
具体实现步骤参见第5节:《第一个SpringMVC
程序》。
8.2.3、优缺点分析
- **优点:**该方式是较老的实现控制器的方法,但使用它更容易理解
SpringMVC
的运行原理。
- 缺点:一个控制器中只有一个方法,如果要多个方法则需要定义多个
Controller
,定义的方式比较麻烦。
8.3、方式二:通过注解实现Controller(推荐)
8.3.1、使用@Controller注解声明控制器
- @
Controller
注解用于声明Spring
类的实例是一个控制器。
**注:**在讲Spring
时还提到了另外3个与@Controller
注解功能一样的注解,即:@Component
、@Service
、@Repository
。(参见《Spring详见》9.4节)
8.3.2、使用@RequestMapping注解映射访问路径
@RequestMapping
注解用于映射url
到控制器类或一个特定的处理程序方法。
- 它可用于类或方法上。若用于类上,则表示类中所有响应请求的方法都是以该路径作为父路径。
8.3.3、实现步骤
具体实现步骤参见第7节:《使用注解开发SpringMVC》。
8.3.4、优缺点分析
- 优点:一个控制器中可以有多个方法,使用简单方便。
- **缺点:**底层都被封装了,使用它可能不太好理解
SpringMVC
的运行原理。
**注:**无论方式一还是方式二,它们都实现了视图的复用(即:不同的请求可以指向同一个视图,但是页面显示的结果可以不同)。因此,控制器与视图之间是弱偶合关系。
9、RestFul风格
9.1、什么是RestFul风格
RestFul
(Representational State Transfer
)就是一种资源定位及资源操作的风格。它不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
互联网上所有的事物都可以被抽象为资源,并且每个资源我们都只能通过唯一的访问路径(URL
)去访问它。
**RestFul
风格中的POST
、DELETE
、PUT
、GET
请求方式与对资源的增、删、改、查操作一一对应。**如:
URL |
请求方式 |
功能 |
http://ip:port/项目虚拟映射路径/book/1 |
GET (查询) |
查询id 为1的图书 |
http://ip:port/项目虚拟映射路径/book |
GET (查询) |
查询全部的图书 |
http://ip:port/项目虚拟映射路径/book |
POST (增加) |
增加一个图书 |
http://ip:port/项目虚拟映射路径/book/1 |
PUT (修改) |
修改id 为1的图书信息 |
http://ip:port/项目虚拟映射路径/book/1 |
DELETE (删除) |
删除id 为1的图书信息 |
**注:**当通过RestFul
风格操作资源时,即使请求地址(URL
)一样,也可以实现不同的功能!请求的动作(增、删、改、查)由请求方式(POST
、DELETE
、PUT
、GET
)决定。
9.2、对比测试
9.2.1、传统的资源操作方式
(1)在上述子项目的基础上,在com.atangbiji.controller
包下新建一个TraditionalController
类,并使用传统的资源操作方式映射访问路径(URL
)。
TraditionalController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/Traditional") public class TraditionalController { @RequestMapping("/add") public String add(int p1, int p2, Model model){ int result = p1 + p2; model.addAttribute("msg","计算结果为:" + result); return "hello"; } }
|
(2)此时,我们需要使用?
传参。重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/Traditional/add?p1=1&p2=2
,页面访问成功。访问结果如下:
9.2.2、RestFul风格的资源操作方式
(1)在上述子项目的基础上,再在com.atangbiji.controller
包下新建一个RestFulController
类,并使用RestFul风格的资源操作方式映射访问路径(URL
)。
RestFulController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/RestFul") public class RestFulController { @RequestMapping("/add/{p1}/{p2}") public String add(@PathVariable int p1,@PathVariable int p2, Model model){ int result = p1 + p2; model.addAttribute("msg","计算结果为:" + result); return "hello"; } }
|
**注:在Spring MVC
中,我们可以使用@PathVariable
(路径变量)**注解,将方法参数的值对应绑定到一个URL
模板变量上。这样,我们就可以在URL
中给参数传参了。
(2)此时,我们需要使用/
传参。重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/RestFul/add/1/2
,页面访问成功。访问结果如下:
9.3、使用RestFul风格的优点
使用RestFul
风格(路径变量)的URL
主要有以下好处:
- 使路径变得更加简洁;
URL
中不会暴露访问的参数(名称),访问更加安全;
- 易于实现缓存,访问更高效;
- 获得参数更加方便,框架会自动进行类型转换;
- 通过路径变量的类型可以约束访问参数。若参数类型不一样,则访问不到对应的请求方法。如:若上述访问路径是
http://localhost:8080/mvc/RestFul/add/1/a
,则路径与方法不匹配(a
不是int
类型),而不会是参数转换失败。
9.4、使用method属性指定请求类型
我们可以在@RequestMapping
注解中使用method
属性来约束请求的类型,以收窄请求范围。
注:@RequestMapping
注解中定义了RequestMethod
(请求方式)的值可以是:GET
、HEAD
、POST
、PUT
、PATCH
、DELETE
、OPTIONS
、TRACE
。
9.4.1、测试步骤
(1)修改上述add
方法的@RequestMapping
注解,通过method
属性限定映射的访问路径必须是POST
请求。
RestFulController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/RestFul") public class RestFulController { @RequestMapping(value = "/add/{p1}/{p2}",method = RequestMethod.POST) public String add(@PathVariable int p1,@PathVariable int p2, Model model){ int result = p1 + p2; model.addAttribute("msg","计算结果为:" + result); return "hello"; } }
|
(2)重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/RestFul/add/1/2
。因为我们使用浏览器地址栏进行访问默认使用是Get
请求,因此会报405
错误。访问结果如下:
(3)如果将上述请求方式由POST
修改为GET
。
RestFulController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/RestFul") public class RestFulController { @RequestMapping(value = "/add/{p1}/{p2}",method = RequestMethod.GET) public String add(@PathVariable int p1,@PathVariable int p2, Model model){ int result = p1 + p2; model.addAttribute("msg","计算结果为:" + result); return "hello"; } }
|
(4)重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/RestFul/add/1/2
,页面访问成功。访问结果如下:
**注:**我们可以使用@GetMapping
注解,将@RequestMapping(value = "/add/{p1}/{p2}",method = RequestMethod.GET)
注解简化为@GetMapping("/add/{p1}/{p2}")
,两者是等价的。
9.4.2、小结
-
我们可以在@RequestMapping
注解中使用method
属性来约束请求的类型,以收窄请求范围。
-
@RequestMapping
注解中定义了RequestMethod
(请求方式)的值可以是:GET
、HEAD
、POST
、PUT
、PATCH
、DELETE
、OPTIONS
、TRACE
。
-
所有的地址栏请求默认都是 HTTP GET 类型的。
-
方法级别的注解变体有:(平时使用的会比较多)
@GetMapping
:等价于@RequestMapping(method = RequestMethod.GET)
。
@PostMapping
:等价于@RequestMapping(method = RequestMethod.POST)
。
@PutMapping
:等价于@RequestMapping(method = RequestMethod.PUT)
。
@DeleteMapping
:等价于@RequestMapping(method = RequestMethod.DELETE)
。
@PatchMapping
:等价于@RequestMapping(method = RequestMethod.PATCH)
。
-
当通过RestFul
风格操作资源时,即使请求地址(URL
)一样,也可以实现不同的功能!请求的动作(增、删、改、查)由请求方式(POST
、DELETE
、PUT
、GET
)决定。
10、结果跳转方式
10.1、通过ModelAndView设置跳转页面
我们可以使用ModelAndView
对象设置视图(view
)名称,然后通过视图解析器跳转到指定的页面。
跳转页面的位置为:{视图解析器前缀} + viewName
(视图名称) + {视图解析器后缀}。
如:第一个SpringMVC程序中
springmvc-servlet.xml
文件:
1 2 3 4 5 6 7 8
| <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
|
HelloController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.atangbiji.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 request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg","HelloSpringMVC!"); mv.setViewName("hello"); return mv; } }
|
10.2、通过Servlet API实现结果跳转(了解)
10.2.1、实现功能
因为Controller
的底层(本质)仍然是Servlet
,所以我们也可以通过Servlet API
实现结果跳转。我们可以:
(1)通过HttpServletResponse
输出结果。
(2)通过HttpServletResponse
实现重定向。
(3)通过HttpServletRequest
实现请求转发。
**注:**通过Servlet API
实现结果跳转,不需要视图解析器。
10.2.2、测试
(1)在上述子项目的基础上,在com.atangbiji.controller
包下新建一个ResultByServletAPI
类。
ResultByServletAPI.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@Controller public class ResultByServletAPI { @RequestMapping("ServletAPI/test1") public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().println("Hello,SpringMVC BY servlet API"); } @RequestMapping("/ServletAPI/test2") public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendRedirect("/mvc/index.jsp"); } @RequestMapping("/ServletAPI/test3") public void test3(HttpServletRequest request, HttpServletResponse response) throws Exception { request.getRequestDispatcher("/index.jsp").forward(request,response); } }
|
(2)修改默认生成的index
页面。
index.jsp
文件:
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>阿汤笔迹</title> </head> <body> 欢迎访问index页面! </body> </html>
|
(3)重新启动Tomcat
。
- 在浏览器中输入
http://localhost:8080/mvc/ServletAPI/test1
,页面访问成功。访问结果如下:
- 在浏览器中输入
http://localhost:8080/mvc/ServletAPI/test2
,页面访问成功。访问结果如下:
- 在浏览器中输入
http://localhost:8080/mvc/ServletAPI/test3
,页面访问成功。访问结果如下:
10.3、通过SpringMVC实现结果跳转
10.3.1、通过SpringMVC实现转发和重定向(无需视图解析器)【了解】
(1)在上述子项目的基础上,先将SpringMVC
配置文件(springmvc-servlet.xml
)中的视图解析器注释掉。
(2)然后,在com.atangbiji.controller
包下新建一个ResultBySpringMVC1
类。
ResultBySpringMVC1.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class ResultBySpringMVC1 { @RequestMapping("SpringMVC1/test1") public String test1(){ return "/index.jsp"; }
@RequestMapping("SpringMVC1/test2") public String test2(){ return "forward:/index.jsp"; }
@RequestMapping("SpringMVC1/test3") public String test3(){ return "redirect:/index.jsp"; } }
|
(3)重新启动Tomcat
。
- 在浏览器中输入
http://localhost:8080/mvc/SpringMVC1/test1
,页面访问成功。访问结果如下:
- 在浏览器中输入
http://localhost:8080/mvc/SpringMVC1/test2
,页面访问成功。访问结果如下:
- 在浏览器中输入
http://localhost:8080/mvc/SpringMVC1/test3
,页面访问成功。访问结果如下:
10.3.2、通过SpringMVC实现转发和重定向(需要视图解析器)【推荐】
(1)在上述子项目的基础上,先将SpringMVC
配置文件(springmvc-servlet.xml
)中视图解析器的注释去除。
(2)然后,在com.atangbiji.controller
包下新建一个ResultBySpringMVC2
类。
ResultBySpringMVC2.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class ResultBySpringMVC2 { @RequestMapping("SpringMVC2/test1") public String test1(Model model){ model.addAttribute("msg","Hello,通过SpringMVC和视图解析器实现请求转发。"); return "hello"; }
@RequestMapping("SpringMVC2/test2") public String test2(){ return "redirect:/index.jsp"; } }
|
(3)重新启动Tomcat
。
- 在浏览器中输入
http://localhost:8080/mvc/SpringMVC2/test1
,页面访问成功。访问结果如下:
- 在浏览器中输入
http://localhost:8080/mvc/SpringMVC2/test2
,页面访问成功。访问结果如下:
注:重定向本质就是重新请求一个新的URl
, 所以它根本不需要视图解析器。(注意路径问题)
11、数据处理
11.1、接受前端提交的数据
11.1.1、提交域名中参数的名称和处理方法中参数的名称一致
若提交域名中参数的名称和处理方法中参数的名称一致,则可直接进行数据传递。
测试:
(1)在上述子项目的基础上,在com.atangbiji.controller
包下新建一个UserController
类。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class UserController { @RequestMapping("/user/test1") public String test1(String name, Model model){ model.addAttribute("msg","提交域名中参数的名称是:name,值为:" + name + "; 处理方法中参数的名称也是:name!"); return "hello"; } }
|
(2)重新启动Tomcat
。在浏览器中输入http://localhost:8080/mvc/user/test1?name=atang
,页面访问成功。访问结果如下:
11.1.2、提交域名中参数的名称和处理方法中参数的名称不一致(推荐)
若提交域名中参数的名称和处理方法中参数的名称不一致,则需要在处理方法中使用@RequestParam
注解声明域名中该参数对应的名称后,才能进行数据传递。
测试:
(1)在UserController
类中下新建一个test2
方法。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam;
@Controller public class UserController { @RequestMapping("/user/test2") public String test2(@RequestParam("userName") String name, Model model){ model.addAttribute("msg","提交域名中参数的名称是:userName,值为:" + name + "; 处理方法中参数的名称是:name!"); return "hello"; } }
|
(2)重新启动Tomcat
。在浏览器中输入http://localhost:8080/mvc/user/test2?userName=atang
,页面访问成功。访问结果如下:
**注:**无论提交域名中参数的名称和处理方法中参数的名称是否一致,我们建议把所有需要从前端接受数据的形参都使用@RequestParam
注解进行声明,这样哪些参数需要从前端传值就会一目了然。
11.1.3、提交一个对象
若提交数据是一个对象,则需要域名中提交的参数名称和实体类(pojo
)中该对象的属性名保持一致,且处理方法中使用该对象作为形参,才能进行数据传递。
测试:
(1)在上述子项目的基础上,在父项目的pom.xml
文件中导入lombok
依赖。
pom.xml
文件:
1 2 3 4 5 6
| <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency>
|
(2)在java
目录下新建一个com.atangbiji.pojo
包,并在该包下新建一个User
实体类。
User.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.atangbiji.pojo;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @NoArgsConstructor @AllArgsConstructor
public class User { private int id; private String name; private int age; private String sex; }
|
(3)在UserController
类中下新建一个test3
方法。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atangbiji.controller;
import com.atangbiji.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class UserController { @RequestMapping("/user/test3") public String test3(User user, Model model){ model.addAttribute("msg","URL传入的User对象为:" + user.toString()); return "hello"; } }
|
(4)重新启动Tomcat
。在浏览器中输入http://localhost:8080/mvc/user/test3?id=1&name=atang&age=18
,页面访问成功。访问结果如下:
注:
- 若前端输入的参数名和对象属性名不一致,则该属性传入的就是
null
。
- 若前端没有输入该对象某一属性对应的参数,则该属性传入的也是
null
。
- 我们也可以在后台处理函数的参数列表中使用
@RequestBody
注解,来接收前端传递给后端的对象数据。
11.2、将数据显示到前端
11.2.1、方式一:通过ModelAndView
如:第一个SpringMVC程序中
HelloController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.atangbiji.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 request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg","HelloSpringMVC!"); mv.setViewName("hello"); return mv; } }
|
11.2.2、方式二:通过ModelMap
HelloController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class HelloController { @RequestMapping("/HelloController/test") public String test(ModelMap modelMap){ modelMap.addAttribute("msg","通过ModelMap将数据显示到前端!"); return "hello"; } }
|
11.2.3、方式三:通过Model
HelloController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class HelloController { @RequestMapping("/HelloController/hello") public String hello(Model model){ model.addAttribute("msg","通过Model将数据显示到前端!"); return "hello"; } }
|
11.2.4、对比
就对于新手而言,上述三种方式的使用区别简单来说就是:
-
Model
只有寥寥几个方法只适合用于储存数据,简化了新手对于Model
对象的操作和理解(使用最多);
-
ModelMap
继承了LinkedMap
,除了实现了自身的一些方法外,同样继承了LinkedMap
的方法和特性;
-
ModelAndView
可以在储存数据的同时,进行设置返回的逻辑视图,进行视图跳转。
12、解决乱码问题
乱码问题是在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题!
以前乱码问题可以通过过滤器解决 , SpringMVC
也给我们提供了一个解决乱码问题的过滤器 , 我们只需在web.xml
中配置即可。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <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>
|
**注:**一般情况下,SpringMVC
默认的编码过滤器就已经能够解决绝大多数的乱码问题了!如果乱码问题仍得不到解决,建议:
(1)检查tomcat
的配置文件是否设置utf-8
编码!
1 2 3
| <Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
|
(2)自定义编码过滤器,然后在web.xml
中配置自定义的编码过滤器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| package com.atangbiji.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;
public class GenericEncodingFilter implements Filter {
@Override public void destroy() { }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse myResponse=(HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8");
HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); }
@Override public void init(FilterConfig filterConfig) throws ServletException { }
}
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request; private boolean hasEncode; public MyRequest(HttpServletRequest request) { super(request); this.request = request; }
@Override public Map getParameterMap() { String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { try { request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { 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; } }
|
13、JSON
13.1、JSON概述
13.1.1、什么是JSON
?
JSON
(JavaScript Object Notation
,JS
对象标记)是一种轻量级的数据交换格式,目前使用特别广泛。其主要特点:
- 采用完全独立于编程语言的文本格式来存储和表示数据。
- 层次结构简洁、清晰。
- 易于人阅读和编写,同时也易于机器解析和生成,并能够有效地提升网络传输效率。
在 JavaScript
语言中,一切都是对象。因此,任何JavaScript
支持的类型都可以通过JSON
来表示,例如字符串、数字、对象、数组等。
13.1.2、JSON
的语法格式
- 对象表示为键值对,数据由逗号(
,
)分隔。
- 大括号(
{}
)保存对象。
- 中括号(
[]
)保存数组。
JSON 键值对是用来保存JavaScript
对象的一种方式,和JavaScript
对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 (""
)包裹,使用冒号(:
)分隔,然后紧接着值。如:
1 2 3
| {"name": "Atang"} {"age": "3"} {"sex": "男"}
|
13.1.3、JSON
和JavaScript
对象的关系
**JSON
是JavaScript
对象的字符串表示法,它使用文本表示一个JS
对象的信息,本质上是一个字符串。**如:
1 2
| var obj = {a: 'Hello', b: 'World'}; var json = '{"a": "Hello", "b": "World"}';
|
13.1.4、JSON
字符串和JavaScript
对象的相互转换
(1)JSON
字符串转JavaScript
对象
要将JSON
字符串转换为JavaScript
对象,需使用JSON.parse()
方法。如:
1 2
| var obj = JSON.parse('{"a": "Hello", "b": "World"}');
|
(2)JavaScript
对象转JSON
字符串
要将JavaScript
对象转换为JSON
字符串,需使用JSON.stringify()
方法。如:
1 2
| var json = JSON.stringify({a: 'Hello', b: 'World'});
|
13.1.5、测试
(1)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-04-json
。
(2)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。(详见5.1节)
(3)在子项目的web/WEB-INF
目录下新建一个html
包,并在该包下新建一个jsonTest.html
文件,用于显示ModelandView
中存放的数据,以及我们的正常页面。
jsonTest.html
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>阿汤笔迹</title> </head> <body> <script type="text/javascript"> var user = { name:"阿汤", age:18, sex:"男" };
var json = JSON.stringify(user); console.log(json); var obj = JSON.parse(json); console.log(obj); </script> </body> </html>
|
(4)在IDEA
中使用浏览器打开html
页面,F12
查看控制台输出!输出结果如下图所示:
13.2、通过Jackson返回JSON数据
13.2.1、常用的json分析处理工具
Jackson
是目前比较好用的json
分析处理工具。
- 当然还有其它的分析处理工具,如:阿里巴巴的
fastjson
等。
13.2.2、通过Jackson输出JSON对象
在上述springMVC-04-json
子项目的基础上:
(1)先确认在父项目的pom.xml
文件中导入父/子工程所需要的SpringMVC
相关的依赖。此外,我们需要导入Jackson
所依赖的jar
包。
pom.xml
文件:
1 2 3 4 5 6
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency>
|
**注:**为了确保项目发布时,jackson
的依赖包发布成功,我们需要再次手动将Maven
依赖包添加到输出跟目录下。(详见5.1节)
(2)同样地,在web.xml
文件中注册DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求。
(3)同样地,在该子项目的resources
目录下新建一个SpringMVC
的配置文件,配置文件的名称为:springmvc-servlet.xml
(即:[servletname]-servlet.xml
)。
(4)在java
目录下新建一个com.atangbiji.pojo
包,并在该包下新建一个User
实体类。
User.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.atangbiji.pojo;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @NoArgsConstructor @AllArgsConstructor
public class User { private int id; private String name; private int age; private String sex; }
|
(5)同样地,在该子项目的java
目录下新建一个com.atangbiji.controller
包(用于存放我们自己编写的控制器),并在该包下新建一个UserController
类。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.atangbiji.controller;
import com.atangbiji.pojo.User; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
@Controller public class UserController {
@RequestMapping("/json/test1") @ResponseBody public String json1() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); User user = new User(1,"阿汤", 18, "男"); String str = mapper.writeValueAsString(user); return str; } }
|
注:
@ResponseBody
注解是配合@Controller
注解使用的。
- 被**
@ResponseBody
注解**的方法不会被视图解析器解析,会直接返回一个字符串。
ObjectMapper
是Jackson
包中的对象映射器。
当然,我们也可以在输出字符串的控制器上直接使用@RestController
注解。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestController public class UserController {
@RequestMapping(value = "/json/test1") public String json1() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); User user = new User(1,"阿汤", 18, "男"); String str = mapper.writeValueAsString(user); return str; } }
|
注:被@RestController
注解的Controller
类,其中所有的方法都将只返回字符串。我们不用再给其中的每一个方法都添加@ResponseBody
注解了!在前后端分离开发中,一般都使用@RestController
注解,十分便捷!
(6)配置Tomcat服务器;deploy(发布)springMVC-04-json:war
包,并为该子项目添加虚拟路径映射:/mvc
。(参见《Servlet详解》)
(7)启动测试。在IDEA中启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/json/test1
,页面访问成功,但出现json
字符串中文乱码问题。访问结果如下:
13.2.3、解决中文乱码问题
(1)方法一:通过@RequestMaping
注解的produces
属性解决json
字符串乱码问题。
UserController.java
文件:
1 2
| @RequestMapping(value = "/json/test1",produces = "application/json;charset=utf-8")
|
重启Tomcat
,在浏览器中输入http://localhost:8080/mvc/json/test1
,页面访问成功,json
字符串中文乱码问题得到解决。访问结果如下:
**注:**如果项目中有许多请求,那么使用方法一解决乱码问题就比较麻烦,因为每一个请求都要需要我们手动添加一个produces
属性。
(2)方法二:通过SpringMVC配置统一解决乱码问题。(推荐)
我们可以在SpringMVC
的配置文件上添加一段StringHttpMessageConverter
消息转换配置!这样就不用每次都去处理json
字符串乱码问题了!
springmvc-servlet.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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>
|
13.2.4、通过Jackson输出JSON数组
在UserController
类中新增一个json2
方法。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package com.atangbiji.controller;
import com.atangbiji.pojo.User; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList;
@Controller public class UserController { @RequestMapping(value = "/json/test2") @ResponseBody public String json2() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); ArrayList<User> userList = new ArrayList<User>(); User user1 = new User(1,"阿汤", 18, "男"); User user2 = new User(2,"张三", 3, "男"); User user3 = new User(3,"李四", 18, "男"); userList.add(user1); userList.add(user2); userList.add(user3); String str = mapper.writeValueAsString(userList); return str; } }
|
重启Tomcat
,在浏览器中输入http://localhost:8080/mvc/json/test2
,页面访问成功。访问结果如下:
13.2.5、通过Jackson输出时间对象
在UserController
类中新增一个json3
方法。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.atangbiji.controller;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Date;
@Controller public class UserController { @RequestMapping(value = "/json/test3") @ResponseBody public String json3() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); Date date = new Date(); String str = mapper.writeValueAsString(date); return str; } }
|
重启Tomcat
,在浏览器中输入http://localhost:8080/mvc/json/test3
,页面访问成功。访问结果如下:
注:Jackson
默认会把时间转换成Timestamp
(时间戳)形式。(即:1970年1月1日到当前时间的毫秒数!)
若要正常输出时间,则需要取消Timestamp形式 , 自定义时间格式。在UserController
类中新增一个json4
方法。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package com.atangbiji.controller;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.text.SimpleDateFormat; import java.util.Date;
@Controller public class UserController { @RequestMapping(value = "/json/test4") @ResponseBody public String json4() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); mapper.setDateFormat(sdf);
Date date = new Date(); String str = mapper.writeValueAsString(date); return str; } }
|
重启Tomcat
,在浏览器中输入http://localhost:8080/mvc/json/test4
,页面访问成功。访问结果如下:
13.2.6、封装成工具类(重点)
在java
目录下新建一个com.atangbiji.utils
包,并在该包下新建一个JsonUtils
,用于封装Jackson
。
JsonUtils.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.atangbiji.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; } }
|
在UserController
类中新增一个json5
方法,并在其中使用我们封装的工具类。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.atangbiji.controller;
import com.atangbiji.utils.JsonUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Date;
@Controller public class UserController { @RequestMapping("/json/test5") @ResponseBody public String json5() { Date date = new Date(); String json = JsonUtils.getJson(date); return json; } }
|
重启Tomcat
,在浏览器中输入http://localhost:8080/mvc/json/test5
,页面访问成功。访问结果如下:
13.3、通过FastJson返回JSON数据
fastjson.jar
是阿里巴巴开发的一款专门用于Java
开发的包,使用它同样可以方便地实现json
对象与JavaBean
对象的转换。
13.3.1、fastjson三个主要的类及常用方法
13.3.2、fastjson的使用步骤
在上述springMVC-04-json
子项目的基础上:
(1)先确认在父项目的pom.xml
文件中导入父/子工程所需要的SpringMVC
相关的依赖。此外,我们需要导入fastjson
所依赖的jar
包。
pom.xml
文件:
1 2 3 4 5 6
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency>
|
**注:**为了确保项目发布时,fastjson
的依赖包发布成功,我们需要再次手动将Maven
依赖包添加到输出跟目录下。(详见5.1节)
(2)在UserController
类中新增一个fastjsonTest
方法。
UserController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.atangbiji.controller;
import com.alibaba.fastjson.JSON; import com.atangbiji.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList;
@Controller public class UserController { @RequestMapping("/fastjson/test") @ResponseBody public String fastjsonTest() { ArrayList<User> userList = new ArrayList<User>(); User user1 = new User(1,"阿汤", 18, "男"); User user2 = new User(2,"张三", 3, "男"); User user3 = new User(3,"李四", 18, "男"); userList.add(user1); userList.add(user2); userList.add(user3); String str = JSON.toJSONString(userList); return str; } }
|
重启Tomcat
,在浏览器中输入http://localhost:8080/mvc/fastjson/test
,页面访问成功。访问结果如下:
14、SSM框架整合
14.1、环境要求
IDEA 2020.2
MySQL 5.7.19
Tomcat 9.0.65
Maven 3.6.1
**注:**整合SSM框架,需要熟练掌握MySQL
数据库、JavaWeb
、Spring
、SpringMVC
、MyBatis
及简单的前端知识。
14.2、配置MyBatis(DAO层)
14.2.1、搭建数据库环境
使用SQL
创建一个名称为ssmbuild
的数据库,在其中创建一个用于存放书籍数据的books
表,并向其中插入测试数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| CREATE DATABASE `ssmbuild`;
USE `ssmbuild`;
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books` ( `bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id', `bookName` VARCHAR(100) NOT NULL COMMENT '书名', `bookCounts` INT(11) NOT NULL COMMENT '数量', `detail` VARCHAR(200) NOT NULL COMMENT '描述', KEY `bookID` (`bookID`) ) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES (1,'Java',1,'从入门到放弃'), (2,'MySQL',10,'从删库到跑路'), (3,'Linux',5,'从进门到进牢');
|
创建的数据库如下图所示:
14.2.2、创建普通的Maven项目
(1)在IDEA中创建一个普通的Maven项目。(具体步骤参考《Maven详解》),项目名称为ssmbuild
。
(2)删除系统自动生成的src
目录,所得到的空项目就是我们的Maven父项目,我们可以在其中创建多个子项目(即:Module
)。
(3)在父项目的pom.xml
文件中导入父/子工程需要的Maven依赖。
pom.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</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> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.10.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> </dependencies>
|
**注:**由于Maven的“约定大于配置”思想,我们的映射器配置文件*.xml
写在了项目中的Java
目录下,因此出现无法被导出或者无法生效的问题。该问题的解决方法是:在父项目(或子项目)的pom.xml
的<build>……</build>
中配置resources
,修改Maven约定的过滤条件,来防止我们的资源导出失败。
pom.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
|
(4)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为ssm-Demo01
。
(5)填写子项目名称和Maven项目GAV,点击Finish
按钮完成子项目创建,等待Maven依赖包导入完毕。
14.2.3、建立项目的基本结构和配置框架
(6)在上述子项目的src/main/java
目录下:
- 新建
com.atangbiji.pojo
包,用于存放实体类;
- 新建
com.atangbiji.dao
包,用于存放持久层代码(映射器接口及其对应的映射器配置文件);
- 新建
com.atangbiji.service
包,用于存放业务层代码;
- 新建
com.atangbiji.controller
包,用于存放控制器代码;
- 新建
com.atangbiji.utils
包,用于存放我们封装的各种工具。
(7)在上述子项目的src/main/resources
目录下:
-
新建MyBatis
的核心配置文件:mybatis-config.xml
。
mybatis-config.xml
文件:
1 2 3 4 5 6 7
| <?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>
</configuration>
|
-
新建Spring
的核心配置文件:applicationContext.xml
。
applicationContext.xml
文件:
1 2 3 4 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">
</beans>
|
14.2.4、配置Mybatis核心配置文件
(8)在该子项目的src/main/resources
目录下,新建一个db.properties
配置文件,用于配置数据库。
db.properties
文件:
1 2 3 4
| jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=123456
|
注:
- 数据源连接信息与使用IDEA连接数据库相同(参见《使用IDEA连接数据库》)。
- 如果我们使用的
MySQL
为8.0及以上版本,那么需要在jdbc.url
中增加时区的配置。
- 整合后,数据库配置文件不再在
MyBatis
核心配置文件中引入,而是变成在Spring
的核心配置文件中引入。
(9)在MyBatis
的核心配置文件中配置类型别名(typeAliases
)和映射器(mappers
)。
mybatis-config.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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> <typeAliases> <package name="com.atangbiji.pojo"/> </typeAliases> <mappers> <package name="com.atangbiji.dao"/> </mappers> </configuration>
|
14.2.5、创建实体类
(10)在子项目java
目录的com.atangbiji.pojo
包下新建一个Books
类,用于实现关系型数据库和业务实体间的映射。
Books.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.atangbiji.pojo;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @AllArgsConstructor @NoArgsConstructor public class Books { private int bookID; private String bookName; private int bookCounts; private String detail; }
|
**注:**实体类中的属性要与数据库表中的字段一一对应。
14.2.6、创建映射器接口
(11)在子项目java
目录的com.atangbiji.dao
包下新建一个BookMapper
映射器接口,然后再向其中添加映射器接口函数。
BookMapper.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atangbiji.dao;
import com.atangbiji.pojo.Books; import java.util.List;
public interface BookMapper { int addBook(Books book); int deleteBookById(int id); int updateBook(Books books); Books queryBookById(int id); List<Books> queryAllBook(); }
|
注:映射器接口中的接口函数用于映射XML
映射器配置文件中对应标签中的SQL
语句。
14.2.7、创建XML映射器配置文件
(12)在子项目java
目录的com.atangbiji.dao
包下新建一个映射器配置文件BookMapper.xml
,并在其中使用标签实现SQL
语句与映射器接口函数的映射。
BookMapper.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atangbiji.dao.BookMapper"> <insert id="addBook" parameterType="Books"> insert into ssmbuild.books(bookName,bookCounts,detail) values (#{bookName}, #{bookCounts}, #{detail}) </insert>
<delete id="deleteBookById" parameterType="int"> delete from ssmbuild.books where bookID=#{bookID} </delete>
<update id="updateBook" parameterType="Books"> update ssmbuild.books set bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail} where bookID = #{bookID} </update>
<select id="queryBookById" resultType="Books"> select * from ssmbuild.books where bookID = #{bookID} </select>
<select id="queryAllBook" resultType="Books"> SELECT * from ssmbuild.books </select> </mapper>
|
14.3、编写Service层的接口和实现类
(13)在子项目java
目录的com.atangbiji.service
包下新建一个BookService
业务层接口,然后再向其中添加相应的接口函数。
BookService.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atangbiji.service;
import com.atangbiji.pojo.Books; import java.util.List;
public interface BookService { int addBook(Books book); int deleteBookById(int id); int updateBook(Books books); Books queryBookById(int id); List<Books> queryAllBook(); }
|
(14)在子项目java
目录的com.atangbiji.service
包下新建一个BookServiceImpl
接口实现类,用于实现业务层接口。并在其中调用DAO层的映射器接口。
BookServiceImpl.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.atangbiji.service;
import com.atangbiji.dao.BookMapper; import com.atangbiji.pojo.Books; import java.util.List;
public class BookServiceImpl implements BookService {
private BookMapper bookMapper;
public void setBookMapper(BookMapper bookMapper) { this.bookMapper = bookMapper; }
public int addBook(Books book) { return bookMapper.addBook(book); }
public int deleteBookById(int id) { return bookMapper.deleteBookById(id); }
public int updateBook(Books books) { return bookMapper.updateBook(books); }
public Books queryBookById(int id) { return bookMapper.queryBookById(id); }
public List<Books> queryAllBook() { return bookMapper.queryAllBook(); } }
|
14.4、Spring整合MyBatis
(15)在子项目的resources
目录下新建一个spring
包,并在该包下新建一个spring-dao.xml
配置文件,用于Spring
整合Mybatis
的相关的配置【参见《Spring整合MyBatis》】。
spring-dao.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <?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:db.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/>
<property name="maxPoolSize" value="30"/> <property name="minPoolSize" value="10"/> <property name="autoCommitOnClose" value="false"/> <property name="checkoutTimeout" value="10000"/> <property name="acquireRetryAttempts" value="2"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="basePackage" value="com.atangbiji.dao"/> </bean> </beans>
|
注:
- 这里使用的数据源(数据库连接池)是
c3p0
,实际项目中我们可以根据需要,选择配置DBCP
、Druid
等其它数据源。
- 为了能够在
Spring
中使用Dao
接口,这里我们通过自动扫描Dao
接口包的方式,动态实现Dao
接口注入到spring
容器中。不再通过《Spring整合MyBatis》方式1和方式2中“先手动实现Dao
接口,然后再手动将Dao
接口的实现类注入到IOC
容器中”的方式实现Dao
接口注入。
14.5、Spring整合Service层
(16)在子项目resources
目录下的spring
包下新建一个spring-service.xml
配置文件,用于Spring
整合Service
层的相关的配置。
spring-service.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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:component-scan base-package="com.atangbiji.service"/> <bean id="BookServiceImpl" class="com.atangbiji.service.BookServiceImpl"> <property name="bookMapper" ref="bookMapper"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
|
14.6、Spring整合SpringMVC
(17)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。(详见5.1节)
(18)在web.xml
文件中:①注册DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求;②配置SpringMVC
的乱码过滤器;③配置Session
过期时间。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath: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>
<session-config> <session-timeout>15</session-timeout> </session-config> </web-app>
|
(19)在子项目resources
目录下的spring
包下新建一个spring-mvc.xml
配置文件,用于Spring
整合Spring MVC
的相关的配置。并在其中完成以下工作:
①在xml
配置文件的头部引入context
和MVC
约束。
②开启自动扫描包,让指定包下的注解生效,由IOC
容器统一管理。
③过滤静态资源,让Spring MVC
不处理静态资源(如:html
、css
、js
、mp3
、mp4
文件等)。
④开启SpringMVC
注解驱动,自动完成DefaultAnnotationHandlerMapping
和AnnotationMethodHandlerAdapter
实例的注入。
**注:**在spring
中一般采用@RequestMapping
注解来完成映射关系。要想使@RequestMapping
注解生效,必须向上下文中注册一个DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter
实例。这两个实例分别在类级别和方法级别处理注解。而annotation-driven
配置帮助我们自动完成上述两个实例的注入。
⑤配置视图解析器(前缀、后缀)。
spring-mvc.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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.atangbiji.controller"/> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
|
14.7、整合Spring
配置文件
(20)我们可以通过以下两种方式来将Spring
整合的各个配置文件添加到同一个ApplicationContext
下(建议两种方式同时配置)。
**方式一:**通过import
标签将多个xml
配置文件中的配置,合并为一个配置文件【参见《import(导入)》】。
applicationContext.xml
文件:
1 2 3 4 5 6 7 8 9 10
| <?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="classpath:/spring/spring-dao.xml"/> <import resource="classpath:/spring/spring-service.xml"/> <import resource="classpath:/spring/spring-mvc.xml"/> </beans>
|
**方式二:**通过IDEA
自动将多个Spring
配置文件添加到同一个ApplicationContext
下。具体操作步骤如下图所示:
此时,我们可以在Project Structure
中查看ApplicationContext
下的配置文件,如下图所示:
至此,SSM框架整合完毕!
15、第一次使用SSM框架
接下来,我们在上述整合后的SSM框架上实现具体的增删改查功能。
15.1、查询全部书籍
(1)在上述子项目的com.atangbiji.controller
包下,新建一个BookController
控制器类。
BookController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.atangbiji.controller;
import com.atangbiji.pojo.Books; import com.atangbiji.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List;
@Controller @RequestMapping("/book") public class BookController { @Autowired @Qualifier("BookServiceImpl") private BookService bookService;
@RequestMapping("/allBook") public String queryAllBooks(Model model){ List<Books> books = bookService.queryAllBook(); model.addAttribute("list",books); return "allBook"; } }
|
(2)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个allBook.jsp
文件,用于显示Model
中存放的数据,以及我们的正常页面。
allBook.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>书籍列表</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 引入Bootstrap美化界面 --> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <!--容器--> <div class="container"> <!--标题(清除浮动)--> <div class="row clearfix"> <!--屏幕栅格化(列12等份)--> <div class="col-md-12 column"> <div class="page-header"> <h1> <small>书籍列表 —— 显示所有书籍</small> </h1> </div> </div> </div> <!--新增按钮--> <div class="row"> <div class="col-md-4 column"> <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增</a> </div> </div> <!--书籍展示列表--> <div class="row clearfix"> <div class="col-md-12 column"> <table class="table table-hover table-striped"> <!--表头--> <thead> <tr> <th>书籍编号</th> <th>书籍名字</th> <th>书籍数量</th> <th>书籍详情</th> <th>操作</th> </tr> </thead> <!--表格主体(从Model的list属性中获取数据并显示)--> <tbody> <c:forEach var="book" items="${requestScope.get('list')}"> <tr> <td>${book.getBookID()}</td> <td>${book.getBookName()}</td> <td>${book.getBookCounts()}</td> <td>${book.getDetail()}</td> <td> <!--修改按钮(并将修改书籍的id传给后端的Controller)--> <a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> | <!--删除按钮(并将删除书籍的id传给后端的Controller)【RestFul风格】--> <a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a> </td> </tr> </c:forEach> </tbody> </table> </div> </div> </div> </body> </html>
|
(3)配置Tomcat服务器;deploy(发布)ssm-Demo01:war
包,并为该子项目添加虚拟路径映射:/ssm
。(参见《Servlet详解》)
(4)启动测试。在IDEA中启动Tomcat
,在浏览器中输入http://localhost:8080/ssm/book/allBook
,页面访问成功。访问结果如下:
15.2、新增书籍
(1)在上述BookController
控制器中添加"跳转到新增书籍页面"和“新增书籍”功能。
BookController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @RequestMapping("/toAddBook") public String toAddPage(){ return "addBook"; }
@RequestMapping("/addBook") public String addBook(Books book){ bookService.addBook(book); return "redirect:/book/allBook"; }
|
(2)在子项目的jsp
包下新建一个addBook.jsp
文件,用于显示新增书籍页面。
addBook.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>新增书籍</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 引入Bootstrap美化界面 --> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </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> <!--新增书籍信息表单--> <form action="${pageContext.request.contextPath}/book/addBook" method="post"> <div class="form-group"> <label>书籍名称</label> <input type="text" class="form-control" name="bookName" required> </div> <div class="form-group"> <label>书籍数量</label> <input type="text" class="form-control" name="bookCounts" required> </div> <div class="form-group"> <label>书籍详情</label> <input type="text" class="form-control" name="detail" required> </div> <!--添加按钮--> <button type="submit" class="btn btn-default">添加</button> </form> </div> </body> </html>
|
**注:**前端input
标签的name
属性的值必须与实体类的属性保存完全一致!
(3)启动测试。
-
在IDEA中重新启动Tomcat
,在浏览器中输入http://localhost:8080/ssm/book/allBook
,进入查询全部书籍的页面;
-
点击“新增”按钮,浏览器跳转至“新增书籍页面”。填写新增书籍的信息:
-
点击"添加"按钮,页面重定向到“查询全部书籍”的页面。如下图所示:
此时,数据库中也新增了一条记录。如下图所示:
15.3、修改书籍
(1)在上述BookController
控制器中添加"跳转到修改书籍页面"和“修改书籍信息”功能。
BookController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @RequestMapping("/toUpdateBook") public String toUpdatePage(int id,Model model){ Books books = bookService.queryBookById(id); model.addAttribute("bookInfo",books); return "updateBook"; }
@RequestMapping("/updateBook") public String updateBook(Books book){ bookService.updateBook(book); return "redirect:/book/allBook"; }
|
(2)在子项目的jsp
包下新建一个updateBook.jsp
文件,用于显示修改书籍页面。
updateBook.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>修改书籍</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 引入Bootstrap美化界面 --> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </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> <!--修改书籍信息表单--> <form action="${pageContext.request.contextPath}/book/updateBook" method="post"> <!--注:修改书籍信息需要传入bookID,否则数据库修改会失败--> <input type="hidden" name="bookID" value="${bookInfo.bookID}"> <div class="form-group"> <label>书籍名称</label> <input type="text" class="form-control" name="bookName" value="${bookInfo.bookName}" required> </div> <div class="form-group"> <label>书籍数量</label> <input type="text" class="form-control" name="bookCounts" value="${bookInfo.bookCounts}" required> </div> <div class="form-group"> <label>书籍详情</label> <input type="text" class="form-control" name="detail" value="${bookInfo.detail}" required> </div> <!--修改按钮--> <button type="submit" class="btn btn-default">修改</button> </form> </div> </body> </html>
|
**注:**修改书籍信息需要传入bookID
,否则数据库修改会失败。
(3)启动测试。
-
在IDEA中重新启动Tomcat
,在浏览器中输入http://localhost:8080/ssm/book/allBook
,进入查询全部书籍的页面;
-
选中C++
书籍,点击“更改”按钮,浏览器跳转至“修改书籍页面”。将C++
修改为:C
语言,书籍数量修改为:20。
-
点击"修改"按钮,页面重定向到“查询全部书籍”的页面。数据修改成功!如下图所示:
-
此时,数据库中的数据也修改成功。如下图所示:
15.4、删除书籍
(1)在上述BookController
控制器中添加“删除书籍”功能。
BookController.java
文件:
1 2 3 4 5 6 7 8
| @RequestMapping("/deleteBook/{bookId}") public String deleteBook(@PathVariable("bookId") int id){ bookService.deleteBookById(id); return "redirect:/book/allBook"; }
|
(2)启动测试。
15.5、小结及展望
到目前为止,这个SSM
项目整合已经完全的OK了,可以直接运行进行测试!这个示例十分的重要,大家需要保证,不看任何东西,自己也可以完整的实现出来!
此时项目结构如下图所示:
这个是我们的第一个SSM
整合案例,一定要烂熟于心!
SSM
框架的重要程度是不言而喻的,学到这里,大家已经可以进行基本网站的单独开发。但是这只是增删改查的基本操作。可以说学到这里,大家才算是真正的步入了后台开发的门。也就是能找一个后台相关工作的底线。
或许很多人,工作就做这些事情,但是对于个人的提高来说,还远远不够!
16、Ajax
16.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
用户界面。
16.2、jQuery.ajax
我们这里不去讲解纯JS
原生实现Ajax
,而是直接使用jquery
提供的Ajax
。这样方便学习和使用,避免重复造轮子,有兴趣的同学可以去了解下JS
原生XMLHttpRequest
。
Ajax
的核心是XMLHttpRequest
对象(XHR
)。XHR
为向服务器发送请求和解析服务器响应提供了接口,能够以异步方式从服务器获取新数据。
16.2.1、jQuery.ajax用法(重点)
-
通过jQuery Ajax
方法,我们能够使用HTTP Get
和HTTP Post
从远程服务器上请求文本、HTML
、XML
或JSON
, 我们同时能够把这些外部数据直接载入网页的被选元素中。
-
jQuery
不是生产者,而是大自然搬运工。
-
jQuery Ajax
的本质就是XMLHttpRequest
,只是jQuery
对其进行了封装,方便调用!
-
jQuery
提供多个与Ajax
有关的方法。最常用的就是$.ajax()
方法,其语法格式为:
主要参数:
-
url
:请求地址。
-
type
:请求方式。GET
、POST
(1.9.0之后用method
)
-
headers
:请求头。
-
data
:要发送的数据(键值对)。
**注:**前后端以“键值对”的形式进行数据传递,其中:键的名称需要与Controller
中传入的形参保持一致。
-
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"
将服务器端返回的内容转换成相应的json
对象;
"jsonp"
:JSONP
格式使用JSONP
形式调用函数时,如"myurl?callback=?"
,jQuery
将自动替换?
为正确的函数名,以执行回调函数。
16.2.2、搭建测试环境
具体实现步骤如下:
(1)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-05-ajax
。
(2)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。(详见5.1节)
(3)先确认在父项目的pom.xml
文件中导入父/子工程所需要的SpringMVC
相关的依赖。
(4)同样地,在web.xml
文件中:
- 注册
DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求。
- 配置
SpringMVC
的乱码过滤器。
(5)同样地,在该子项目的resources
目录下新建一个SpringMVC
的配置文件,配置文件的名称为:applicationContext.xml
。
并在其中完成以下工作:
①在xml
配置文件的头部引入context
和MVC
约束。
②开启自动扫描包,让指定包下的注解生效,由IOC
容器统一管理。
③过滤静态资源,让Spring MVC
不处理静态资源(如:html
、css
、js
、mp3
、mp4
文件等)。
④开启SpringMVC
注解驱动,自动完成DefaultAnnotationHandlerMapping
和AnnotationMethodHandlerAdapter
实例的注入。
**注:**在spring
中一般采用@RequestMapping
注解来完成映射关系。要想使@RequestMapping
注解生效,必须向上下文中注册一个DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter
实例。这两个实例分别在类级别和方法级别处理注解。而annotation-driven
配置帮助我们自动完成上述两个实例的注入。
⑤配置视图解析器(前缀、后缀)。
(6)在java
目录下新建com.atangbiji.controller
包,用于存放控制器代码。并在该包下新建一个AjaxController
控制器类。
AjaxController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@Controller @RequestMapping("/ajax") public class AjaxController {
@RequestMapping("/test") public String test(){ return "ajaxTest"; }
@RequestMapping("/a1") @ResponseBody public void a1(String name, HttpServletResponse response) throws IOException { response.setCharacterEncoding("utf-8");
if (name.equals("")){ response.getWriter().print("前端通过Ajax传入的名字为空!"); }else { response.getWriter().print("前端通过Ajax传入的名字为:"+ name + "(非空)"); } } }
|
(7)导入jQuery
。
方式一:使用在线的CDN
。
1
| <script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
|
方式二:下载后导入使用。
- 去
jQuery
官网下载jquery-3.6.3.js
库。
- 在子项目的
web
目录下新建一个static/js
目录,并通过右键菜单将static
标记为,然后将下载好的jquery-3.6.3.js
文件复制到该目录下。
- 在
ajaxTest.jsp
文件的head
标签中引入本地的jQuery
库。
1
| <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.3.js"></script>
|
**注:**我们这里使用方式二导入jquery
。
(8)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个ajaxTest.jsp
文件,用于显示Model
中存放的数据,以及我们的正常页面。
ajaxTest.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Ajax测试</title> <%--导入jQuery--%> <%--<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>--%> <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.3.js"></script> <script> function test1(){ $.post({ url:"${pageContext.request.contextPath}/ajax/a1", data:{'name':$("#txtName").val()}, success:function (data,status) { alert("后台响应的数据为:" + data + ";状态为:" + status); } }); } </script> </head> <body> <%--onblur:当失去焦点时,触发事件(向服务器发送一个请求)--%> 用户名:<input type="text" id="txtName" onblur="test1()"/> </body> </html>
|
(9)配置Tomcat服务器;deploy(发布)springMVC-05-ajax:war
包,并为该子项目添加虚拟路径映射:/mvc
。(参见《Servlet详解》)
(10)启动测试。在IDEA中启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/ajax/test
,页面访问成功。
**注:**类似于百度的搜索框,当我们在输入框中输入用户名的过程中,页面在不刷新的情况下也能异步向后台发送请求,并获取后台的响应!
16.2.3、Ajax异步加载json数据
在上述子项目的基础上:
(1)在java
目录下新建一个com.atangbiji.pojo
包,并在该包下新建一个User
实体类。
User.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.atangbiji.pojo;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @AllArgsConstructor @NoArgsConstructor public class User { private String name; private int age; private String sex; }
|
(2)在该子项目的AjaxController
控制器中添加“返回json
数据”的功能。
AjaxController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.atangbiji.controller;
import com.atangbiji.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.List;
@Controller @RequestMapping("/ajax") public class AjaxController { @RequestMapping("/test2") public String test2(){ return "ajaxTestJSON"; } @RequestMapping("/getUsers") @ResponseBody public List<User> getUsers(){ ArrayList<User> userList = new ArrayList<User>(); User user1 = new User("阿汤", 18, "男"); User user2 = new User("张三", 3, "男"); User user3 = new User("李四", 18, "男"); userList.add(user1); userList.add(user2); userList.add(user3); return userList; } }
|
注:
@ResponseBody
注解会自动将userList
转成json
格式返回。
- 我们也可以使用
Jackson
或FastJson
返回json
数据。
(3)在子项目的jsp
包下新建一个ajaxTestJSON.jsp
文件,并在其中使用Ajax
异步获取后端的json
数据。
ajaxTestJSON.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Ajax异步加载json数据</title> <%--导入jQuery--%> <%--<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>--%> <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.3.js"></script> <script> $(function () { $("#btn").click(function () { <%--使用Ajax异步获取后端的json数据--%> $.post("${pageContext.request.contextPath}/ajax/getUsers",function (data) { console.log(data); var html=""; for (var i = 0; i < data.length ; i++) { html+= "<tr>" + "<td>" + data[i].name + "</td>" + "<td>" + data[i].age + "</td>" + "<td>" + data[i].sex + "</td>" + "</tr>" }; $("#content").html(html); }); }) }) </script> </head> <body> <input type="button" id="btn" value="获取数据"/> <%--在表格中显示后端返回的json数据--%> <table width="80%" align="center"> <tr> <td>姓名</td> <td>年龄</td> <td>性别</td> </tr> <tbody id="content"> </tbody> </table> </body> </html>
|
(4)启动测试。在IDEA中重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/ajax/test2
,页面访问成功。访问结果如下:
16.2.4、Ajax实现输入提示效果
在上述子项目的基础上:
(1)在该子项目的AjaxController
控制器中添加“用户名和密码校验”的功能。
AjaxController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
@Controller @RequestMapping("/ajax") public class AjaxController { @RequestMapping("/test3") public String test3(){ return "login"; }
@RequestMapping("/checkNameAndPwd") @ResponseBody public String checkNameAndPwd(String name,String pwd){ String msg = ""; if (name!=null){ if ("admin".equals(name)){ msg = "OK"; }else { msg = "用户名输入错误"; } } if (pwd!=null){ if ("123456".equals(pwd)){ msg = "OK"; }else { msg = "密码输入有误"; } } return msg; } }
|
**注:**记得在SpringMVC
配置文件中统一处理字符串乱码问题。
applicationContext.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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>
|
(2)在子项目的jsp
包下新建一个login.jsp
文件,并在其中实现登录页面。
login.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Ajax实现输入提示效果</title> <%--导入jQuery--%> <%--<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>--%> <script src="${pageContext.request.contextPath}/static/js/jquery-3.6.3.js"></script> <script> function a1(){ $.post({ url:"${pageContext.request.contextPath}/ajax/checkNameAndPwd", data:{'name':$("#name").val()}, success:function (data) { if (data.toString()=='OK'){ $("#userInfo").css("color","green"); }else { $("#userInfo").css("color","red"); } $("#userInfo").html(data); } }); } function a2(){ $.post({ url:"${pageContext.request.contextPath}/ajax/checkNameAndPwd", data:{'pwd':$("#pwd").val()}, success:function (data) { if (data.toString()=='OK'){ $("#pwdInfo").css("color","green"); }else { $("#pwdInfo").css("color","red"); } $("#pwdInfo").html(data); } }); } </script> </head> <body> <p> <%--onblur:当失去焦点时,触发事件(向服务器发送一个请求)--%> 用户名:<input type="text" id="name" onblur="a1()"/> <%--提示信息--%> <span id="userInfo"></span> </p> <p> <%--onblur:当失去焦点时,触发事件(向服务器发送一个请求)--%> 密码:<input type="text" id="pwd" onblur="a2()"/> <%--提示信息--%> <span id="pwdInfo"></span> </p> </body> </html>
|
(3)启动测试。在IDEA中重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/ajax/test3
,页面访问成功。测试输入框的提示效果,访问结果如下:
17、拦截器
17.1、概述
SpringMVC
的处理器、拦截器的工作原理类似于Servlet
开发中的过滤器Filter
,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
**过滤器与拦截器的区别:**拦截器是AOP思想的具体应用。
过滤器(Filter):
servlet
规范中的一部分,在web.xml
文件中配置,任何java web
工程都可以使用。
- 在
url-pattern
标签中配置了/*
之后,可以对所有要访问的资源进行拦截。
拦截器(Interceptor):
- 拦截器是
SpringMVC
框架自己的,在SpringMVC
的配置文件中配置,只有使用了SpringMVC
框架的工程才能使用。
- 拦截器只会拦截访问控制器的方法, 如果访问的是
jsp/html/css/image/js
是不会进行拦截的。
17.2、自定义拦截器
若想要自定义拦截器,则必须实现HandlerInterceptor
接口。
具体实现步骤如下:
(1)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-06-interceptor
。
(2)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。(详见5.1节)
(3)先确认在父项目的pom.xml
文件中导入父/子工程所需要的SpringMVC
相关的依赖。
(4)同样地,在web.xml
文件中:
- 注册
DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求。
- 配置
SpringMVC
的乱码过滤器。
(5)同样地,在该子项目的resources
目录下新建一个SpringMVC
的配置文件,配置文件的名称为:applicationContext.xml
。
并在其中完成以下工作:
①在xml
配置文件的头部引入context
和MVC
约束。
②开启自动扫描包,让指定包下的注解生效,由IOC
容器统一管理。
③过滤静态资源,让Spring MVC
不处理静态资源(如:html
、css
、js
、mp3
、mp4
文件等)。
④开启SpringMVC
注解驱动,自动完成DefaultAnnotationHandlerMapping
和AnnotationMethodHandlerAdapter
实例的注入。
**注:**在spring
中一般采用@RequestMapping
注解来完成映射关系。要想使@RequestMapping
注解生效,必须向上下文中注册一个DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter
实例。这两个实例分别在类级别和方法级别处理注解。而annotation-driven
配置帮助我们自动完成上述两个实例的注入。
⑤配置视图解析器(前缀、后缀)。
(6)在java
目录下新建com.atangbiji.interceptor
包,用于存放拦截器代码。在该包下新建一个MyInterceptor
拦截器类,并在其中重写HandlerInterceptor
接口的方法。
MyInterceptor.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.atangbiji.interceptor;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("------------处理前------------"); return true; }
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("------------处理后------------"); }
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("------------清理------------"); } }
|
注:
preHandle
在请求处理的方法之前执行。若该方法返回true
,则执行下一个拦截器;若返回false
,则不再执行下一个拦截器。
- 我们一般在重写的
preHandle
方法中实现拦截功能,在postHandle
方法中实现拦截日志的功能。
(7)在springMVC
的配置文件中配置拦截器。
applicationContext.xml
文件:
1 2 3 4 5 6 7 8 9 10 11
| <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.atangbiji.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
|
(8)在java
目录下新建com.atangbiji.controller
包,用于存放控制器代码。并在该包下新建一个InterceptorController
控制器类。
InterceptorController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;
@Controller @RequestMapping("/interceptor") public class InterceptorController {
@RequestMapping("/test") @ResponseBody public String testFunction() { System.out.println("执行控制器(Controller)中的方法!"); return "控制器中的方法执行成功!"; } }
|
(9)配置Tomcat服务器;deploy(发布)springMVC-06-interceptor:war
包,并为该子项目添加虚拟路径映射:/mvc
。(参见《Servlet详解》)
(10)启动测试。在IDEA中启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/interceptor/test
,页面访问成功。
(11)若将拦截器preHandle
方法的返回值改为false
。即:
MyInterceptor.java
文件:
1 2 3 4 5
| public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("------------处理前------------"); return false; }
|
启动测试。在IDEA中重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/interceptor/test
,页面访问成功。
-
浏览器前端输出结果如下:
-
此时,服务器后台输出结果如下:
17.3、验证用户是否已经登录
在上述子项目的基础上:
(1)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个login.jsp
文件,用于用户登录页面。
login.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录页面</title> </head> <body> <form action="${pageContext.request.contextPath}/login/goLogin" method="post"> 用户名:<input type="text" name="username"> <br> 密码:<input type="password" name="pwd"> <br> <input type="submit" value="提交"> </form> </body> </html>
|
**注:**在WEB-INF
目录下的页面或资源,无法直接访问,只能通过Controller
或Servlet
进行访问。
(2)在jsp
包下新建一个success.jsp
文件,作为登录成功页面。
success.jsp
文件:
1 2 3 4 5 6 7 8 9 10
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>welcome</title> </head> <body> <h1>欢迎进入首页</h1> <a href="${pageContext.request.contextPath}/login/logout">注销</a> </body> </html>
|
(3)在com.atangbiji.controller
包下新建一个LoginController
控制器类,用于处理用户的登录请求。
LoginController
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession;
@Controller @RequestMapping("/login") public class LoginController {
@RequestMapping("/test") public String test(){ return "success"; }
@RequestMapping("/goLogin") public String login(HttpSession session, String username, String pwd) { session.setAttribute("username", username); session.setAttribute("password", pwd); return "redirect:/login/test"; }
@RequestMapping("/logout") public String logout(HttpSession session) { session.invalidate(); return "login"; } }
|
(4)在com.atangbiji.interceptor
包下新建一个LoginInterceptor
拦截器类,用于拦截所有的请求,判断用户是否登录。若session
中登录信息正确,则放行;否则,则跳转到登录页面。
LoginInterceptor.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.atangbiji.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getRequestURI().contains("goLogin")){ return true; }
HttpSession session = request.getSession(); String username = (String) session.getAttribute("username"); String password = (String) session.getAttribute("password");
if ("atang".equals(username) && "123456".equals(password)){ return true; }else { request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } } }
|
(5)在Springmvc
的配置文件中注册拦截器。
applicationContext.xml
文件:
1 2 3 4 5 6 7 8 9 10 11
| <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.atangbiji.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
|
(6)启动测试。
- 在IDEA中重新启动
Tomcat
,在浏览器中输入http://localhost:8080/mvc/login/test
,请求被拦截,由于用户未登录,页面跳转到登录页面。如下图所示:
- 只有当输入正确的用户名(
atang
)和密码(123456
)后,才能登录,并成功进入首页。如下图所示:
**注:**若点击“注销”按钮或关闭浏览器,则又需要重新登录才能成功进入首页。
18、文件上传和下载
18.1、文件上传
18.1.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
:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
1 2 3 4
| <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
的组件。
18.1.2、具体实现
具体实现步骤如下:
(1)在父项目中新建一个模块(Module
),并在其中创建一个普通的Maven子项目,项目名称为springMVC-07-file
。
(2)右键选择"Add FrameworkSupport
"菜单项,为该子项目添加Web Application
的支持!
注:如果我们的Java Web
应用是通过普通Maven
项目添加Web Application
支持的方式创建,那么当我们deploy
(发布)项目时,可能会出现输出目录无法生成lib
目录,进而无法导出依赖包的问题,此时页面显示404
错误。
- 出现该问题的原因在于:使用
Maven
模板创建MavenWeb
项目默认生成的Web
资源目录,与通过普通Maven
项目添加Web Application
支持的方式创建的Web
项目的Web
资源目录不相同。
- 解决方法:点击
File->Project Structure
菜单,打开项目结构目录,我们只需在其中将该项目的Web
资源目录修改正确,并手动将Maven
依赖包添加到输出跟目录下即可。(详见5.1节)
(3)先确认在父项目的pom.xml
文件中导入父/子工程所需要的SpringMVC
相关的依赖。此外,我们需要导入commons-fileupload
所依赖的jar
包。
pom.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
|
注:
- 为了确保项目发布时,
jackson
的依赖包发布成功,我们需要再次手动将Maven
依赖包添加到输出跟目录下。(详见5.1节)
- 记得注掉低版本的
servlet-api
依赖。
(4)同样地,在web.xml
文件中:
- 注册
DispatcherServlet
(中心控制器),并通过DispatcherServlet
(中心控制器)拦截浏览器的所有请求。
- 配置
SpringMVC
的乱码过滤器。
(5)同样地,在该子项目的resources
目录下新建一个SpringMVC
的配置文件,配置文件的名称为:applicationContext.xml
。
并在其中完成以下工作:
①在xml
配置文件的头部引入context
和MVC
约束。
②开启自动扫描包,让指定包下的注解生效,由IOC
容器统一管理。
③过滤静态资源,让Spring MVC
不处理静态资源(如:html
、css
、js
、mp3
、mp4
文件等)。
④开启SpringMVC
注解驱动,自动完成DefaultAnnotationHandlerMapping
和AnnotationMethodHandlerAdapter
实例的注入。
**注:**在spring
中一般采用@RequestMapping
注解来完成映射关系。要想使@RequestMapping
注解生效,必须向上下文中注册一个DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter
实例。这两个实例分别在类级别和方法级别处理注解。而annotation-driven
配置帮助我们自动完成上述两个实例的注入。
⑤配置视图解析器(前缀、后缀)。
(6)在SpringMVC
的配置文件中配置bean
:multipartResolver
。
applicationContext.xml
文件:
1 2 3 4 5 6 7 8
| <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"/> <property name="maxUploadSize" value="10485760"/> <property name="maxInMemorySize" value="40960"/> </bean>
|
注意:这个bena的id必须为:multipartResolver ,否则上传文件会报400的错误!在这里栽过坑,教训!
(7)在java
目录下新建com.atangbiji.controller
包,用于存放控制器代码。并在该包下新建一个FileController
控制器类。
FileController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.commons.CommonsMultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.*;
@Controller @RequestMapping("/file") public class FileController {
@RequestMapping("/test") public String test(){ return "upload"; }
@RequestMapping(value = "/upload",produces = "application/json;charset=utf-8") @ResponseBody public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
String uploadFileName = file.getOriginalFilename();
if ("".equals(uploadFileName)){ return "文件名为空,请返回上传页面重新选择上传文件!"; } 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 "文件上传成功!"; } }
|
附:CommonsMultipartFile 的常用方法
String getOriginalFilename()
:获取上传文件的原名。
InputStream getInputStream()
:获取文件流。
void transferTo(File dest)
:将上传文件保存到一个目录文件中。
(8)在子项目的web/WEB-INF
目录下新建一个jsp
包,并在该包下新建一个upload.jsp
文件,用于文件上传页面。
upload.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>文件上传</title> </head> <body> <form action="${pageContext.request.contextPath}/file/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"> </form> </body> </html>
|
(9)配置Tomcat服务器;deploy(发布)springMVC-07-file:war
包,并为该子项目添加虚拟路径映射:/mvc
。(参见《Servlet详解》)
(10)启动测试。在IDEA中启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/file/test
,文件上传页面访问成功。选择要上传的文件后,点击“upload
”按钮。如下图所示:
浏览器页面返回“文件上传成功!”,如下图所示:
与此同时,文件上传目录中出现已上传的文件,如下图所示:
**注:**我们也可以采用file.Transto
来保存上传的文件。
FileController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
@RequestMapping(value = "/upload2",produces = "application/json;charset=utf-8") @ResponseBody 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);
file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
return "文件上传成功!"; }
|
18.2、文件下载
18.2.1、文件下载步骤
(1)设置response
响应头
(2)读取文件 – InputStream
(3)写出文件 – OutputStream
(4)执行操作
(5)关闭流 (先开后关)
18.2.2、具体实现
具体实现步骤如下:
(1)在FileController
控制器中,添加文件下载的功能。
FileController.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.atangbiji.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder;
@Controller @RequestMapping("/file") public class FileController {
@RequestMapping("/downloadTest") public String downloadTest(){ return "download"; }
@RequestMapping(value="/download") public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{ String path = request.getServletContext().getRealPath("/upload"); String fileName = "下载测试.png";
response.reset(); 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); InputStream input=new FileInputStream(file); OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024]; int index=0; while((index= input.read(buff))!= -1){ out.write(buff, 0, index); out.flush(); } out.close(); input.close(); return null; } }
|
(2)上传一个"下载测试.png"的图片到文件上传目录。
(3)在jsp
包下新建一个download.jsp
文件,用于文件下载页面。
download.jsp
文件:
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>文件下载</title> </head> <body> <a href="${pageContext.request.contextPath}/file/download">点击下载</a> </body> </html>
|
(4)在IDEA中重新启动Tomcat
,在浏览器中输入http://localhost:8080/mvc/file/downloadTest
,文件下载页面访问成功。点击“点击下载
”按钮,文件下载成功。如下图所示:
(本讲完,系列博文持续更新中…… )
关注**“阿汤笔迹”** 微信公众号,获取更多学习笔记。
原文地址:http://www.atangbiji.com/2023/01/05/SpringMVCInDetail
博主最新文章在个人博客 http://www.atangbiji.com/ 发布。