SpringMVC概述

SpringMVC 基本说明

  • SpringMVC是基于spring的, 是spring中的一个模块,做web开发使用的。 springmvc 叫做spring web mvc

说明他是spring的核心技术, 做web开发,springmvc内部是使用mvc架构模式。

  • SpringMVC 是一个容器, 管理对象的,使用IoC核心技术。 springmvc管理界面层中的控制器对象。
  • SpringMVC底层也是Servlet。 以Servlet为核心, 接收请求,处理请求。 显示处理结果给用户。
  • 处理用户的请求:用户发起请求——SpringMVC—-Spring—-MyBatis—mysql数据库

    SpringMVC中的核心Servlet — DispatcherServlet

    DispatcherServlet 是框架一个Servlet对象。 负责接收请求, 响应处理结果。
    DispatcherServlet 他的父类是HttpServlet
    DispatcherServlet 也叫做前端控制器( front controller)。
    SpringMVC是管理控制器对象, 原来没有SpringMVC之前使用 Servlet作为控制器对象使用。现在通过SpringMVC容器创建一种叫做控制器的对象,代替Servlet行驶控制器的角色。功能。
    SpringMVC 主要使用注解的方式, 创建控制器对象, 处理请求。

    案例

    用户发起一个请求,springmvc接受请求,显示请求的处理结果
    步骤:
  1. 新建web应用
  2. 加入web依赖

springmvc依赖,servlet依赖

  1. 声明springmvc核心对象DispatcherServlet

1、DispatcherServlet是一个Servlet对象
2、DispatcherServlet叫作前端控制器
3、DispatcherServlet的作用:在servlet的init()方法中,创建springmvc中的容器对象、作为servlet,接收请求

  1. 创建一个jsp,发起请求
  2. 创建一个普通类,作为控制器使用

1、在类的上面加入@Controller注解
2、在类中定义方法,方法的上面加入@RequestMapping注解,方法处理请求时,相当于servlet的doGet,doPost

  1. 创建作为结果的jsp文件
  2. 创建springmvc的配置文件

声明组件扫描器指定,@Controller注解所在的包名
声明视图解析器对象

  1. 使用逻辑视图名称

图片.png

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <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/maven-v4_0_0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <packaging>war</packaging>
  5. <properties>
  6. <maven.compiler.source>1.8</maven.compiler.source>
  7. <maven.compiler.target>1.8</maven.compiler.target>
  8. </properties>
  9. <name>demo3</name>
  10. <groupId>org.example</groupId>
  11. <artifactId>demo3</artifactId>
  12. <version>1.0-SNAPSHOT</version>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.springframework</groupId>
  16. <artifactId>spring-webmvc</artifactId>
  17. <version>5.2.5.RELEASE</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>javax.servlet</groupId>
  21. <artifactId>javax.servlet-api</artifactId>
  22. <version>3.1.0</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>junit</groupId>
  26. <artifactId>junit</artifactId>
  27. <version>4.11</version>
  28. <scope>test</scope>
  29. </dependency>
  30. </dependencies>
  31. </project>

