什么是SpringMVC
是基于spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的。可以理解成是servlet的一个升级。
SpringMVC能够创建对象, 放入到容器中(SpringMVC容器), springmvc容器中放的是控制器对象。
web开发底层是servlet, springmvc中有一个对象是Servlet : DispatherServlet(中央调度器)。DispatherServlet负责接收用户的所有请求, 用户把请求给了DispatherServlet, 之后DispatherServlet把请求转发给我们的Controller对象, 最后是Controller对象处理请求。
DispatcherServlet叫做中央调度器, 是一个servlet , 它的父类是继承HttpServlet。
DispatcherServlet也叫做前端控制器( front controller )调用其它的控制器对象(@Controller所注解的类)
Dispatcherservlet负责接收用户提交的请求,并把请求的处理结果显示给用户
我们要做的是使用@Contorller创建控制器对象, 把对象放入到springmvc容器中, 把创建的对象作为控制器使用这个控制器对象能接收用户的请求, 显示处理结果,就相对于是把@Contorller注解的类当成是servlet使用。
但要注意的是,使用@Controller注解创建的是一个普通类的对象, 不是Servlet。只不过是 springmvc赋予了控制器对象一些额外的功能。
index.jsp-----DispatherServlet(Servlet)----转发,分配给---Controller对象(@Controller注解创建的对象)
什么是MVC?
它是一种开发模式,它是模型视图控制器的简称.所有的web应用都是基于MVC模式的开发.
M:模型层,包含实体类,业务逻辑层,数据访问层
V:视图层,html,javaScript,vue等都是视图层,用来显现数据
C:控制器,它是用来接收客户端的请求,并返回响应到客户端的组件,Servlet就是组件
SpringMVC框架的优点
1)轻量级,基于MVC的框架
2)易于上手,容易理解,功能强大
3)它具备IOC和AOP
4)完全基于注解开发
SpringMVC优化的方向
![](https://img-blog.csdnimg.cn/img_convert/4f87a21a0ff80f120b1f6f13c7ee1934.png)
SpringMVC执行的流程
![](https://img-blog.csdnimg.cn/img_convert/bdb198da799f82f9a69e36a284f76da5.png)
基于注解的SpringMVC框架开发的步骤
(1)、新建项目,选择webapp模板(不选择quickStart)
(2)、修改目录,添加缺失的目录,并修改目录属性
![](https://img-blog.csdnimg.cn/img_convert/d36455587595c4513abfc79cc5700f60.png)
其中,WEB-INF在webapp目录下。
(3)、修改pom.xml文件,添加SpringMVC的依赖,添加Servlet的依赖(SpringMVC的依赖会间接把spring的依赖也都加入到项目)(因为由maven来管依赖,所以在pom文件中编写)。同时也要添加Servlet依赖,不然没有JavaX或Jakarta类的方法可用。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>springmvc_001_demo</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--springMVC的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--添加servlet的依赖,web应用程序必须有servlet的支持-->
<!--同时也要添加Servlet依赖,不然没有JavaX或Jakarta类的方法可用。-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
<build>
<finalName>springmvc</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>
(4)、添加springmvc.xml配置文件,指定包扫描,添加视图解析器。(暂时不添加视图解析器)
1)声明组件扫描器,指定@contorller注解所在的包名
2)声明视图解析器。帮助处理视图的。
位置:
![](https://img-blog.csdnimg.cn/img_convert/983661ce4c777e450567b7c10bb390c9.png)
<?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.bjpowernode.controller"></context:component-scan>
</beans>
(5)、删除web.xml文件,新建web.xml(idea自己创建的web.xml版本较低)(web.xml中如果是web-app_4_0.xsd则不需要更改)
![](https://img-blog.csdnimg.cn/img_convert/87f2f573c8d2ab3929a4e34e55e9a41a.png)
![](https://img-blog.csdnimg.cn/img_convert/1ab4327f0e643c33126713132f5e4e5a.png)
必须在新建的时候修改web.xml的前缀名,不然新建的还是低版本的xml,最后再重命名所生成的xml文件为web.xml即可。
(6)、在web.xml文件中注册springmvc框架的核心对象DispatcherServlet(所有的web请求都是基于servlet的)
DispatcherServlet叫做中央调度器, 是一个servlet , 它的父类是继承HttpServlet。
DispatcherServlet也叫做前端控制器( front controller )调用其它的控制器对象(@Controller所注解的类)
Dispatcherservlet负责接收用户提交的请求,并把请求的处理结果显示给用户
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--声明,注册springmvc的核心对象DispatcherServlet
并且我们需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
为什么要创建DispatcherServlet对象的实例呢?
因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,读取springmvc的配置文件,
把这个配置文件中的对象都创建好后, 当用户发起请求时就可以直接使用对象了。
因此<load-on-startup>1</load-on-startup>指定创建优先级
但启动tomcat报错,因为无法读取这个文件 /WEB-INF/springmvc-servlet.xml,
更改名字为将servlet-name更改为myweb,又会报无法读取/WEB-INF/myweb-servlet.xml错误
因为springmvc创建容器对象时,读取的配置文件的位置默认是/WEB-INF/<servlet-name>-servlet.xml .
因此,我们需要用<init-param>指定SpringMVC配置文件的位置
servlet的初始化会执行init()方法。 DispatcherServlet在init()中{
//创建容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
//把容器对象放入到ServletContext中
getServletContext().setAttribute(key, ctx);
}
这样我们才能在@Controller所注解的类中使用ctx这个对象来对请求进行处理
-->
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置-->
<init-param>
<!--springmvc的配置文件的位置的属性-->
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--在tomcat启动后,创建Servlet对象
load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
tomcat创建对象的时间越早。 大于等于0的整数。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<!--
使用框架的时候, url-pattern可以使用两种值
1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
表示以do或以action或以mvc结尾的路径均交给这个servlet管理,但不能使用 *.jsp
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用斜杠 "/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
(7)、在webapp目录下创建一个发起请求的页面 index.jsp。
![](https://img-blog.csdnimg.cn/img_convert/eb80a24d95840f92858200ea1a0313fc.png)
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>第一个springmvc项目</p>
<p><a href="some.do">发起some.do的请求</a> </p>
</body>
</html>
(8)、开发控制器(处理器)类,它是一个普通的类。
1)在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中
2)在类中的方法上面加入@RequestMapping注解。
package com.bjpowernode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import javax.xml.ws.RequestWrapper;
/**
* @Controller:创建处理器对象,对象放在springmvc容器中。
* 位置:在类的上面
* 能处理请求的都是控制器(处理器): MyController能处理请求,被称为后端控制器(back controller)
*/
@Controller
public class MyController {
/*
处理用户提交的请求,springmvc中是使用方法来处理的。
方法是自定义的, 可以有多种返回值, 多种参数,方法名称自定义
*/
/**
* 准备使用doSome方法处理some.do请求。
* @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
* 一个请求指定一个方法处理。
* 属性: 1. value 是一个String数组,表示请求的uri地址的(some.do)。
* value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”为开头
* 位置:1.在方法的上面,常用的。
* 2.在类的上面
* 说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
* 使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost
*
* 返回值:ModelAndView 表示本次请求的处理结果
* Model: 数据,请求处理完成后,要显示给用户的数据
* View: 视图, 比如jsp等等。
*/
@RequestMapping(value = {"/some.do","/first.do"})
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
//添加数据, 框架在请求的最后把数据放入到request作用域。
//request.setAttribute("msg","欢迎使用springmvc做web开发");
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的是doSome方法");
//指定视图, 指定视图的完整路径
//框架对视图执行的forward操作(转发操作), request.getRequestDispather("/show.jsp).forward(...)
mv.setViewName("/show.jsp");
// 返回mv
return mv;
// 在该方法结束后,将运行到show.jsp中,我们可以在show.jsp中对mv.addObject添加的数据进行使用。
}
}
(9)、在webapp目录下创建一个作为结果的jsp,显示请求的处理结果。
![](https://img-blog.csdnimg.cn/img_convert/eb80a24d95840f92858200ea1a0313fc.png)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/view/show.jsp从request作用域获取数据</h3><br/>
<h3>msg数据:${msg}</h3><br/>
<h3>fun数据:${fun}</h3>
</body>
</html>
(10)、添加tomcat进行测试功能
![](https://img-blog.csdnimg.cn/img_convert/44e3abc93a8ac23fee49a62914ccce04.png)
springmvc请求的处理流程
1)发起some.do
2)tomcat查看web.xml,通过url-pattern标签知道 *.do的请求给中央调度器DispatcherServlet
3)DispatcherServlet根据springmvc.xml的组件扫描器以及doSome()方法的@RequestMapping注解知道 some.do请求对应doSome()方法
4)DispatcherServlet把some.do转发给MyController.doSome()方法
5)框架执行doSome()把得到ModelAndView进行处理, 转发到show.jsp
上面的过程简化的方式
some.do请求---DispatcherServlet---MyController
![](https://img-blog.csdnimg.cn/img_convert/b6e60ca5af683a3100b2d753deb15249.png)
WEB-INF的保护机制
在当前项目中index.jsp和show.jsp都放在了webapp目录下,如果用户知道该show.jsp的访问地址,就可以通过直接输入地址跳过index.jsp文件直接到达show.jsp。但因为并没有经过MyController的处理,servlet没执行,所以没有数据。
![](https://img-blog.csdnimg.cn/img_convert/69efd9cdd4800113b3c9145c206b2eda.png)
但我们应该避免这种情况。
因此,对于这种要保护的jsp,我们都会放到WEB-INF目录下,因为WEB-INF目录下的所有内容都是不能直接访问的,只能通过servlet的转发才能访问。
重定向不能访问到WEB-INF目录下的资源,因为重定向是客户端的,而转发是服务端内部的。
重定向是让客户端去访问重定向的地址,而WEB-INF下的文件是不能通过外部访问的!
另外,通常不会直接放在WEB-INF目录下,因为一个项目受保护的jsp可能很多,所以一般是在WEB-INF下新建一个包并把需要保护的jsp放到新建的包中。
![](https://img-blog.csdnimg.cn/img_convert/b6f9d8c5a63c48fd08e6e94266497b2a.png)
我们需要更改一下控制器类:
package com.bjpowernode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import javax.xml.ws.RequestWrapper;
@Controller
public class MyController {
@RequestMapping(value = {"/some.do","/first.do"})
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的是doSome方法");
// 改变
mv.setViewName("/WEB-INF/view/show.jsp");
return mv;
}
}
注意,index.jsp一般不放在WEB-INF目录下,因为他是网站的首页。
视图解析器
因为在一个项目中,我们把大量的视图文件(jsp文件)都放到了WEB-INF目录下的某一个包中,我们在编写控制器类的代码时就需要大量编写mv.setViewName("/WEB-INF/view/show.jsp");中jsp的路径,因此我们可以设置视图解析器,编写路径的前缀和后缀,我们只需要编写路径的主要名称,至于前缀后缀由视图解析器为我们编写。
更改后的springmvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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.bjpowernode.controller" />
<!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<!--注意/WEB-INF/view/前后都有斜杠-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
</beans>
这样一来,代码可省略为:mv.setViewName("show");
详解@RequestMapping
RequestMapping是请求映射,作用是把一个请求地址和一个方法绑定在一起,让一个请求指定一个方法处理。
属性:
value 是一个String数组,表示请求的uri地址的。value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”为开头
位置:
1.在方法的上面,常用的。
2.在类的上面
说明:
使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost。
上面案例已经演示过修饰方法的作用,再次不再赘述。
当RequestMapping修饰类时
package com.bjpowernode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import javax.xml.ws.RequestWrapper;
/**
* @RequestMapping:
* value : 所有请求地址的公共部分,叫做模块称
* 位置: 放在类的上面
*/
@Controller
@RequestMapping(value = "/test")
public class MyController {
@RequestMapping(value = {"/some.do","/first.do"})
// 等价于@RequestMapping(value = {"/test/some.do","/test/first.do"})
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的是doSome方法");
// 改变
mv.setViewName("/WEB-INF/view/show.jsp");
return mv;
}
}
通过在类上面定义@RequestMapping,可以指定类中方法请求路径前面的公共部分,简化代码。
与此同时,如果该类中的方法的请求路径如果有统一的前缀改动,也会非常方便。
其余属性
method属性, 表示请求的方式。 它的值RequestMethod类枚举值。
例如表示get请求方式, RequestMethod.GET
post方式, RequestMethod.POST
对于get请求的方法不使用get方式访问,前端会报:HTTP Status 405 - Request method 'GET' not supported 错误,反之同理。
![](https://img-blog.csdnimg.cn/img_convert/8ce8c44fb18aa34ce6e639358520d824.png)
不指定请求方式,则访问没有限制。
控制器类方法的参数
处理器方法可以包含以下七类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
1、HttpServletRequest
2、HttpServletResponse
3、HttpSession
4、Model
5、Map
6、ModelMap
7、请求中所携带的请求参数
注意:Map、Model、ModelMap和request一样,都使用请求作用域进行数据传递.所以服务器端的跳转必须是请求转发。
自动赋值的参数
控制器类:
package com.bjpowernode.controller;
import com.bjpowernode.pojo.Users;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;
/**
*
*/
@Controller
public class DataAction {
@RequestMapping("/data")
public String data(HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
Model model,
Map map,
ModelMap modelMap){
//做一个数据,传到main.jsp页面上
Users u = new Users("张三",22);
//传递数据
request.setAttribute("requestUsers",u);
session.setAttribute("sessionUsers",u);
model.addAttribute("modelUsers",u);
map.put("mapUsers",u);
modelMap.addAttribute("modelMapUsers",u);
return "main"; //请求转发方式跳转
}
}
main.jsp:
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2022/2/28
Time: 17:20
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>main...........显示数据</h2>
requestUsers:${requestUsers}<br>
sessionUsers:${sessionUsers}<br>
modelUsers:${modelUsers}<br>
mapUsers:${mapUsers}<br>
modelMapUsers:${modelMapUsers}<br>
从index.jsp页来来的数据也可以读:${param.name}
</body>
</html>
如果采用重定向,return "redirect:/admin/main.jsp"; 那么只有session域的数据能拿到。
接收用户提交的参数
接收用户提交的参数有三种方法:
1、逐个接收
2、对象接收
3、动态占位符提交(少用)
逐个接收示例
要求: 处理器(控制器)方法的形参名和请求中参数名必须一致。从而让框架把同名的请求参数赋值给同名的形参。
控制器类MyController中:
@Controller
public class MyController {
@RequestMapping(value = "/receiveproperty.do")
public ModelAndView doSome(String name, int age){
System.out.println("doSome, name="+name+" age="+age);
//可以在方法中直接使用 name , age
//处理some.do请求了。 相当于service调用处理完成了。
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",Integer.valueOf(age));
//show是视图文件的逻辑名称(文件名称)
mv.setViewName("show");
return mv;
}
}
index.jsp中:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>提交参数给Controller</p>
<form action="receiveproperty.do" method="post">
姓名:<input type="text" name="name"> <br/>
年龄:<input type="text" name="age"> <br/>
<input type="submit" value="提交参数">
</form>
</body>
</html>
show.jsp中:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/view/show.jsp从request作用域获取数据</h3><br/>
<h3>myname数据:${myname}</h3><br/>
<h3>myage数据:${myage}</h3>
<h3>student数据:${mystudent}</h3>
</body>
</html>
请求参数名和处理器方法的形参名一致的情况下,可以在控制器类直接作为形参使用,且类型可以改变,由SpringMVC进行类型转换。
框架的大致工作流程:
1. 使用request对象接收请求参数
String strName = request.getParameter("name");
String strAge = request.getParameter("age");
2. springmvc框架通过 DispatcherServlet 调用 MyController的doSome()方法
调用方法时,按名称对应,把接收的参数赋值给形参
doSome(strName,Integer.valueOf(strAge))
框架会提供类型转换的功能,能把String转为 int ,long , float, double等类型。
上述代码中,如果用户端在index.jsp界面输入请求参数时,name为空并不会引发什么错误,只不过是界面在展示数据的时候会显示不出来,但如果年龄为空,那么会浏览器报400错误,因为SpringMVC在底层会调用方法对age进行类型转换,想转换为int类型,但age为空所以会引发异常。
另外,字符串转数字等操作也会报400异常。
400状态码是客户端错误, 表示提交请求参数过程中,发生了问题。
但后端并不会抛异常,只会记录错误日志:
![](https://img-blog.csdnimg.cn/img_convert/ae3c302eba9cd788859a0eddcaec9bc1.png)
注意,如果某个值不是必填项,允许前端传递为空,但也存在不为空的情况,那我们可以把这个值的类型换成包装类型,包装类型允许NULL串。
@Controller
public class MyController {
@RequestMapping(value = "/receiveproperty.do")
public ModelAndView doSome(String name, Integer age){
}
}
Post请求的乱码问题/Spring自带的字符编码过滤器
注意:
在提交请求参数时,get请求方式中文没有乱码问题,使用post方式提交请求,中文可能有乱码问题。(响应也可能存在乱码问题)
以前,我们会在doGet或doPost代码中,使用request.setCharacterEncoding("UTF-8");来解决Post请求的乱码问题。但如果在控制类中,我们每个方法都使用这串代码来更改字符编码,那会有很多重复冗余的代码。
@Controller
public class MyController {
@RequestMapping(value = "/doSome.do")
public ModelAndView doSome(String name, Integer age){
request.setCharacterEncoding("UTF-8");
......
}
@RequestMapping(value = "/doOther.do")
public ModelAndView doOther(String name, Integer age){
request.setCharacterEncoding("UTF-8");
......
}
}
因此我们可以在过滤器中进行该操作。JavaWeb中我们学过过滤器,SpringMVC也为我们提供了过滤器(CharacterEncodingFilter类)。
SpringMVC的过滤器非常方便,只需要在web.xml文件中配置即可:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--声明,注册springmvc的核心对象DispatcherServlet
需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
为什么要创建DispatcherServlet对象的实例呢?
因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,
读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起
请求时就可以直接使用对象了。
servlet的初始化会执行init()方法。 DispatcherServlet在init()中{
//创建容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
//把容器对象放入到ServletContext中
getServletContext().setAttribute(key, ctx);
}
启动tomcat报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)
springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml .
-->
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置-->
<init-param>
<!--springmvc的配置文件的位置的属性-->
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--在tomcat启动后,创建Servlet对象
load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
tomcat创建对象的时间越早。 大于等于0的整数。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<!--
使用框架的时候, url-pattern可以使用两种值
1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
不能使用 *.jsp
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用斜杠 "/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--注册声明过滤器,解决post请求乱码的问题-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置项目中使用的字符编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<!--强制请求对象(HttpServletRequest)使用encoding编码的值-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制应答对象(HttpServletResponse)使用encoding编码的值-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--
/*:表示强制所有的请求先通过过滤器处理。
斜杠 / 表示根目录,表示访问所有文件都经过这个过滤器
-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
@RequestParam注解
逐个接收参数时,前端变量名与后端控制器类中方法的形参名不一致的情况下,控制器类的方法是没办法取到值的,我们可以使用@RequestParam注解来指定参数的对应关系。
前端:
<p>请求参数名和处理器方法的形参名不一样</p>
<form action="receiveparam.do" method="post">
姓名:<input type="text" name="rname"> <br/>
年龄:<input type="text" name="rage"> <br/>
<input type="submit" value="提交参数">
</form>
后端控制器类中的方法:
/**
* 请求中参数名和处理器方法的形参名不一样
* @RequestParam: 逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
* 属性: 1. value 请求中的参数名称
* 2. required 是一个boolean,默认是true
* true:表示请求中必须包含此参数。
* 位置: 在处理器方法的形参定义的前面
*/
@RequestMapping(value = "/receiveparam.do")
public ModelAndView receiveParam(@RequestParam("rname") String name,
@RequestParam("rage") Integer age){
System.out.println("doSome, name="+name+" age="+age);
//可以在方法中直接使用 name , age
//处理some.do请求了。 相当于service调用处理完成了。
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
//show是视图文件的逻辑名称(文件名称)
mv.setViewName("show");
return mv;
}
以上代码如果前端在发送请求时没有属性没有给值(值为NULL),那么会导致400错误,因为@RequestParam注解默认会对所修饰的变量进行检测,检测其是否存在值。、
如果该值不是必须的,那么我们可以使用@RequestParam注解的required属性来控制。false表示该参数不是必须包含的。
/**
* 请求中参数名和处理器方法的形参名不一样
* @RequestParam: 逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
* 属性: 1. value 请求中的参数名称
* 2. required 是一个boolean,默认是true
* true:表示请求中必须包含此参数。
* 位置: 在处理器方法的形参定义的前面
*/
@RequestMapping(value = "/receiveparam.do")
public ModelAndView receiveParam(@RequestParam(value = "rname",required = false) String name,
@RequestParam(value = "rage",required = false) Integer age){
System.out.println("doSome, name="+name+" age="+age);
//可以在方法中直接使用 name , age
//处理some.do请求了。 相当于service调用处理完成了。
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
//show是视图文件的逻辑名称(文件名称)
mv.setViewName("show");
return mv;
}
使用对象接收参数
当浏览器提交过来的参数太多时,如果采用逐个接收的方式则需要写非常多的形参,或者我们想让形参直接用对象封装起来,这时我们可以采用对象接受数据。
index.jsp:
<form action="receiveobject.do" method="post">
姓名:<input type="text" name="name"> <br/>
年龄:<input type="text" name="age"> <br/>
<input type="submit" value="提交参数">
</form>
控制器类:
/**
* 处理器方法形参是java对象, 这个对象的属性名和请求中参数名一样的
* 框架会创建形参的java对象, 给属性赋值。 请求中的参数是name,框架会调用setName()
* @return
*/
@RequestMapping(value = "/receiveobject.do")
public ModelAndView receiveParam(Student myStudent){
System.out.println("receiveParam, name="+myStudent.getName()+" age="+myStudent.getAge());
//可以在方法中直接使用 name , age
//处理some.do请求了。 相当于service调用处理完成了。
ModelAndView mv = new ModelAndView();
mv.addObject("myname",myStudent.getName());
mv.addObject("myage",myStudent.getAge());
mv.addObject("mystudent",myStudent);
//show是视图文件的逻辑名称(文件名称)
mv.setViewName("show");
return mv;
}
框架会调用实体类的无参数构造方法,并调用Set方法给属性赋值。
另外,如果存在:
/**
* 处理器方法形参是java对象, 这个对象的属性名和请求中参数名一样的
* 框架会创建形参的java对象, 给属性赋值。 请求中的参数是name,框架会调用setName()
* @return
*/
@RequestMapping(value = "/receiveobject.do")
public ModelAndView receiveParam(Student myStudent,School mySchool,String sex){
}
框架会根据同名的原则依次给形参赋值。如果处理器方法有2个形参对象,并且两个对象的属性名相同,则前端请求参数的值会同时赋值给两个对象的属性。
@RequestParam注解在使用对象接收中不适用。
动态占位符提交(少用)
动态占位符提交仅限于超链接或地址拦提交数据。
它是“一杠一值,一杠一大括号,使用注解@PathVariable来解析”。
超链接:
<a href="${pageContext.request.contextPath}/some/张三/22.do">动态提交</a>
控制器类方法:
@RequestMapping("/some/{uname}/{uage}")
public String three(
@PathVariable("uname") ===>用来解析路径中的请求参数
String name,
@PathVariable("uage")
int age){
System.out.println("name="+name+",age="+age);
return "main";
}
控制器类方法的返回值
使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型:
第一种: ModelAndView
第二种: String
第三种:无返回值 void
第四种:返回自定义类型对象
根据不同的情况,选择使用不同的返回值。
返回 ModelAndView
若处理器方法处理完后,需要跳转到其它资源(forwork转发、重定向),且又要在跳转的资源间传递数据(往域里存取数据),此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义ModelAndView 对象。
若在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。
返回 String
返回逻辑视图名称:
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。
springmvc.xml:
<!--声明组件扫描器-->
<context:component-scan base-package="com.bjpowernode.controller" />
<!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
<!--没有注解驱动-->
<mvc:annotation-driven />
控制器类:
@RequestMapping(value = "/returnString-view.do")
public String doReturnView(HttpServletRequest request,String name, Integer age){
return "show";
}
直接return逻辑名称show即可跳转到/WEB-INF/view/show.jsp界面,前提是配置了视图解析器。
此时的show界面并不能得到任何数据。当然,控制器类可以手动向域中添加数据,但这样还不如使用ModelAndView作为返回值了:
@RequestMapping(value = "/returnString-view.do")
public String doReturnView(HttpServletRequest request,String name, Integer age){
System.out.println("doReturnView, name="+name+" age="+age);
//可以自己手工添加数据到request作用域
request.setAttribute("myname",name);
request.setAttribute("myage",age);
// show : 逻辑视图名称,项目中配置了视图解析器
// 框架对视图执行forward转发操作
return "show";
}
返回完整路径:
返回完整路径就不能配置视图解析器,否则视图解析器会对完整的路径再次实施路径前后缀的拼接,路径会发生错误,会报404错误,资源找不到。
//处理器方法返回String,表示完整视图路径, 此时不能配置视图解析器
@RequestMapping(value = "/returnString-view2.do")
public String doReturnView2(HttpServletRequest request,String name, Integer age){
// 完整视图路径,项目中不能配置视图解析器
// 框架对视图执行forward转发操作
return "/WEB-INF/view/show.jsp";
}
返回 void(了解)
这个void的返回类型,应用不是很大, 一般用于ajax请求返回的结果。
目前我们响应ajax请求的时候,都是通过response.getwriter().print() 的形式输出到浏览器上,因此ajax请求没有需要servlet返回的数据,如果也不需要跳转到某个视图,那么就可以返回void。
但是 Springmvc返回ajax又有更好的方法 , 所以这个 void 只相当于老的 servlet中返回数据给ajax , 在springmvc中 基本不用它就可以, 我们这里只说一说就可以了。
void 要和 HttpServletResponse 相结合使用。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
$(function(){
$("button").click(function(){
//alert("button click");
$.ajax({
//url:"returnVoid-ajax.do",
//url:"returnStudentJsonArray.do",
url:"returnStringData.do",
data:{
name:"zhangsan",
age:20
},
type:"post",
dataType:"json",
success:function(resp){
//resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}
//jquery会把字符串转为json对象, 赋值给resp形参。
alert(resp.name + " "+resp.age);
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
//处理器方法返回void, 响应ajax请求
@RequestMapping(value = "/returnVoid-ajax.do")
public void doReturnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {
System.out.println("===doReturnVoidAjax====, name="+name+" age="+age);
//处理ajax, 使用json做数据的格式
//service调用完成了, 使用Student表示处理结果
Student student = new Student();
student.setName("张飞同学");
student.setAge(28);
String json = "";
//把结果的对象转为json格式的数据
if( student != null){
// 使用jackson组件,相对于fastjson
ObjectMapper om = new ObjectMapper();
json = om.writeValueAsString(student);
System.out.println("student转换的json===="+json);
}
//输出数据,响应ajax的请求
response.setContentType("application/json;charset=utf-8");
PrintWriter pw = response.getWriter();
pw.println(json);
pw.flush();
pw.close();
}
手工实现ajax返回json数据的代码有重复的部分:
1. java对象转为json;
2. 通过HttpServletResponse输出json数据
可以交给框架去做,即返回Object
返回Object
返回Object的情况一般是处理ajax操作,相对于返回void,返回Object更方便快捷,代码无千篇一律的部分。
如String , Integer , Map,List, Student等等都是对象,对象有属性, 属性就是数据。 所以返回Object表示数据, 和视图无关。
现在做ajax, 主要使用json的数据格式。
实现步骤:
1.加入处理json的工具库的依赖, springmvc默认使用的jackson。
2.在sprigmvc配置文件之间加入 <mvc:annotation-driven> 注解驱动。
相对于 json = om.writeValueAsString(student);
3.在处理器方法的上面加入@ResponseBody注解
相对于 response.setContentType("application/json;charset=utf-8");
PrintWriter pw = response.getWriter();
pw.println(json);
![](https://img-blog.csdnimg.cn/img_convert/6d327792def571c7929ead7dfbb4dff0.png)
springmvc处理器方法返回Object, 可以转为json输出到浏览器,响应ajax的内部原理:
1. <mvc:annotation-driven> 注解驱动。
注解驱动实现的功能是 完成java对象到json,xml, text,二进制等数据格式的转换。
<mvc:annotation-driven>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口的7个实现类对象, 包括 MappingJackson2HttpMessageConverter (该实现类会使用jackson工具库中的ObjectMapper实现java对象转为json字符串)
![](https://img-blog.csdnimg.cn/img_convert/7eebbd3316991b6808e7f5b206370fcc.png)
HttpMessageConverter接口:消息转换器。
功能:定义了java转为json,xml等数据格式的方法。 这个接口有很多的实现类。这些实现类完成 java对象到json, java对象到xml,java对象到二进制数据的转换
HttpMessageConverter接口包含以下两个方法:
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)
对于以下处理器方法
@RequestMapping(value = "/returnString.do")
public Student doReturnView2(HttpServletRequest request,String name, Integer age){
Student student = new Student();
student.setName("lisi");
student.setAge(20);
return student;
}
1)canWrite作用检查处理器方法的返回值,能不能转为var2表示的数据格式。
检查student(lisi,20)能不能转为var2表示的数据格式。如果检查能转为json,canWrite返回true
MediaType:表示数据格式, 例如json, xml等等
2)write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串。
json = om.writeValueAsString(student);
2. @ResponseBody注解
放在处理器方法的上面, 通过HttpServletResponse输出数据,响应ajax请求的。
相对于:
PrintWriter pw = response.getWriter();
pw.println(json);
pw.flush();
pw.close();
示例:
/**
* 处理器方法返回一个Student,通过框架转为json,响应ajax请求
* @ResponseBody:
* 作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器。
* 位置:方法的定义上面。 和其它注解没有顺序的关系。
* 返回对象框架的处理流程:
* 1. 框架会把返回Student类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
* 检查哪个HttpMessageConverter接口的实现类能处理Student类型的数据--MappingJackson2HttpMessageConverter
*
* 2.框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法
* 把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json
* contentType: application/json;charset=utf-8
*
* 3.框架会调用@ResponseBody把结果数据输出到浏览器, ajax请求处理完成
*/
@RequestMapping(value = "/returnStudentJson.do")
@ResponseBody
public Student doStudentJsonObject(String name, Integer age) {
//调用service,获取请求结果数据 , Student对象表示结果数据
Student student = new Student();
student.setName("李四同学");
student.setAge(20);
return student; // 会被框架转为json
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
$(function(){
$("button").click(function(){
//alert("button click");
$.ajax({
//url:"returnVoid-ajax.do",
//url:"returnStudentJsonArray.do",
url:"returnStringData.do",
data:{
name:"zhangsan",
age:20
},
type:"post",
dataType:"json",
success:function(resp){
//jquery会把字符串转为json对象, 赋值给resp形参。
alert(resp.name + " "+resp.age);
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
返回Object的时候,如果返回的是一个对象数组,那么就会响应给前端一个json数组:
/**
* 处理器方法返回List<Student>
* 返回对象框架的处理流程:
* 1. 框架会把返回List<Student>类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
* 检查那个HttpMessageConverter接口的实现类能处理Student类型的数据--MappingJackson2HttpMessageConverter
*
* 2.框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法
* 把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json array
* contentType: application/json;charset=utf-8
*
* 3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
*/
@RequestMapping(value = "/returnStudentJsonArray.do")
@ResponseBody
public List<Student> doStudentJsonObjectArray(String name, Integer age) {
List<Student> list = new ArrayList<>();
//调用service,获取请求结果数据 , Student对象表示结果数据
Student student = new Student();
student.setName("李四同学");
student.setAge(20);
list.add(student);
student = new Student();
student.setName("张三");
student.setAge(28);
list.add(student);
return list;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
$(function(){
$("button").click(function(){
//alert("button click");
$.ajax({
//url:"returnVoid-ajax.do",
//url:"returnStudentJsonArray.do",
url:"returnStringData.do",
data:{
name:"zhangsan",
age:20
},
type:"post",
dataType:"json",
success:function(resp){
//jquery会把字符串转为json对象, 赋值给resp形参。
// [{"name":"李四同学","age":20},{"name":"张三","age":28}]
$.each(resp,function(i,n){
alert(n.name+" "+n.age)
})
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
返回Object的情况下返回String:
如果存在@ResponseBody注解,那么返回String的时候框架会认为我们返回的是数据,否则会认为我们返回的是视图。
返回String时前端接收到的是一个普通的文本数据。此时前端的ajax请求如果dataType:"json" ,即希望后端返回json,如果返回的是一个不可以转化成json的字符串,那么前端就会报错。
去掉dataType:"json",相对于dataType:"text",但此时浏览器默认不会采用UTF-8的字符编码去解析这个普通字符串,会出现乱码。
![](https://img-blog.csdnimg.cn/img_convert/15dbf63aa84a5d0c60c62833b9336eb1.png)
可以在@RequestMapping中添加produces属性解决这个问题。
/**
* 处理器方法返回的是String , String表示数据的,不是视图。
* 区分返回值String是数据,还是视图,看有没有@ResponseBody注解
* 如果有@ResponseBody注解,返回String就是数据,反之就是视图
*
* 默认使用“text/plain;charset=ISO-8859-1”作为contentType,导致中文有乱码,
* 解决方案:给RequestMapping增加一个属性 produces, 使用这个属性指定新的contentType
*
* 返回对象框架的处理流程:
* 1. 框架会把返回String类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
* 检查那个HttpMessageConverter接口的实现类能处理String类型的数据--StringHttpMessageConverter
*
* 2.框架会调用实现类的write(), StringHttpMessageConverter的write()方法
* 把字符按照指定的编码处理 text/plain;charset=ISO-8859-1
*
* 3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
*/
@RequestMapping(value = "/returnStringData.do",produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData(String name,Integer age){
return "Hello SpringMVC 返回对象,表示数据";
}
注入日期(浏览器->服务器)与日期显示(服务器->浏览器)需要专门处理
日期提交的最优做法是让用户选而不是让用户写,因为写的情况太多太复杂了,什么格式都有。
![](https://img-blog.csdnimg.cn/img_convert/04182bea4727d98efdb4d52dea2d0504.png)
日期是不能用上述方式让SpringMVC逐个接收或对象接收的!除了日期其他都可以。
index.jsp:
<form action="${pageContext.request.contextPath}/mydate.action">
日期:<input type="date" name="mydate"><br>
<input type="submit" value="提交">
</form>
控制器类:
@RequestMapping("/mydate")
public String mydate(Date mydate){
return "show";
}
这种方式是不能获得Date的。
日期变量的注入处理
A.单个日期处理
要使用注解@DateTimeFormat,此注解必须搭配springmvc.xml文件中的<mvc:annotationdriven标签>
index.jsp:
<form action="${pageContext.request.contextPath}/mydate.action">
日期:<input type="date" name="mydate"><br>
<input type="submit" value="提交">
</form>
控制器类:
@RequestMapping("/mydate")
public String mydate(
@DateTimeFormat(pattern = "yyyy-MM-dd") // 这个是接收的格式,直接输出还得设置格式
Date mydate){
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(mydate);
System.out.println(sf.format(mydate)); // 修改输出格式并输出
return "show";
}
springmvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--添加包扫描-->
<context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
<!--添加视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/admin/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--添加注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
![](https://img-blog.csdnimg.cn/img_convert/680876dc1aecf4026721bedc9efceae2.png)
B.类中全局日期处理
如果采用A的方法,对每一个日期变量都单独处理,那么如果一个方法的形参有多个Date类型的变量那么就需要多个注解,如果不同方法都需要多个Date类型的变量那所需要的注解就会更多。
所以这时我们可以采用全局的日期处理。即注册一个注解,用来解析本类中所有的日期类型,自动转换。
public class MyDateAction {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
//注册一个全局的日期处理注解
@InitBinder
public void initBinder(WebDataBinder dataBinder){
// 注册一个自定义的转换器 (需要转成的类型) (转成的格式,是否容许为空)
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
}
@RequestMapping("/mydate")
public String mydate(Date mydate){
System.out.println(mydate);
System.out.println(sf.format(mydate));
return "show";
}
}
用这种方式不用添加<mvc:annotation-driven></mvc:annotation-driven>驱动。
含日期属性的对象的注入处理
User实体类:
package com.bjpowernode.pojo;
public class Users {
private String name;
private int age;
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Users(String name, int age) {
this.name = name;
this.age = age;
}
public Users() {
}
}
控制器方法:
@RequestMapping("/mydate")
public String mydate(User user){
return "show";
}
如果控制器方法接收的参数是含Date类型变量的对象,那么如果我们想正确接收到数据,必须在实体类的Date属性或或该Date属性的set方法上添加@DateTimeFormat(pattern = "yyyy-MM-dd"),或采用全局日期处理的方式@InitBinder。
JSP日期的显示处理
如果在控制器类中:
public class MyDateAction {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
//注册一个全局的日期处理注解
@InitBinder
public void initBinder(WebDataBinder dataBinder){
// 注册一个自定义的转换器 (需要转成的类型) (转成的格式,是否容许为空)
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
}
@RequestMapping("/mydate")
public String mydate(Date mydate){
request.setAttribute("mydate",sf.format(mydate));
return "show";
}
}
这样可以对单个日期对象进行处理,即直接转为好看的格式化的字符串返回前端进行显示。
但如果是list中的实体类对象的成员变量是日期类型,我们无法改变其类型,如果直接传Date类型到前端并且前端不加以处理,那么日期数据就不能格式化显示,如下图、代码。
<table width="800px" border="1">
<tr>
<th>姓名</th>
<th>生日</th>
</tr>
<c:forEach items="${list}" var="stu">
<tr>
<td>${stu.name}</td>
<td>${stu.birthday}</td>
</tr>
</c:forEach>
</table>
![](https://img-blog.csdnimg.cn/img_convert/d162734228524bc109f556bafc7542c7.png)
因此,要在页面上显示好看的日期,必须在jsp中使用JSTL对Date数据进行格式化显示。
步骤:
A)添加依赖jstl
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
B)在页面上导入标签库
<%--导入jstl核心标签库--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--导入jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
C)使用标签显示数据
<table width="800px" border="1">
<tr>
<th>姓名</th>
<th>生日</th>
</tr>
<c:forEach items="${list}" var="stu">
<tr>
<td>${stu.name}</td>
<td><fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate></td>
</tr>
</c:forEach>
</table>
JSON日期的显示处理
需要在类中的成员变量或成员变量的 getXXX 方法上加注解
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
Date date;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date getDate() {
return date;
}
中央调度器的url-pattern设置为“/"
分析以下发起的请求是由哪些服务器程序处理的:
http://localhost:8080/ch05_url_pattern/index.jsp :tomcat(jsp会转为servlet)
http://localhost:8080/ch05_url_pattern/js/jquery-3.4.1.js : tomcat
http://localhost:8080/ch05_url_pattern/images/p1.jpg : tomcat
http://localhost:8080/ch05_url_pattern/html/test.html: tomcat
http://localhost:8080/ch05_url_pattern/some.do : DispatcherServlet(springmvc框架处理的)
tomcat本身能处理静态资源的访问, 像html, 图片, js文件都是静态资源
tomcat的web.xml文件有一个servlet 名称是 default , 在服务器启动时创建的。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern> 表示静态资源和未映射的请求都这个default处理
</servlet-mapping>
default这个servlet作用:
1、处理静态资源
2、处理未映射到其它servlet的请求。
先前我们的web.xml文件<url-pattern>采用的是*.do的方式:
<?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>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置-->
<init-param>
<!--springmvc的配置文件的位置的属性-->
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--在tomcat启动后,创建Servlet对象
load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
tomcat创建对象的时间越早。 大于等于0的整数。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<!--
使用框架的时候, url-pattern可以使用两种值
1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
表示以do或以action或以mvc结尾的路径均交给这个servlet管理,但不能使用 *.jsp
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用斜杠 "/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
倘若我们把<url-pattern>*.do</url-pattern>换成<url-pattern>/</url-pattern>,它会替代 tomcat中的default:
<?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>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
这会导致所有的静态资源都给DispatcherServlet处理, 默认情况下DispatcherServlet没有处理静态资源的能力。没有控制器对象能处理静态资源的访问。会导致静态资源(html,js,图片,css)都是404。动态资源some.do是可以访问的,因为我们程序中有MyController控制器对象,能处理some.do请求。
解决方法:
因为如果我们采用<url-pattern>/</url-pattern>的形式,那么所有的超链接以及控制器类的中的路径内容都不用写.do,开发比较方便。但又会带来静态资源无法访问的问题。SpringMVC为此提供了两个解决方案。
方案一:使用<mvc:default-servlet-handler/>
springmvc.xml中:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--声明组件扫描器-->
<context:component-scan base-package="com.bjpowernode.controller" />
<!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
<!-- default-servlet-handler 和 @RequestMapping注解 有冲突, 需要加入annotation-driven 解决问题-->
<mvc:annotation-driven />
<!--第一种处理静态资源的方式:
需要在springmvc配置文件加入 <mvc:default-servlet-handler>
原理是: 加入这个标签后,框架会创健控制器对象DefaultServletHttpRequestHandler(类似我们自己创建的MyController).
DefaultServletHttpRequestHandler这个对象可以把接收的请求转发给 tomcat的default这个servlet。
-->
<mvc:default-servlet-handler />
</beans>
default-servlet-handler 和 @RequestMapping注解 有冲突, 需要加入annotation-driven 解决问题。
方案二:使用<mvc:resources/>(掌握)
使用<mvc:default-servlet-handler/>是将静态资源的请求转发给服务器,这就要求服务器有default这个servlet,但好处是方便。
在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。并且添加了<mvc:resources/>标签,专门用于解决静态资源无法访问问题。这种方式运用得更多,独立性更强。
<?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.bjpowernode.controller" />
<!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
<mvc:resources mapping="/images/**" location="/images/" />
<mvc:resources mapping="/html/**" location="/html/" />
<mvc:resources mapping="/js/**" location="/js/" />
<!--mvc:resources和@RequestMapping有一定的冲突-->
<mvc:annotation-driven />
</beans>
第二种处理静态资源的方式mvc:resources 加入后框架会创建 ResourceHttpRequestHandler这个处理器对象,让这个对象处理静态资源的访问,不依赖tomcat服务器。
mapping:访问静态资源的uri地址(请求路径), 使用通配符 **
location:静态资源在你的项目中的目录位置。
通过观察下列地址:
http://localhost:8080/ch05_url_pattern/index.jsp :tomcat(jsp会转为servlet)
http://localhost:8080/ch05_url_pattern/js/jquery-3.4.1.js : tomcat
http://localhost:8080/ch05_url_pattern/images/p1.jpg : tomcat
http://localhost:8080/ch05_url_pattern/html/test.html: tomcat
http://localhost:8080/ch05_url_pattern/some.do : DispatcherServlet(springmvc框架处理的)
我们可以发现,图片我们都会放到images包下,html文件我们会放到html包下。
我们可以使用mapping来指定这些静态资源请求路径:
mapping="/images/**" 包括 images/p1.jpg , images/user/logo.gif , images/order/history/list.png
location="/images/" 第一个/表示根目录,第二个/表示images是一个目录,二者不可或缺。
联合起来表示以/images开头的都去/images/目录下找静态资源。
mvc:resources和@RequestMapping也存在一定的冲突,需要<mvc:annotation-driven />注解驱动。
但如果静态资源很多,我们可以把静态资源都放到一个static目录下,这样只需要配置一个 mvc:resources 即可。
![](https://img-blog.csdnimg.cn/img_convert/2b5017734bf59ce3802a6c3edffa9ee5.png)
<!--使用一个配置语句,指定多种静态资源的访问-->
<mvc:resources mapping="/static/**" location="/static/" />
绝对路径与相对路径
jsp的页面路径中,有时候需要以/开头,有时候又不需要,那么什么时候需要什么时候不需要呢?
在jsp , html中使用的地址, 都是在前端页面中的地址,都是相对地址。
地址分类:
1.绝对地址 , 带有协议名称的是绝对地址, http://www.baidu.com , ftp://202.122.23.1
2.相对地址, 没有协议开头的, 例如 user/some.do , /user/some.do
相对地址不能独立使用,必须有一个参考地址。 通过参考地址+相对地址本身才能指定资源。
3.参考地址
在http://localhost:8080/ch06_path/index.jsp地址中,路径:http://localhost:8080/ch06_path/,资源: index.jsp。
1)在index.jsp点击 user/some.do超链接(访问地址不加 "/"),访问的是: http://localhost:8080/ch06_path/user/some.do,开始路径是当前路径http://localhost:8080/ch06_path/,参考地址是项目访问地址。
2)在index.jsp点击/user/some.do超链接(访问地址加 "/"),访问的是:http://localhost:8080/user/some.do,参考地址是服务器地址(不含项目名)。
以/开头要注意的问题
如果在超链接中,想要以/开头并且能跳转到正确的some.do地址,必须
<a href="/ch06_path/user/some.do">发起some.do请求</a>
但这样就会写死了,因为如果在配置Tomcat时项目的访问地址不设为/ch06_path,那么就必须修改jsp代码。因此我们可以采用EL表达式:
<a href="${pageContext.request.contextPath}/user/some.do">发起some.do请求</a>
${pageContext.request.contextPath} 返回“/项目访问地址”
另,idea的项目访问地址设置:
![](https://img-blog.csdnimg.cn/img_convert/1555eb350b2f3f79be496ebdd58e88e2.png)
不以/开头要注意的问题
当在http://localhost:8080/ch06_path/index.jsp中跳转到http://localhost:8080/ch06_path/user/some.do后,参考地址会变,由原来的http://localhost:8080/ch06_path/变成http://localhost:8080/ch06_path/user/。
如果我们在http://localhost:8080/ch06_path/user/some.do的视图界面(jsp),点击user/other.do的超链接,会跳转到http://localhost:8080/ch06_path/user/user/other.do ,而不是http://localhost:8080/ch06_path/user/other.do 。因为参考地址变了。
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>第一个springmvc项目</p>
<p><a href="user/some.do">发起请求</a> </p>
<br/>
</body>
</html>
控制器doSome方法:
@RequestMapping(value = "/user/some.do")
public ModelAndView doSome(){
mv.setViewName("/next.jsp");
// setViewName是转发操作,不会改变地址,地址依旧是http://localhost:8080/ch06_path/user/some.do
return mv;
}
next.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p><a href="user/other.do">发起请求</a>
</body>
</html>
控制器doOther方法:
@RequestMapping(value = "/user/other.do")
public ModelAndView doSome(){
mv.setViewName("/next222222.jsp");
// setViewName是转发操作,不会改变地址,地址依旧是http://localhost:8080/ch06_path/user/some.do
return mv;
}
以上代码无法正常执行doOther方法,因为代码运行到next.jsp时,参考地址变了,不再是http://localhost:8080/ch06_path/,而是http://localhost:8080/ch06_path/user/ 。
解决方法(使用base标签):
base标签是html语言中的标签。 表示当前页面中访问地址的基地址。
如果你的页面中所有没有以“/”开头的地址,则都是以base标签中的地址为参考地址。
即使用base中的地址 + user/some.do、base中的地址 + user/other.do 组成新的访问地址。
代码改进:
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<base href="<%=basePath%>" />
</head>
<body>
<p>第一个springmvc项目</p>
<p><a href="user/some.do">发起user/some.do的get请求</a> </p>
<br/>
</body>
</html>
next.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<base href="<%=basePath%>" />
</head>
<body>
<p><a href="user/other.do">发起user/some.do的get请求</a>
</body>
</html>
但这样base标签就写死了,我们可以动态获取base标签内的路径:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<title>Title</title>
<base href="<%=basePath%>" />
</head>
<body>
<p>第一个springmvc项目</p>
<p><a href="user/some.do">发起user/some.do的get请求</a> </p>
<br/>
</body>
</html>
控制器类方法的四种跳转方式
本质上还是两种跳转:请求转发和重定向。
SpringMVC衍生出四种,分别是“请求转发页面”,“请求转发action”,“重定向页面”,“重定向action”。
其中,action为路径后缀名,类似于/some.do的do。
源码追踪:
![](https://img-blog.csdnimg.cn/img_convert/7550d2d64dc7c75eb2fc4cd8751616ce.png)
![](https://img-blog.csdnimg.cn/img_convert/e5703c7e68db82794bcb87f42d6104b3.png)
![](https://img-blog.csdnimg.cn/img_convert/e455b4fc593b7422425ff7fd0e51beb0.png)
UrlBaseViewResolver类提供了两大常量,分别表示转发和重定向。
请求转发不会改变地址栏的地址,重定向会改变地址栏的地址。
forward:转发 redirect:重定向
代码示例:
SpringMVC.xml视图解析器:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--添加包扫描-->
<context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
<!--添加视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/admin/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<br><br><br>
<br>
<a href="${pageContext.request.contextPath}/one.action">1.请求转发页面(默认)</a><br><br>
http://localhost:8080/index.jsp 向 /one.action 发起请求,
/one.action 转发到/main.jsp,
地址栏最终还是/one.action,因为是转发操作不是重定向
<a href="${pageContext.request.contextPath}/two.action">2.请求转发action</a><br><br>
http://localhost:8080/index.jsp 向 /two.action 发起请求,
/two.action 转发到 /other.action,
/other.action 又转发到/main.jsp,
地址栏最终还是/two.action,因为是转发操作不是重定向
<a href="${pageContext.request.contextPath}/three.action">3.重定向页面</a><br><br>
http://localhost:8080/index.jsp 向 /three.action 发起请求,
/three.action 重定向到/main.jsp,
地址栏最终是/main.jsp,因为是重定向
<a href="${pageContext.request.contextPath}/four.action">4.重定向action</a><br><br>
http://localhost:8080/index.jsp 向 /four.action 发起请求,
/four.action 重定向到/other.action,
/other.action 又转发到/main.jsp,
地址栏最终是/other.action
<a href="${pageContext.request.contextPath}/five.action">5.随便跳页面</a><br><br>
</body>
</html>
控制器类(JumpAction):
package com.bjpowernode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
*/
@Controller
public class JumpAction {
@RequestMapping("/one")
public String one(){
System.out.println("这是请求转发页面跳转.........");
return "main"; //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
}
@RequestMapping("/two")
public String two(){
System.out.println("这是请求转发action跳转.........");
// forward: 这组字符串可以屏蔽前缀和后缀的拼接.实现请求转发跳转
// 虽然/other.action是以/开头,但并不会从服务器访问地址开始,因为有forward标志,springmvc会自动处理
return "forward:/other.action"; //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
}
@RequestMapping("/three")
public String three(){
System.out.println("这是重定向页面.......");
//redirect: 这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转
return "redirect:/admin/main.jsp";
}
@RequestMapping("/four")
public String four(){
System.out.println("这是重定向action.......");
//redirect: 这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转
return "redirect:/other.action";
}
@RequestMapping("/five")
public String five(){
System.out.println("这是随便跳.......");
return "forward:/fore/login.jsp";
}
}
对于/two:如果直接return "/other.action",视图解析器不会闲着,会把路径解析成 /admin/ /other.action .jsp。
对于/five:因为配置了视图解析器,如果直接return"login"会转发到/admin/login.jsp,但如果想跳转到/fore/login.jsp,就不能只写逻辑名称,需要把webapp下的完整目录写上。(不明白可以看下面的目录)
注意:无论是转发至action还是重定向至action,都要加action后缀!!记住,只要是跳转到控制器类,无论是在控制器类内开始跳,还是在jsp的超链接中开始跳,路径后都要加后缀action。
控制器类(OtherAction):
package com.bjpowernode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
*/
@Controller
public class OtherAction {
@RequestMapping("/other")
public String other(){
System.out.println("这是other的action访问 .............");
return "main";
}
}
目录:
![](https://img-blog.csdnimg.cn/img_convert/82060e31c1585eb7eaad79a244650911.png)
<mvc:annotation-driven/>标签的使用
<mvc:annotation-driven/>会自动注册两个bean,分别为DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。是springmvc为@controller分发请求所必须的。除了注册了这两个bean,还提供了很多支持。
1)支持使用ConversionService 实例对表单参数进行类型转换;
2)支持使用 @NumberFormat 、@DateTimeFormat;
3)注解完成数据类型的格式化;
4)支持使用 @RequestBody 和 @ResponseBody 注解;
5)静态资源的分流也使用这个标签;
拦截器
虽然把静态资源放在WEB-INF目录下就可以拦截对静态资源的直接访问,但是还是可以直接用资源地址访问action并由action转发到静态资源,所以依旧也不安全。
SpringMVC的拦截器是针对请求和响应进行的额外的处理。在请求和响应的过程中添加预处理、后处理和最终处理。
拦截器执行的时机
1、preHandle():在请求被处理之前进行操作,预处理
2、postHandle():在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果,后处理
3、afterCompletion:所有的请求响应结束后执行善后工作,清理对象,关闭资源 ,最终处理
拦截器的应用场景
1、日志记录:记录请求信息的日志
2、权限检查,如登录检查
3、性能检测:检测方法的执行时间
拦截器实现的两种方式
1、继承HandlerInterceptorAdapter的父类
2、实现HandlerInterceptor接口,实现的接口,推荐使用实现接口的方式
拦截器实现的步骤
1、改造登录方法,在session中存储用户信息,用于进行权限验证
@RequestMapping("/login")
public String login(String name, String pwd, HttpServletRequest request){
if("zar".equalsIgnoreCase(name) && "123".equalsIgnoreCase(pwd)){
//在session中存储用户信息,用于进行权限验证
request.getSession().setAttribute("users",name);
return "main";
}else{
request.setAttribute("msg","用户名或密码不正确!");
return "login";
}
}
2、开发拦截器的功能.实现HandlerInterceptor接口,重写preHandle()方法
package com.bjpowernode.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//是否登录过的判断
if(request.getSession().getAttribute("users") == null){
//此时就是没有登录,打回到登录页面,并给出提示
request.setAttribute("msg","您还没有登录,请先去登录!");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
return true;//放行请求
}
}
3、在springmvc.xml文件中注册拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--映射要拦截的请求(/**表示所有都拦截)-->
<mvc:mapping path="/**"/>
<!--设置不拦截的请求(登录界面不拦截)-->
<mvc:exclude-mapping path="/showLogin"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/login"></mvc:exclude-mapping>
<!--配置具体的拦截器实现类-->
<bean class="com.bjpowernode.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
![](https://img-blog.csdnimg.cn/img_convert/6cda1dc36b7c5c83daea25e1b5fc76cc.png)
多个拦截器可以组成拦截器链。
SSM整合开发
1、建库,建表
/*
Navicat Premium Data Transfer
Source Server : zar
Source Server Type : MySQL
Source Server Version : 80022
Source Host : localhost:3306
Source Schema : ssmusers
Target Server Type : MySQL
Target Server Version : 80022
File Encoding : 65001
Date: 26/06/2021 16:26:14
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
CREATE DATABASE IF NOT EXISTS `ssmuser` DEFAULT CHARACTER SET utf8;
USE `ssmuser`;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`card_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`card_no` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_age` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('15968162087363060', '身份证', '114264195202156467', '张三', '男', '30', '办事人员和有关人员');
INSERT INTO `user` VALUES ('15968162346981977', '护照', 'A32532654', '李四', '男', '29', '不便分类的其他从业人员');
INSERT INTO `user` VALUES ('15968162893439470', '身份证', '112344198709094532', '王五', '男', '31', '农、林、牧、渔、水利业生产人员');
INSERT INTO `user` VALUES ('15968163245457143', '身份证', '453234199909094532', '赵六', '男', '34', '未知');
INSERT INTO `user` VALUES ('15968163514764733', '军官证', '军7657868', '钱七', '女', '23', '不便分类的其他从业人员');
INSERT INTO `user` VALUES ('15968165113694372', '台湾往来大陆通行证', '43256786', '周八', '女', '48', '生产、运输设备操作人员及有关人员');
INSERT INTO `user` VALUES ('15968165371931786', '港澳居民通行证', 'C98767665', '吴九', '女', '35', '不便分类的其他从业人员');
INSERT INTO `user` VALUES ('15968941217553030', '身份证', '343546199801018768', '郑十', '男', '22', '军人');
INSERT INTO `user` VALUES ('15968943937844616', '身份证', '445453199603025756', '冯十一', '女', '31', '不便分类的其他从业人员');
INSERT INTO `user` VALUES ('15968944123869023', '护照', 'B54322654', '陈十二', '女', '39', '农、林、牧、渔、水利业生产人员');
INSERT INTO `user` VALUES ('15968953962316864', '身份证', '110232199505056789', '朱十三', '女', '33', '商业、服务业人员');
INSERT INTO `user` VALUES ('15968954638794962', '身份证', '110654196604079098', '孔十四', '女', '29', '生产、运输设备操作人员及有关人员');
SET FOREIGN_KEY_CHECKS = 1;
2、新建Maven项目,选择webapp模板
3、修改目录
4、修改pom.xml文件(使用老师提供)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<!-- 集中定义依赖版本号,方便修改 -->
<properties>
<!--单元测试的依赖-->
<junit.version>4.12</junit.version>
<!--spring的相关依赖-->
<spring.version>5.2.5.RELEASE</spring.version>
<!--mybatis的相关依赖-->
<mybatis.version>3.5.1</mybatis.version>
<!--mybaits与spring整合的依赖-->
<mybatis.spring.version>1.3.1</mybatis.spring.version>
<!--mybatis支持的分页插件的依赖,本次不用,JSP分页会用,vue不用-->
<mybatis.paginator.version>1.2.15</mybatis.paginator.version>
<!--mysql的依赖-->
<mysql.version>8.0.26</mysql.version>
<!--slf4j日志依赖-->
<slf4j.version>1.6.4</slf4j.version>
<!--阿里的数据库连接池——德鲁伊-->
<druid.version>1.1.12</druid.version>
<!--分页插件的依赖-->
<pagehelper.version>5.1.2</pagehelper.version>
<!--JSTL的依赖(jsp的标准标签库)-->
<jstl.version>1.2</jstl.version>
<!--servlet的依赖-->
<servlet-api.version>3.0.1</servlet-api.version>
<!--jsp的依赖-->
<jsp-api.version>2.0</jsp-api.version>
<!--jackson的依赖,springmvc框架默认进行JSON转换的依赖工具-->
<jackson.version>2.9.6</jackson.version>
</properties>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!--引用定义的版本编号-->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<dependency>
<groupId>com.github.miemiedev</groupId>
<artifactId>mybatis-paginator</artifactId>
<version>${mybatis.paginator.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<!--仅用于测试,不参与打包-->
<scope>test</scope>
</dependency>
<!-- JSP相关 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<!--provided表示使用别人提供的,因为tomcat有了,不参与打包-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<!--provided表示使用别人提供的,因为tomcat有了,不参与打包-->
<scope>provided</scope>
<version>${jsp-api.version}</version>
</dependency>
<!-- Jackson Json处理工具包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<!-- 插件配置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<!--识别所有的配置文件-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
5、添加jdbc.properties属性文件
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmuser?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
6、添加SqlMapConfig.xml文件(使用模板)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--设置日志输出语句,显示相应操作的sql语句-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
7、添加applicationContext_mapper.xml文件(数据访问层的核心配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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:jdbc.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用上面配置的数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--配置(引用)SqlMapConfig.xml核心配置-->
<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
<!--注册实体类的别名,注册完后Mapper文件就可以使用别名代替完整类名-->
<property name="typeAliasesPackage" value="com.bjpowernode.pojo"></property>
</bean>
<!--注册mapper.xml文件-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--在Mybatis中如果使用动态代理那么要用class来注册mapper,但这里不是Mybatis-->
<!--一个mapper文件(查数据库的)与一个接口对应(MyBatis)-->
<property name="basePackage" value="com.bjpowernode.mapper"></property>
</bean>
</beans>
8、添加applicationContext_service.xml文件(业务逻辑层的核心配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--添加包扫描-->
<context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan>
<!--添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--切记切记:配置数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务切面-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*select*" read-only="true"/>
<tx:method name="*find*" read-only="true"/>
<tx:method name="*serach*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<!--propagation,spring事务的传播特性-->
<!--可用no-rollback-for指定不会滚的异常,这里不指定表示发生异常就回滚-->
<tx:method name="*insert*" propagation="REQUIRED"/>
<tx:method name="*add*" propagation="REQUIRED"/>
<tx:method name="*save*" propagation="REQUIRED"/>
<tx:method name="*set*" propagation="REQUIRED"/>
<tx:method name="*update*" propagation="REQUIRED"/>
<tx:method name="*change*" propagation="REQUIRED"/>
<tx:method name="*modify*" propagation="REQUIRED"/>
<tx:method name="*delete*" propagation="REQUIRED"/>
<tx:method name="*drop*" propagation="REQUIRED"/>
<tx:method name="*remove*" propagation="REQUIRED"/>
<tx:method name="*clear*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--配置切入点+绑定-->
<aop:config>
<aop:pointcut id="mycut" expression="execution(* com.bjpowernode.service.impl.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="myadvice" pointcut-ref="mycut"></aop:advisor>
</aop:config>
</beans>
REQUIRED:如果当前没有事务,就开启一个事务;如果有,就加入当前事务。
SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务。
觉得配置文件不好记的,其实这里就是把spring相关配置分成controller、service、dao三层。service层就是业务层,包括主业务(包扫描)和切面业务(aop)相关,这里的事务其实也是一种切面业务。
dao层数据访问层,因为整合了mybatis,所以配置sqlsessionbuildFactory(需要mybatis配置+数据源(因为数据源交给spring管理,不再在mybatis中配置)),以及dao层的重点,动态代理生成mapper对象,并交给spring管理注入MapperScanner。
9、添加spirngmvc.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--添加包扫描-->
<context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
<!--添加注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--因为本项目全部是ajax请求,不需要配置视图解析器-->
</beans>
10、删除web.xml文件,新建,改名,设置中文编码,并注册spirngmvc框架,并注册Spring框架
<?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">
<!--添加中文编码过滤器
private String encoding;
private boolean forceRequestEncoding;
private boolean forceResponseEncoding;
-->
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--注册SpringMVC框架-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--注册Spring框架,目的就是启动spring容器,通过监听器让项目在启动时注册spring框架-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext_*.xml</param-value>
</context-param>
</web-app>
11、新建实体类user
package com.bjpowernode.pojo;
/**
*
*/
public class User {
private String userId;
private String cardType;
private String cardNo;
private String userName;
private String userSex;
private String userAge;
private String userRole;
@Override
public String toString() {
return "User{" +
"userId='" + userId + '\'' +
", cardType='" + cardType + '\'' +
", cardNo='" + cardNo + '\'' +
", userName='" + userName + '\'' +
", userSex='" + userSex + '\'' +
", userAge='" + userAge + '\'' +
", userRole='" + userRole + '\'' +
'}';
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getCardType() {
return cardType;
}
public void setCardType(String cardType) {
this.cardType = cardType;
}
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAge() {
return userAge;
}
public void setUserAge(String userAge) {
this.userAge = userAge;
}
public String getUserRole() {
return userRole;
}
public void setUserRole(String userRole) {
this.userRole = userRole;
}
public User(String userId, String cardType, String cardNo, String userName, String userSex, String userAge, String userRole) {
this.userId = userId;
this.cardType = cardType;
this.cardNo = cardNo;
this.userName = userName;
this.userSex = userSex;
this.userAge = userAge;
this.userRole = userRole;
}
public User() {
}
}
12、新建UserMapper.java接口
再在此,我们不能一上来就把增删改查功能瞎写一通,我们应该先根据项目原型图分析所需要的方法。在工作中,一般项目经理会给出接口文档,前后端都必须根据接口文档来进行开发。
原型图:
![](https://img-blog.csdnimg.cn/img_convert/bc33b223dea6d80dc5ce409a29dcf317.png)
接口文档下载:
ssm前后端项目接口文档(SpringMVC文章用)-Java文档类资源-CSDN文库
package com.bjpowernode.mapper;
import com.bjpowernode.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
*
*/
public interface UserMapper {
/**
* url /user/selectUserPage?userName=z&userSex=男&page=null
* 参数
* userName:表单中用户名称
* userSex:表单中用户性别
* page:提交的页码(第一次访问为null)
* 结果 有数据时:
* [{
* "userId":"15968954638794962",
* "cardType":"身份证","
* cardNo":"343343554654",
* "userName":"撒撒旦",
* "userSex":"女",
* "userAge":"29",
* "userRole":"生产、运输设备操作人员及有关人员"},
* {….}
* ]
* 无数据时:
* []
*
* select * from user
* #limit (当前页码-1)*每页条数,每页条数
* limit 10,5;
*/
List<User> selectUserPage(
@Param("userName")
String userName,
@Param("userSex")
String userSex,
@Param("startRow") //算好的起始行的值
int startRow);
/**
* url /user/createUser(参数见下面)
* 参数
* cardType: this.ruleForm.cardType,//证件类型
* cardNo: this.ruleForm.cardNo,//证件号码
* userName: this.ruleForm.userName,//用户姓名
* userSex: this.ruleForm.userSex,//用户性别
* userAge: this.ruleForm.userAge,//用户年龄
* userRole: this.ruleForm.userRole,//用户角色
* 结果 增加成功时:
* 1
* 增加失败时:
* 0
*/
int createUser(User user);
/**
* url /user/ deleteUserById?userId= 15968162087363060
* 参数
* userId:删除用户的id
* 结果
* 删除成功时:
* 1
* 删除失败时:
* 0
*/
int deleteUserById(String userId);
/**
* url /user/getRowCount?userName=z&userSex=男
* 参数
* userName:表单中用户名称
* userSex:表单中用户性别
* 结果 有数据时:
* 12
* 无数据时:
* 0
*/
int getRowCount(
@Param("userName")
String userName,
@Param("userSex")
String userSex);
}
selectUserPage() 根据select * from user limit (当前页码-1)*每页条数,每页条数 来进行分页输出。
@Param是当参数大于等于2时给MyBatis的mapper文件的注解,用于区分两变量。如果只有一个入参,那么直接#{}取进行。(大括号内为什么名称都可以)
13、新建UserMapper.xml实现增删查所有功能,没有更新
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjpowernode.mapper.UserMapper">
<!--完成实体类与表中列名的映射
private String userId;
private String cardType;
private String cardNo;
private String userName;
private String userSex;
private String userAge;
private String userRole;
-->
<resultMap id="usermap" type="user">
<id property="userId" column="user_id"></id>
<result property="cardType" column="card_type"></result>
<result property="cardNo" column="card_no"></result>
<result property="userName" column="user_name"></result>
<result property="userSex" column="user_sex"></result>
<result property="userAge" column="user_age"></result>
<result property="userRole" column="user_role"></result>
</resultMap>
<!--定义全部列名-->
<sql id="allColumns">
user_id,card_type,card_no,user_name,user_sex,user_age,user_role
</sql>
<!--
List<User> selectUserPage(
@Param("userName")
String userName,
@Param("userSex")
String userSex,
@Param("startRow") //算好的起始行的值
int startRow);
-->
<select id="selectUserPage" resultMap="usermap">
select <include refid="allColumns"></include>
from user
<where>
<if test="userName != null and userName != ''">
and user_name like concat('%',#{userName},'%')
</if>
<if test="userSex != null and userSex != ''">
and user_sex = #{userSex}
</if>
</where>
limit #{startRow},5
</select>
<!--
int createUser(User user);
-->
<insert id="createUser" parameterType="user">
insert into user values(#{userId},#{cardType},#{cardNo},#{userName},#{userSex},#{userAge},#{userRole})
</insert>
<!--
int deleteUserById(String userId);
-->
<delete id="deleteUserById" parameterType="string">
delete from user where user_id = #{userId}
</delete>
<!--
int getRowCount(
@Param("userName")
String userName,
@Param("userSex")
String userSex);
-->
<select id="getRowCount" resultType="int">
select count(*)
from user
<where>
<if test="userName != null and userName != ''">
and user_name like concat('%',#{userName},'%')
</if>
<if test="userSex != null and userSex != ''">
and user_sex = #{userSex}
</if>
</where>
</select>
</mapper>
由于存在返回对象的业务(查询用户需要 接口返回List<User>),而且实体类的成员变量与列名不一致,所以需要使用resultMap对实体类进行成员变量和列名的映射。用于查询出来的数据注入对象对应的成员变量。
14、新建service接口和实现类
接口:
package com.bjpowernode.service;
import com.bjpowernode.pojo.User;
import java.util.List;
/**
*
*/
public interface UserService {
/**
* url /user/selectUserPage?userName=z&userSex=男&page=null
*/
List<User> selectUserPage(String userName,String userSex,int startRow);
/**
* /user/createUser(参数见下面)
*/
int createUser(User user);
/**
* user/ deleteUserById?userId= 15968162087363060
*/
int deleteUserById(String userId);
/**
* /user/getRowCount?userName=z&userSex=男
*/
int getRowCount(String userName,String userSex);
}
实现类:
package com.bjpowernode.service.impl;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.pojo.User;
import com.bjpowernode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
*
*/
@Service
public class UserServiceImpl implements UserService {
//切记切记:一定会有数据访问层的对象
@Autowired
UserMapper userMapper;
@Override
public List<User> selectUserPage(String userName, String userSex, int startRow) {
return userMapper.selectUserPage(userName,userSex,startRow);
}
@Override
public int createUser(User user) {
return userMapper.createUser(user);
}
@Override
public int deleteUserById(String userId) {
return userMapper.deleteUserById(userId);
}
@Override
public int getRowCount(String userName, String userSex) {
return userMapper.getRowCount(userName,userSex);
}
}
15、新建测试类,完成所有功能的测试
按理来说,每写完一层都要对功能进行测试。
package test;
import com.bjpowernode.pojo.User;
import com.bjpowernode.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
*
*/
@RunWith(SpringJUnit4ClassRunner.class) //启动spring容器
@ContextConfiguration(locations = {"classpath:applicationContext_mapper.xml","classpath:applicationContext_service.xml"})
public class MyTest {
@Autowired
UserService userService;
@Test
public void testSelectUserPage(){
List<User> list = userService.selectUserPage("三","男",0);
list.forEach(user -> System.out.println(user));
}
@Test
public void testDeleteUserById(){
int num = userService.deleteUserById("15968162087363060");
System.out.println(num);
}
@Test
public void testGetRowCount(){
int num = userService.getRowCount(null,"男");
System.out.println(num);
}
@Test
public void testCreateUser(){
User u = new User("125412145214547846","身份证","121451245784","哈哈","男","23","工人");
int num = userService.createUser(u);
System.out.println("-----"+num);
}
}
16、新建控制器,完成所有功能
package com.bjpowernode.controller;
import com.bjpowernode.pojo.User;
import com.bjpowernode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
*/
@CrossOrigin //在服务器端支持跨域访问
@RestController //如果本类中全部都是ajax请求,则使用此注解,方法上的@ResponseBody可不写
@RequestMapping("/user")
public class UserController {
//切记切记:一定会有业务逻辑层的对象
@Autowired
UserService userService;
public static final int PAGE_SIZE = 5;
//user/selectUserPage?userName=z&userSex=男&page=null
@RequestMapping("/selectUserPage")
public List<User> selectUserPage(String userName,String userSex,Integer page){
//根据页码计算起始行
int startRow = 0;
if(page != null){
startRow = (page-1) * PAGE_SIZE;
}
return userService.selectUserPage(userName,userSex,startRow);
}
///user/getRowCount?userName=z&userSex=男
@RequestMapping("/getRowCount")
public int getRowCount(String userName,String userSex){
return userService.getRowCount(userName,userSex);
}
///user/deleteUserById?userId= 15968162087363060
@RequestMapping("/deleteUserById")
public int deleteUserById(String userId){
return userService.deleteUserById(userId);
}
///user/createUser(参数见下面)
@RequestMapping("/createUser")
public int createUser(User user){
String userId = System.currentTimeMillis()+"";
user.setUserId(userId);
return userService.createUser(user);
}
}
17、浏览器测试功能(完)
整体目录:
![](https://img-blog.csdnimg.cn/img_convert/5549ac27a9e9c01268bd560a6321767a.png)