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="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)声明视图解析器。帮助处理视图的。

位置:

<?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则不需要更改)

必须在新建的时候修改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。

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,显示请求的处理结果。

<%@ 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="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 错误,反之同理。

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

控制器类方法的参数

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

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状态码是客户端错误, 表示提交请求参数过程中,发生了问题。

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

注意,如果某个值不是必填项,允许前端传递为空,但也存在不为空的情况,那我们可以把这个值的类型换成包装类型,包装类型允许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);

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

可以在@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 返回对象,表示数据";
    }

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

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

日期是不能用上述方式让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>

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>

因此,要在页面上显示好看的日期,必须在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 即可。

<!--使用一个配置语句,指定多种静态资源的访问-->
<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的项目访问地址设置:

不以/开头要注意的问题

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

源码追踪:

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";
    }
}

目录:

<mvc:annotation-driven/>标签的使用

<mvc:annotation-driven/>会自动注册两个bean,分别为DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter。是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>

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

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接口

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

原型图:

接口文档下载:

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、浏览器测试功能(完)

整体目录:

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦矜

对你有帮助的话,请我吃颗糖吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值