web.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="2.4"
  3. xmlns="http://java.sun.com/xml/ns/j2ee"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  6. <!--
  7. 声明springmvc的核心对象
  8. 访问mymvc地址后,报错文件没有找到。找的文件 /WEB-INF/springmvc-servlet.xml
  9. /WEB-INF/myweb-servlet.xml
  10. 错误原因:
  11. servletinit()方法中,创建springmvc使用的容器对象WebApplicationContext.
  12. WebApplicationContext ctx=new ClassPathXmlApplicationContext(配置文件)
  13. 配置文件的默认路径:/WEB-INF/<servlet-name>-servlet.xml
  14. -->
  15. <servlet>
  16. <servlet-name>myweb</servlet-name>
  17. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  18. <!--自定义配置文件的位置-->
  19. <init-param>
  20. <param-name>contextConfigLocation</param-name>
  21. <param-value>classpath:springmvc.xml</param-value>
  22. </init-param>
  23. <!--让服务器tomcat创建对象的顺序,大于0,数值越小,创建对象越早-->
  24. <load-on-startup>1</load-on-startup>
  25. </servlet>
  26. <servlet-mapping>
  27. <servlet-name>myweb</servlet-name>
  28. <!--url-pattern作用:把一些请求交给指定的servlet处理
  29. 使用中央调度器(DispatcherServlet
  30. 1.使用拓展名方式,格式*.xxx,xxx是自定义的拓展名。例如 *.do,*.action,*.mvc,不能使用.jsp等等
  31. http://localhost:8080/demo3/some.do
  32. 2.使用斜杠 /
  33. -->
  34. <url-pattern>*.do</url-pattern>
  35. </servlet-mapping>
  36. </web-app>

some.do

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>show</title>
</head>
<body>
    .show.jsp,显示request作用域中的数据<br>
    msg数据:${msg}<br>
    fun数据:${fun}<br>

</body>
</html>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>第一个springmvc</title>
</head>
<body>
    <a href="some.do"> 发起some.do请求</a>
</body>
</html>

MyController.java

package com.bjpowernode.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 *  @Controller创建控制器(处理器)对象
 *  控制器:叫做后端控制器,自定义的类处理请求
 *  位置:在类上面,表示创建此类的对象,对象放在springmvc的容器中
 */
@Controller
public class MyController {
    //定义方法处理请求 public void doGet(){}
    /**
     * springmvc框架,使用控制器类中的方法,处理请求。
     * 方法的特点:1.方法的形参,表示请求中的参数
     * 2。方法的返回值,表示本次请求的处理请求
     *
     * @RequestMapping:请求映射
     *      属性:value 请求中的uri地址,唯一值。以/开头
     *      位置:1.在方法的上面(必须的) 2.在类定义的上面(可选)
     *      作用:把指定的请求,交给指定的方法处理,等同于url-pattern
     * 返回值ModelAndView:表示本次请求的处理结果(数据和视图)
     *      Model表示数据
     *      View表示视图
     *
     */
    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(){
        //使用这个方法处理请求。能处理请求的方法叫做控制器方法
        //调用servlet对象,处理请求,返回数据
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","处理了some.do请求");
        mv.addObject("fun","执行了some.do方法");

        //指定视图,setViewName("视图的完整路径")
        mv.setViewName("/show.jsp");
        return mv;
    }
    /**
     * 当框架调用完dosome()方法后,得到返回中ModelAndView
     * 框架会在后续的处理逻辑值,处理mv对象里面的数据和视图
     * 对数据执行request.setAttribute("msg","处理了some.do请求");把数据放入到request作用域
     * 对试图执行forword转发操作,等同于request.getRequestDispather("/show.jsp").forword(..)
     */
}

springmvc请求的处理过程

  • 用户发起请求some.do—->Tomcat接收了请求—-DispatcherServlet—分配MyController(doSome()返回mv对象)—mv显示给用户了。

    配置视图解析器

    为了解决转发到视图中文件路径重复的问题,可以在springmvc.xml中配置视图解析器

    @RequestMapping(value = "/some.do")
      public ModelAndView doSome(){
          //使用这个方法处理请求。能处理请求的方法叫做控制器方法
          //调用servlet对象,处理请求,返回数据
          ModelAndView mv = new ModelAndView();
          //把数据放入到request作用域
          mv.addObject("msg","处理了some.do请求");
          mv.addObject("fun","执行了some.do方法");
    
          //指定视图,setViewName("视图的完整路径") 对视图执行转发操作
          mv.setViewName("/WEB-INF/view/show.jsp");
          return mv;
      }
    

    添加内容如下

    <!--声明视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <!--前缀:指定视图文件路径-->
      <property name="prefix" value="/WEB-INF/view/"/>
      <!--后缀:视图文件的扩展名-->
      <property name="suffix" value=".jsp"/>
    </bean>
    

    声明后可以直接使用文件名进行转发

    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(){
      //使用这个方法处理请求。能处理请求的方法叫做控制器方法
      //调用servlet对象,处理请求,返回数据
      ModelAndView mv = new ModelAndView();
      //把数据放入到request作用域
      mv.addObject("msg","处理了some.do请求");
      mv.addObject("fun","执行了some.do方法");
    
      //使用了逻辑名称,框架使用配置文件中视图解析器的前缀和后缀,拼接为完整视图路径
      //指定视图,setViewName("视图的完整路径") 对视图执行转发操作
      mv.setViewName("show");
      return mv;
    }
    

    web开发中配置文件的说明

  1. web.xml部署描述符文件,给服务器(tomcat)。作用:服务器在启动的时候,读取web.xml,根据文件中的声明创建各种对象。根据文件中的声明知道请求和servlet等对象的关系
  2. 框架的配置文件,springmvc的配置文件。作用:声明框架创建项目中的各种对象,主要是创建Controller对象的

配置文件的加载顺序和功能

  1. tomcat服务器启动, 读取web.xml. 根据web.xml文件中的说明,创建对象。
    创建DispatcherServlet他的对象, 会执行init()方法。 在init()方法中会执行 springmvc容器对象创建
    WebApplicationContext ctx = new ClassPathXmlApplicationContext(“classpath:springmvc.xml”)

    <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>
        <!--
           表示服务器tomcat创建对象的顺序, 是个整数值, 大于等于0.
           数值越小,创建对象的时间越早。
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
  2. springmvc框架, new ClassPathXmlApplicationContext()读取springmvc的配置文件。
    使用组件扫描器base-package=”com.bjpowernode.controller” ,遍历controller包中的所有类,MyController类, 找到这个类中的@Controller, @RequestMapping注解, 就能创建MyContoller对象。
    知道some.do的请求是执行doSome()方法
    以上 1, 2. 都是项目启动的过程, 没有执行任何的用户请求。

    <context:component-scan base-package="com.bjpowernode.controller" />
    
    <!--声明视图解析器:帮助处理视图-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀:指定视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/" />
        <!--后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp" />
    </bean>
    
  3. 用户发起请求some.do——DispatcherServlet
    DispatcherServlet里面有 WebApplicationContext 。 WebApplicationContext 里面有MyController对象。
    请求some.do ,DispatcherServlet 就知道是 MyController处理的。

    SpringMVC内部的执行流程

    springmvc内部请求的处理过程:

  4. 用户发起请求给DispatcherServlet

  5. DispatcherServlet把请求(request)交给了 处理器映射器。
    处理器映射器: springmvc框架中的对象, 需要实现HandlerMapping接口。
    映射器作用: 从springmvc容器中,获取控制器对象(MyController),把找到的控制器和拦截器对象都放到 处理器执行链对象中,保存,并返回给中央调度器。(MyController controller = ApplicationContext.getBean())
  6. DispatcherServlet把获取到的处理器执行链中的控制器对象,交给了处理器适配器
    处理器适配器:是springmvc框架中的对象, 实现HandlerAdapter接口。
    适配器作用: 执行控制器的方法, 也就是执行MyController.doSome()方法。得到结果ModelAndView
  7. DispatcherServlet把控制器执行结果mv交给了 视图解析器
    视图解析器: springmvc中的对象,需要实现ViewResolver接口。
    视图解析器作用: 处理视图的, 组成视图的完整路径。 能创建View类型的对象
  8. DispatcherServlet调用View类的方法, 把Model中的数据放入到request作用域。 执行request.setAttribute(), 对视图执行forward()转发行为, request.getRequestDispather(“/show.jsp”).forward(request,response)

    SpringMvc注解开发

    @RequestMapping注解的使用

    属性:value请求的uri地址
    位置:①在方法上面,必须的。②在类上面,作为模块名称
@RequestMapping(value ="/some.do")
public ModelAndView doSome(){}

@Controller
@RequestMapping("/test")
public class MyController {}

属性 method 请求的方式, 使用RequestMehtod类的枚举,表示请求方式

@RequestMapping(value ="/other.do",method = RequestMethod.POST)
public ModelAndView doOther(){}

DispatcherServlet作用

  1. 在init()中创建springmvc的容器对象 WebApplicationContext,创建springmvc配置文件在的所有Java对象。java对象,就是Controller对象
  2. DipatcherServlet是一个Servlet,能够接收请求

接收请求中的参数

  • 对应HttpServletRequest, HttpServletResponse, HttpSession 只需要在控制器方法的形参列表中,定义就可以了。框架会给参数赋值, 在控制器方法内部可以直接使用 request,response,session参数。
  • 400 : http status , 表示客户端异常。 主要是发生在用户提交参数过程中。

    逐个接收

    逐个接收: 请求中的参数名和控制器方法的形参名一样。按照名称对象接收参数 ```java //方式一、框架使用request对象,接收参数 @RequestMapping(value = “/some.do”,method = RequestMethod.GET) public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response, HttpSession session){

      String usernaem = request.getParameter("usernaem");
      System.out.println("username的值为"+usernaem);
    
    //使用这个方法处理请求。能处理请求的方法叫做控制器方法
    //调用servlet对象,处理请求,返回数据
    ModelAndView mv = new ModelAndView();
    //把数据放入到request作用域
    mv.addObject("msg","处理了some.do请求");
    mv.addObject("fun","执行了some.do方法");

    //指定视图,setViewName("视图的完整路径") 对视图执行转发操作
    mv.setViewName("show");
    return mv;
}

//方式二、在中央调度器的内部调用doProperparam方法时,按名称对象传递参数 @RequestMapping(value = “/receive-property.do”,method = RequestMethod.POST) public ModelAndView receive(String name,int age){ ModelAndView mv = new ModelAndView();

    mv.addObject("name",name);
    mv.addObject("age",age);
    mv.setViewName("show1");
    return mv;
}

<a name="uriwt"></a>
### 解决中文乱码问题
使用过滤器处理,在web.xml中添加配置文件
```java
    <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>
        <init-param>
            <!--强制请求(request)对象使用encoding的编码方式-->
            <param-name>forcerequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>

        <init-param>
            <!--强制应答(respondse)对象使用encoding的编码方式-->
            <param-name>foeceresponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <!--所有请求先通过过滤器处理-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

请求中参数名和形参名不一样

/**
 * 逐个接收请求参数, 请求中参数名和形参名不一样
 * @RequestParam : 解决名称不一样的问题
 *       属性: value 请求中的参数名称
 *              required : boolean类型的,默认是true
 *                    true:请求中必须有此参数,没有报错。
 *                    false:请求中可以没有此参数。
 *       位置: 在形参定义的前面
 */
@RequestMapping(value ="/receive-param.do")
public ModelAndView doReceiveParam(
    @RequestParam(value = "rname",required = false) String name,
    @RequestParam(value = "rage",required = false) Integer age) { }

对象接收

对象接收: 在控制器方法的形参是java对象, 使用java对象的属性接收请求中参数值。
要求: java对象的属性名和请求中参数名一样。

 */
public class Student {

    // 属性名和请求中参数名一样
    private String name;
    private Integer age;
    // set| get方法
}

 @RequestMapping("/receive-object.do")
 public ModelAndView doReceiveObject(Student student){
    System.out.println("MyController的方法doReceiveObject="+student);
    ModelAndView mv = new ModelAndView();
    mv.addObject("myname", student.getName());
    mv.addObject("myage", student.getAge());
    mv.setViewName("show");
    return mv;
}

集合类型的对象接收

<body>
<%--    <input id="aa" type="button" value="发起Ajax请求" >--%>
<%--    <button id="btnAjax" >发起ajax请求</button>--%>

    <form action="/demo3/test/return-void-ajax.do" method="post">
        姓名:<input type="text" name="name"><br/>
        年龄:<input type="text" name="age"><br/>
        list_name:<input type="text" name="list[0].name"><br/>
        list_age:<input type="text" name="list[0].age"><br/>
        map_name:<input type="text" name="map['username']"><br/>
        map_age:<input type="text" name="map['userage']"><br/>
        <input type="submit" value="提交">
    </form>
</body>


 @RequestMapping(value = "/return-void-ajax.do",method = RequestMethod.POST)
    public void returnVoidAjax(HttpServletResponse response,HttpServletRequest request,Student student) throws IOException {
        System.out.println(student);
    }
package com.bjpowernode.Pojo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Student {
    private String name;
    private Integer age;
    private List<User> list;
    private Map<Object,Object>map;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", list=" + list +
                ", map=" + map +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public List<User> getList() {
        return list;
    }

    public void setList(List<User> list) {
        this.list = list;
    }

    public Map<Object, Object> getMap() {
        return map;
    }

    public void setMap(Map<Object, Object> map) {
        this.map = map;
    }
}

class User{
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

自定义类型转换器

实现转换类

import org.springframework.core.convert.converter.Converter;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class StringToDateConverter implements Converter<String,Date> {

    @Override
    public Date convert(String s) {
        //判断
        if(s==null){
            throw new RuntimeException("请输入数据");
        }
        DateFormat df =new SimpleDateFormat("wwww-MM-dd");
        try {
            return df.parse(s);
        } catch (ParseException e) {
            throw new RuntimeException("数据类型转换出现错误");
        }
    }
}

添加配置文件

    <!--配置自定义类型转换器-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.bjpowernode.util.StringToDateConverter"/>
            </set>
        </property>
    </bean>
    <!--开启SpringMVC框架注解的支持-->
    <mvc:annotation-driven conversion-service="conversionService"/>

HttpMessageConverter 消息转换器

HttpMessageConverter 接口,作用是
1)实现请求的数据转为java对象,
2) 把控制器方法返回的对象转为json,xml,text,二进制等不同格式的数据。

