SpringMVC笔记完善

编程入门 行业动态 更新时间:2024-10-21 09:25:03

SpringMVC<a href=https://www.elefans.com/category/jswz/34/1770047.html style=笔记完善"/>

SpringMVC笔记完善

什么是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优化的方向

SpringMVC执行的流程

基于注解的SpringMVC框架开发的步骤

(1)、新建项目,选择webapp模板(不选择quickStart)

(2)、修改目录,添加缺失的目录,并修改目录属性

其中,WEB-INF在webapp目录下。

(3)、修改pom.xml文件,添加SpringMVC的依赖,添加Servlet的依赖(SpringMVC的依赖会间接把spring的依赖也都加入到项目)(因为由maven来管依赖,所以在pom文件中编写)。同时也要添加Servlet依赖,不然没有JavaX或Jakarta类的方法可用。

<?xml version="1.0" encoding="UTF-8"?><project xmlns=".0.0" xmlns:xsi=""xsi:schemaLocation=".0.0 .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><mavenpiler.source>1.8</mavenpiler.source><mavenpiler.target>1.8</mavenpiler.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)声明视图解析器。帮助处理视图的。

位置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:context=""xsi:schemaLocation=" .xsd  .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则不需要更改)

必须在新建的时候修改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=""xmlns:xsi=""xsi:schemaLocation=" .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管理,但不能使用 *.jsphttp://localhost:8080/myweb/some.dohttp://localhost:8080/myweb/other.do2.使用斜杠 "/"--><url-pattern>*.do</url-pattern></servlet-mapping></web-app>

(7)、在webapp目录下创建一个发起请求的页面 index.jsp。

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");// 返回mvreturn mv;// 在该方法结束后,将运行到show.jsp中,我们可以在show.jsp中对mv.addObject添加的数据进行使用。}
}

(9)、在webapp目录下创建一个作为结果的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>msg数据:${msg}</h3><br/><h3>fun数据:${fun}</h3>
</body>
</html>

(10)、添加tomcat进行测试功能

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

WEB-INF的保护机制

在当前项目中index.jsp和show.jsp都放在了webapp目录下,如果用户知道该show.jsp的访问地址,就可以通过直接输入地址跳过index.jsp文件直接到达show.jsp。但因为并没有经过MyController的处理,servlet没执行,所以没有数据。

但我们应该避免这种情况。

因此,对于这种要保护的jsp,我们都会放到WEB-INF目录下,因为WEB-INF目录下的所有内容都是不能直接访问的,只能通过servlet的转发才能访问。

重定向不能访问到WEB-INF目录下的资源,因为重定向是客户端的,而转发是服务端内部的。

重定向是让客户端去访问重定向的地址,而WEB-INF下的文件是不能通过外部访问的!

另外,通常不会直接放在WEB-INF目录下,因为一个项目受保护的jsp可能很多,所以一般是在WEB-INF下新建一个包并把需要保护的jsp放到新建的包中。

我们需要更改一下控制器类:

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=""xmlns:xsi=""xmlns:context=""xsi:schemaLocation=" .xsd  .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 错误,反之同理。

不指定请求方式,则访问没有限制。

控制器类方法的参数

处理器方法可以包含以下七类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。

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: AdministratorDate: 2022/2/28Time: 17:20To 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状态码是客户端错误, 表示提交请求参数过程中,发生了问题。

但后端并不会抛异常,只会记录错误日志:

注意,如果某个值不是必填项,允许前端传递为空,但也存在不为空的情况,那我们可以把这个值的类型换成包装类型,包装类型允许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=""xmlns:xsi=""xsi:schemaLocation=" .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等等不能使用 *.jsphttp://localhost:8080/myweb/some.dohttp://localhost:8080/myweb/other.do2.使用斜杠 "/"--><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组件,相对于fastjsonObjectMapper 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);

springmvc处理器方法返回Object, 可以转为json输出到浏览器,响应ajax的内部原理:

1. <mvc:annotation-driven> 注解驱动。

注解驱动实现的功能是 完成java对象到json,xml, text,二进制等数据格式的转换。

<mvc:annotation-driven>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口的7个实现类对象, 包括 MappingJackson2HttpMessageConverter (该实现类会使用jackson工具库中的ObjectMapper实现java对象转为json字符串)

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")@ResponseBodypublic 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")@ResponseBodypublic 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的字符编码去解析这个普通字符串,会出现乱码。

可以在@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")@ResponseBodypublic String doStringData(String name,Integer age){return "Hello SpringMVC 返回对象,表示数据";}

注入日期(浏览器->服务器)与日期显示(服务器->浏览器)需要专门处理

日期提交的最优做法是让用户选而不是让用户写,因为写的情况太多太复杂了,什么格式都有。