public interface HttpMessageConverter<T> {
   /**
      作用: 检查clazz这个类型的对象,能否转为 mediaType表示的数据格式
            如果能转为mediaType表示的类型, 返回true, 返回true调用read()
    */
   boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

   /**
      作用: 接收请求中的数据,把数据转为 clazz表示的对象
    */
   T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
         throws IOException, HttpMessageNotReadableException;

   /**
      作用:检查clazz这种数据类型,能否转为mediaType表示的数据格式
    */
   boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

   /**
      作用: 把t对象,按照contentType说明的格式,把对象转为json或者xml
    */
   void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;

}

MediaType:媒体类型,表示互联网中数据的格式。例如application/json, text/html, image/gif

HttpMessageConverter 接口的实现类:
MappingJackson2HttpMessageConverter : 用jackson工具库的ObjectMapper把java对象转为json数据格式
StringHttpMessageConverter : 把字符串类型的数据,进行格式转换和编码

怎么使用实现类:

框架根据控制器方法的返回类型, 自动查找使用的实现类。

@RequestMapping("/receive-object.do")
public Student doReceiveObject(String name,Integer age){
    System.out.println("MyController的方法doReceiveObject=");
    Student student = new Student();
    student.setName("lisi");
    student.setAge(20);
    return student;
}

默认情况下: springmvc使用了HttpMessageConveter接口的4个实现类。包括了StringHttpMessageConverter.

需要在springmvc的配置文件,加入注解驱动的标签 mvc:annotation-driven. 加入这个标签后, springmvc项目启动后,会创建HttpMessageConveter接口的7个实现类对象,包括StringHttpMessageConverter 和 MappingJackson2HttpMessageConverter。

控制器方法的返回值

控制器方法的返回值表示本次请求的处理结果,返回值有ModelAndView, String, void , Object
请求的处理结果包含: 数据和视图。

ModelAndView 数据和视图

请求的结果有数据和视图,使用ModelAndView最方便
数据:存放request作用域。
视图:执行forward转发操作

String 视图

框架对返回值是String,执行的是forward转发操作。
视图可以表示为完整视图路径, 或者视图的逻辑名称

@RequestMapping(value ="/return-string-view.do")
public String doReturnStringView1(HttpServletRequest request,String name, Integer age) {
        System.out.println("执行了MyController的doReturnStringView1方法name=");

        //返回结果,forward,转发到show.jsp
        //逻辑名称, 需要配置视图解析器
        return "show";
}


@RequestMapping(value ="/return-string-view2.do")
public String doReturnStringView2(HttpServletRequest request,String name, Integer age) {
    System.out.println("执行了MyController的doReturnStringView2方法name=");
    //完整视图路径,不能使用视图解析器
    return "/WEB-INF/view/show.jsp";

}