日期是不能用上述方式让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=""xmlns:xsi=""xmlns:context=""xmlns:mvc=""xsi:schemaLocation=" .xsd  .xsd  .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>

B.类中全局日期处理

如果采用A的方法,对每一个日期变量都单独处理,那么如果一个方法的形参有多个Date类型的变量那么就需要多个注解,如果不同方法都需要多个Date类型的变量那所需要的注解就会更多。

所以这时我们可以采用全局的日期处理。即注册一个注解,用来解析本类中所有的日期类型,自动转换。

public class MyDateAction {SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");//注册一个全局的日期处理注解@InitBinderpublic 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;@Overridepublic 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");//注册一个全局的日期处理注解@InitBinderpublic 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>

因此,要在页面上显示好看的日期,必须在jsp中使用JSTL对Date数据进行格式化显示。

步骤:

A)添加依赖jstl

    <dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency>

B)在页面上导入标签库

<%--导入jstl核心标签库--%>
<%@taglib prefix="c" uri="" %>
<%--导入jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="" %>

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=""xmlns:xsi=""xsi:schemaLocation=" .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管理,但不能使用 *.jsphttp://localhost:8080/myweb/some.dohttp://localhost:8080/myweb/other.do2.使用斜杠 "/"--><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=""xmlns:xsi=""xsi:schemaLocation=" .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=""xmlns:xsi=""xmlns:context=""xmlns:mvc=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/context/spring-context.xsd://www.springframework/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=""xmlns:xsi=""xmlns:context=""xmlns:mvc=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/context/spring-context.xsd://www.springframework/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 即可。

<!--使用一个配置语句,指定多种静态资源的访问-->
<mvc:resources mapping="/static/**" location="/static/" />

绝对路径与相对路径

jsp的页面路径中,有时候需要以/开头,有时候又不需要,那么什么时候需要什么时候不需要呢?

在jsp , html中使用的地址, 都是在前端页面中的地址,都是相对地址。

地址分类:

1.绝对地址 , 带有协议名称的是绝对地址, , 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的项目访问地址设置:

不以/开头要注意的问题

当在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.doreturn 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.doreturn 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。

源码追踪:

UrlBaseViewResolver类提供了两大常量,分别表示转发和重定向。

请求转发不会改变地址栏的地址,重定向会改变地址栏的地址。

forward:转发 redirect:重定向

代码示例:

SpringMVC.xml视图解析器:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:mvc=""xsi:schemaLocation=" .xsd  .xsd  .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";}
}

目录:

<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 {@Overridepublic 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>

多个拦截器可以组成拦截器链。

SSM整合开发

1、建库,建表

/*Navicat Premium Data TransferSource Server         : zarSource Server Type    : MySQLSource Server Version : 80022Source Host           : localhost:3306Source Schema         : ssmusersTarget Server Type    : MySQLTarget Server Version : 80022File Encoding         : 65001Date: 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=".0.0" xmlns:xsi=""xsi:schemaLocation=".0.0 .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//DTD Config 3.0//EN"".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=""xmlns:xsi=""xmlns:context=""xsi:schemaLocation=" .xsd  .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=""xmlns:xsi=""xmlns:context="" xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd  .xsd  .xsd  .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=""xmlns:xsi=""xmlns:context=""xmlns:mvc=""xsi:schemaLocation=" .xsd  .xsd  .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=""xmlns:xsi=""xsi:schemaLocation=" .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;@Overridepublic 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接口

再在此,我们不能一上来就把增删改查功能瞎写一通,我们应该先根据项目原型图分析所需要的方法。在工作中,一般项目经理会给出接口文档,前后端都必须根据接口文档来进行开发。

原型图:

接口文档下载:

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//DTD Mapper 3.0//EN"".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 {//切记切记:一定会有数据访问层的对象@AutowiredUserMapper userMapper;@Overridepublic List<User> selectUserPage(String userName, String userSex, int startRow) {return userMapper.selectUserPage(userName,userSex,startRow);}@Overridepublic int createUser(User user) {return userMapper.createUser(user);}@Overridepublic int deleteUserById(String userId) {return userMapper.deleteUserById(userId);}@Overridepublic 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 {@AutowiredUserService userService;@Testpublic void testSelectUserPage(){List<User> list = userService.selectUserPage("三","男",0);list.forEach(user -> System.out.println(user));}@Testpublic void testDeleteUserById(){int num = userService.deleteUserById("15968162087363060");System.out.println(num);}@Testpublic void testGetRowCount(){int num = userService.getRowCount(null,"男");System.out.println(num);}@Testpublic 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 {//切记切记:一定会有业务逻辑层的对象@AutowiredUserService 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、浏览器测试功能(完)

整体目录:

更多推荐

SpringMVC笔记完善

本文发布于:2024-02-25 15:19:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1699406.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记   SpringMVC

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!