void 没有数据和视图

void: 没有数据和视图, 可以使用HttpServletResponse对象输出数据,响应ajax请求。

<%--
  Created by IntelliJ IDEA.
  User: lhd
  Date: 2021-07-28
  Time: 16:45
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>第一个springmvc</title>
    <script type="text/javascript" src="jquery-1.7.2.js"></script>
    <script type="text/javascript">
        $(function () {
            //绑定事件
            $("#aa").click(function () {
                $.ajax({
                    url:"/demo3/test/return-void-ajax.do",
                    data:{
                        name:"lisi",
                        age:20
                    },
                    dataType:"json",
                    success:function (resp) {
                        alert("resp,name=="+resp.name+"resp.age,"+resp.age)
                    }
                })
            })
        });
    </script>
</head>
<body>
    <input id="aa" type="button" value="发起Ajax请求" >
<%--    <button id="btnAjax" >发起ajax请求</button>--%>
</body>
</html>




  /**
     * 控制器方法返回void,相应ajax请求,使用Http'ServletResponse输出数据
     */
    @RequestMapping(value = "/return-void-ajax.do")
    public void returnVoidAjax(HttpServletResponse response,String name,Integer age) throws IOException {
        System.out.println("处理void返回类型,name="+name+"age="+age);
        //调用servlet得到结果对象
        Student stu = new Student(name,age);

        //把对象转化为json
        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(stu);
        System.out.println("服务器端对象转化为json=="+json);

        //输出json,相应ajax
        response.setContentType("application/json;charset=utf-8");
        PrintWriter pw = response.getWriter();
        pw.write(json);
        pw.flush();
        pw.close();

    }

常用注解

@RequestParam

作用:把请求中指定名称的参数给控制器中的形参赋值
属性:value,请求参数中的名称
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错

<a href="/demo3/test/testServletParam?username=lhd&age=18">RequestParam</a>


    @RequestMapping("/testServletParam")
    public String testRequestParam(@RequestParam(name = "username" ,required = false) String name){
        System.out.println("执行了");
        System.out.println(name);
        return "show";
    }

@ResponseBody

作用:用于获取请求体内容。直接使用得到是key=value&key=value结构的数据。
属性:required:是否必须有请求体。默认是true。当取值为true时,get请求方式会报错。如果取值为false,get请求得到是null。

<form action="/demo3/test/testRequestBody" method="post">
        用户姓名:<input type="text" name="name" /> <br/>
        用户年龄:<input type="text" name="age" /> <br/>
        用户生日:<input type="text" name="date" /> <br/>
        <input type="submit" value="提交">
</form>

    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){//如果不加注解会默认是获得body属性,加了注解表示获取请求体
        System.out.println(body);
        return "show";
    }

PathVaribale

作用:用于绑定url中的占位符。例如url中/delete/{id},这个{id}就是url的占位符。url支持占位符是spring3.0之后加入的。是springmvc支持rest风格URL的一个重要标志。
属性:value:用于指定url中占位符名称
required:是否必须提供占位符

 <a href="/demo3/test/testPathVariable/10">testPathVariable</a>



    @RequestMapping("/testPathVariable/{id}")
    public String testPathVariable(@PathVariable(name = "id") String id){
        System.out.println(id);
        return "show";
    }

RequestHeader注解

作用:用于获取请求消息头
属性:value:提供消息头名称
required:是否必须有此消息头

   <a href="/demo3/test/RequestHeader">RequestHeader</a>    

    @RequestMapping("/RequestHeader")
    public String RequestHeader(@RequestHeader(value = "Accept") String header){
        System.out.println(header);
        return "show";
    }

CookieValue注解

作用:用于把指定cookie名称的值传入控制器方法参数
属性:value:指定cookie的名称
required:是否必须有此cookie

<a href="/demo3/test/testCookieValue">testCookieValue</a>


    @RequestMapping("/testCookieValue")
    public String testCookieValue(@CookieValue(value = "JSESSIONID") String cookie){
        System.out.println(cookie);
        return "show";
    }

ModelAttribute

作用:该注解是SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
属性:value:用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key。
应用场景:当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。

<a href="/demo3/test/testModelAttribute?name=lhd">testModelAttribute</a>



    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("abc") User user){
        System.out.println(user);
        System.out.println("testModelAttribute方法执行了");
        return "show";
    }

    /**
     * 该方法会先执行
     */
    @ModelAttribute
    public User showUser(String name){ //有返回值的情况
        System.out.println("showUser方法执行了");
        //通过用户查询数据库与
        User user =new User();
        user.setAge(20);
        user.setName(name);
        user.setDate(new Date());
        //该对象会被返回到testModelAttribute中
        return user;
    }
     @ModelAttribute
    public void showUser1(String name, Map<String,User> map){ //无返回值的情况
        System.out.println("showUser方法执行了");
        //通过用户查询数据库与
        User user =new User();
        user.setAge(21);
        user.setName(name);
        user.setDate(new Date());
        //该对象会被返回到testModelAttribute中
         map.put("abc",user);
    }

SessionAttribute

作用:用于多次执行控制器方法间的参数共享
属性:value:用于指定存入的属性名称
type:用于指定存入的数据类型
设置数据

@Controller
@RequestMapping("/test")
@SessionAttributes(value = {"msg"}) //把msg==美美存入到session域中
public class MyController {
    @RequestMapping("/testSessionAttribute")
    public String testSessionAttribute(Model model){
        System.out.println("testSessionAttribute方法执行了");
        // 将数据保存到request域中
        model.addAttribute("msg","美美");
        return "show";
    }
}

获取数据

    @RequestMapping("/testGetAttribute")
    public String testGetAttribute(ModelMap modelMap){
        System.out.println("testGetAttribute方法执行了");
        Object msg = modelMap.get("msg");
        System.out.println(msg);
        return "show";
    }

删除数据

    @RequestMapping("/delSessionAttribute")
    public String delSessionAttribute(SessionStatus status){
        System.out.println("delSessionAttribute方法执行了");
        // 将数据保存到request域中
        status.setComplete();
        return "show";
    }

响应数据和结果视图

返回值分类

字符串

show1.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>执行成功</h3>
    username:${user.username}
    password:${user.password}
    age:${user.age}
</body>
</html>

response.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="/demo3/user/testString">testString</a>
</body>
</html>

UserController.java

package com.bjpowernode.controller;

import com.bjpowernode.Pojo.User1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(value = "user")
public class UserController {

    @RequestMapping("testString")
    public String testString(Model model){
        System.out.println("testString方法执行了");
        //模拟从数据库中查询出USer对象
        User1 user1 = new User1("美美","123",20);
        //model
        model.addAttribute("user",user1);
        return "show1";
    }
}

void

如果没有返回值,会默认访问路径名+.jsp的文件,所以需要使用请求转发或者重定向或者直接响应来实现对访问的处理

     @RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("testvoid方法执行了");
        //请求转发
        //request.getRequestDispatcher("/WEB-INF/view/show1.jsp").forward(request,response);

        //重定向
        response.sendRedirect(request.getContextPath()+"/index.jsp");
        return;

    }

ModelAbdView

 @RequestMapping(value = "/testModeAndView",method = RequestMethod.POST)
    public ModelAndView testModeAndView(){
        //使用这个方法处理请求。能处理请求的方法叫做控制器方法
        //调用servlet对象,处理请求,返回数据
        ModelAndView mv = new ModelAndView();
        //把数据放入到request作用域
        mv.addObject("msg","处理了second.do请求");
        mv.addObject("fun","执行了second.do方法");

        //指定视图,setViewName("视图的完整路径") 对视图执行转发操作
        mv.setViewName("show");
        return mv;
    }

转发和重定向

    @RequestMapping("testForwordOrRedirect")
    public String  testForwordOrRedirect()  {
        System.out.println("testForwordOrRedirect方法执行了");
        //请求转发
        //return "forward:/WEB-INF/view/show1.jsp";
        //重定向
        return "redirect:../index.jsp";
    }

ResponseBody响应json数据

  1. DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置。
  • mov:resource标签配置不过滤
    • location元素表示webapp目录下的包下的所有文件
    • mapping元素表示以/static开头的所有请求路径,如/static/a或者/static/a/b

配置前端控制器,不拦截某些静态资源

    <!--前端控制器,哪些静态资源不拦截-->
    <mvc:resources mapping="/js/**" location="/js"/>

导入jar包

<dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.0</version>
</dependency>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>第一个springmvc</title>
    <script type="text/javascript" src="./js/jquery-1.7.2.js"></script>
    <script type="text/javascript">
        $(function () {
            //绑定事件
            $("#aa").click(function () {
                $.ajax({
                    url:"/demo3/user/returnvoidajax",
                    contentType:"application/json;charset=UTF-8",
                    data:'{"username":"lisi","password":"1234","age":20}',
                    dataType:"json",
                    type:"post",
                    success:function (data) {
                        alert(data.username+data.password+data.age);
                    }
                })
            })
        });
    </script>
</head>
<body>
    <input id="aa" type="button" value="发起Ajax请求" >
</body>
</html>
    @RequestMapping(value = "returnvoidajax",method = RequestMethod.POST)
    public @ResponseBody User1 returnvoidajax(@RequestBody User1 user)  {
        System.out.println("returnvoidajax方法执行了");
        //客户端发送ajax请求,传的是json字符串,后端把json字符串封装到user对象中
        System.out.println(user);
        //做响应,模拟查询数据库
        user.setUsername("hha");
        user.setAge(22);
        return user;


    }

SpringMVC实现文件上传

文件上传的回顾

文件上传的必要前提

  1. form表单的enctype取值必须是:multipart/form-data

默认值是:application/x-www-form-urlencoded
enctype:是表单请求正文的类型

  1. method属性取值必须是Post
  2. 提供一个文件选择域

    借助第三方组件实现文件上传

    使用Commons-fileupload组件实现文件上传,需要导入该组件相应支撑jar包:Commons-fileupload和commons-io.commons-io不属于文件上传组件开发jar文件。

    传统方式实现文件上传

    导入依赖

     <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
       <version>2.4</version>
     </dependency>
    
     <dependency>
       <groupId>commons-fileupload</groupId>
       <artifactId>commons-fileupload</artifactId>
       <version>1.3.3</version>
     </dependency>
    

    前端页面

    <h3>文件上传</h3><br/>
     <form action="/demo3/user/fileUpload1" method="post" enctype="multipart/form-data">
         选择文件:<input type="file" name="upload"/><br/>
         <input type="submit" value="上传"><br/>
     </form>
    

    文件上传

     @RequestMapping("/fileUpload1")
     public String fileUpload(HttpServletRequest request) throws Exception {
         System.out.println("文件上传...");
         //使用fileupload组件完成文件上传
         //上传的位置
         String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
         //判断路径是否存在
         File file = new File(realPath);
         if (!file.exists()){
             //创建文件夹
             file.mkdir();
         }
    
         //解析request对象,获取上传文件项
         DiskFileItemFactory factory = new DiskFileItemFactory();
         ServletFileUpload upload = new ServletFileUpload(factory);
         //解析request
         List<FileItem> fileItems = upload.parseRequest(request);
         //遍历
         for (FileItem item : fileItems) {
             //进行判断,当前item对象是否是上传文件项
             if(item.isFormField()){
                 //说明普通表单项
             }else {
                 //说明上传文件项
                 //获取上传文件的名称
                 String name = item.getName();
                 //把文件的名称设置唯一值,uuid
                 String uuid = UUID.randomUUID().toString().replace("-", "");
                 name=uuid+"-"+name;
                 //完成文件上传
                 item.write(new File(realPath ,name));
                 //删除临时文件
                 item.delete();
             }
         }
         return "show1";
     }
    

SpringMVC方式实现文件上传

设置文件上传对象和大小

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10485760"/>
    </bean>
    @RequestMapping("/fileUpload2")
    public String fileUpload2(HttpServletRequest request, MultipartFile upload) throws Exception {
        System.out.println("springmvc文件上传...");
        //使用fileupload组件完成文件上传
        //上传的位置
        String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
        //判断路径是否存在
        File file = new File(realPath);
        if (!file.exists()){
            //创建文件夹
            file.mkdir();
        }

        //说明上传文件项
        //获取上传文件的名称
        String name = upload.getOriginalFilename();
        //把文件的名称设置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        name=uuid+"-"+name;
        //完成文件上传
        upload.transferTo(new File(realPath,name));
        return "show1";
    }

SpringMVC跨服务器方式文件上传

  1. 搭建图片服务器
  • 根据文档配置tomcat9的服务器,现在是两个服务器
  • 导入资料中项目,作为图片服务器使用
  1. 实现SpringMVC跨服务器方式文件上传
  • 导入开发需要的jar包

    导入jar包

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
      </dependency>
    
      <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.3</version>
      </dependency>
    
      <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.18.1</version>
      </dependency>
    
      <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-core</artifactId>
        <version>1.18.1</version>
      </dependency>
    

    编写控制器实现上传图片

      @RequestMapping("/fileUpload3")
      public String fileUpload3(MultipartFile upload) throws Exception {
          System.out.println("跨服务器文件上传...");
          //定义上传文件服务器路径
          String path="http:localhost:9090/uploads/";
    
          //说明上传文件项
          //获取上传文件的名称
          String name = upload.getOriginalFilename();
          //把文件的名称设置唯一值,uuid
          String uuid = UUID.randomUUID().toString().replace("-", "");
          name=uuid+"-"+name;
    
          //创建客户端对象
          Client client = Client.create();
          //和图片服务器连接
          WebResource webResource = client.resource(path + name);
          //完成文件上传
          webResource.put();upload.getBytes();
          return "show1";
      }
    

    SpringMVC的异常处理

    处理异常的思路

  1. Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatherServlet找异常处理器进行异常处理

    SpringMVC的异常

  2. 编写自定义异常类(做提示信息的) ```java package com.bjpowernode.exception;

public class SysException extends Exception{ //存储提示信息 private String message;

public String getMessage() {
    return message;
}

public void setMessage(String message) {
    this.message = message;
}

public SysException(String message) {
    this.message = message;
}

}


2. 编写异常处理器
```java
package com.bjpowernode.exception;

public class SysException extends Exception{
    //存储提示信息
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public SysException(String message) {
        this.message = message;
    }
}
package com.bjpowernode.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 异常处理器
 */
public class SysExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        //获取异常对象
        SysException exception=null;
        if (exception instanceof SysException){
            exception=(SysException)e;
        }else {
            exception=new SysException("系统正在维护...");
        }
        //创建ModelAndView对象
        ModelAndView mv = new ModelAndView();
        mv.addObject("errorMsg",exception.getMessage());
        mv.setViewName("error");
        return mv;
    }
}


@RequestMapping("/testException")
    public String testException() throws SysException {
        System.out.println("testException执行了");
        //模拟异常
        try {
            int a=10/0;
        } catch (Exception e) {
            //打印异常信息
            e.printStackTrace();
            throw new SysException("查询所有用户出现错误了...");
        }
        return "show1";
    }
  1. 配置异常处理器(跳转到提示页面) ```java

<%— Created by IntelliJ IDEA. User: lhd Date: 2021-07-29 Time: 22:56 To change this template use File | Settings | File Templates. —%> <%@ page contentType=”text/html;charset=UTF-8” language=”java” %>

${errorMsg}
<a name="ypD47"></a>
# 拦截器
<a name="Cz0Sy"></a>
## 拦截器的作用
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于处理器进行预处理和后处理<br />用户可以自己定义一些拦截器来实现特定的功能。<br />它也是AOP思想的具体应用。我们想要自定义拦截器,要求必须实现:HandlerInterceptor接口。
<a name="fE2vq"></a>
## 过滤器和拦截器的区别

- 过滤器是servlet规范中的一部分,任何java web工程都可以使用
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程能使用
- 过滤器在url-pattern中配置/*之后,可以对所有要访问的资源拦截
- 拦截器它只是会拦截访问的控制方法,如果访问的是jsp,html,css,image或者js是不会进行拦截的。

<a name="LXA9e"></a>
## 自定义框架的步骤

1. 编写拦截器类,实现接口HandlerInterceptor
```java
package com.bjpowernode;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义拦截器
 */
public class MyInterceptor1 implements HandlerInterceptor{
    /**
     * 预处理,controller方法执行前
     * @param request
     * @param response
     * @param handler
     * @return true 放行,执行下一个拦截器,如果没有,执行controller中的方法,为false时会被拦截,不会执行controller中的方法
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1执行了...前");
        //请求转发
//        request.getRequestDispatcher("error.jsp").forward(request,response);
        return true; 
    }
    /**
     * 后处理方法,controller方法执行后
     * @param request
     * @param response
     * @param handler
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor1执行了...后");
    }

    /**
     * show.jsp页面执行后,该方法会执行
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor1执行了...最后");
    }

}
  1. 配置拦截器
    <!--配置拦截器-->
     <mvc:interceptors>
         <mvc:interceptor>
             <!--要拦截的具体方法-->
             <mvc:mapping path="/user/*"/>
             <!--不要拦截的方法-->
             <!--<mvc:exclude-mapping path=""/>-->
             <!--配置拦截器对象-->
             <bean class="com.bjpowernode.MyInterceptor1"/>
         </mvc:interceptor>
     </mvc:interceptors>