SpringMVC学习笔记

SpringMVC

一、SpringMVC的基本概念

SpringMVC是Spring提供的一个实现了Web MVC设计模式的轻量级框架。

SpringMVC是基于spring的一个框架,实际上就是spring的一个模块,专门是做web开发的。可以理解为servlet的一个升级。

Web开发的底层是servlet,框架是在servlet基础上面加入一些功能,让web开发更方便。
SpringMVC能够创建对象,放入到SpringMVC容器中,springmvc容器中放的是控制器对象,我们要做的就是使用@Controller创建控制器对象,将对象放到springmvc容器中,把创建的对象作为控制器使用。这个控制器对象能接收用户的请求,显示处理结果,当作一个servlet使用。
使用@Controller注解创建的对象是一个普通对象,并不是Servlet,springmvc赋予控制器对象一些额外的功能。
SpringMVC中有一个对象是Servlet:DispatcherServlet(中央调度器)
DispatcherServlet:负责接收用户的所有请求,之后DispatcherServlet把请求转发给Controller对象,最后Controller对象处理请求。
在这里插入图片描述

SpringMVC的具体特点

1.SpringMVC是spring框架的一部分,可以方便的利用Spring所提供的其他功能
2.灵活性强,易于与其他框架进行集成
3.提供了一个前端控制器DIspatcherServlet,使开发人员无需额外开发控制器对象
4.可自动绑定用户输入,并能正确的转换数据类型
5.内置了常见的校验器,可以校验用户输入。如果校验不能通过,就会重定向到输入表单
6.支持国际化。可以根据用户区域显示多国语言
7.支持多种试图技术。它支持JSP、Velocity和FreeMarker等识图技术
8.使用给予XML的配置文件,在编辑后,不需要重新编译应用程序

回顾MVC

什么是MVC
(1)MVC是Model模型(dao,service)、View视图图(jsp)、Controller控制(Servlet)的简写,是一种软件设计规范
(2)MVC是通过将业务逻辑、数据、视图分离的方法来组织代码
(3)MVC的主要作用是降低了视图层与业务逻辑层之间的耦合性
(4)MVC是一种架构模式,不同的MVC存在差异
在这里插入图片描述

Model(模型)是指模型表示业务规则。在MVC三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样的模型能为多个视图提供数据,由于应用于模型的代码只需要写一次就可以被多个视图重用,所以就减少了代码的重复性

View(视图)是指用户看到并与之交互的界面。MVC的优点之一就是它能够为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操作的方式

Controller(控制器)是指控制器接受用户的输入并调用Model和View去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构建去处理请求,然后再决定用哪个视图来显示返回的数据

二、第一个springmvc项目

需求:用户在页面发送一个请求,请求交个springmvc的控制器来处理,并显示请求的处理结果(在结果页面显示一个欢迎语句)

实现步骤

1.新建web maven工程

在这里插入图片描述

2.加入依赖

加入spring-webmvc依赖,间接的把spring的依赖都加入项目jsp、servlet依赖

<!--  Servlet依赖  -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
<!--  springMVC依赖  -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
<!--   编码、编译和JDK版本  (可加可不加) -->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
3.在web.xml中注册springmvc框架的核心对象DispatcherServlet
(1)DispatcherServlet叫做中央调度器,是一个servlet,它的父类是继承HttpServlet
(2)DispatcherServlet也叫做前端控制器(front controller)
(3)DispatcherServlet负责接收用户请求,调用其他的控制器对象,并把请求的处理结果显示给用户

在Tomcat启动后,创建Servlet对象
     load-on-startup:表示Tomcat启动后创建对象的顺序,它的值是一个整数,数值越小,优先级越高,越先被创建(大于等于0的整数)

使用框架的时候,url-pattern可以使用两种值
  1.使用扩展名方式,语法*.xxx,其中后缀名xxx是自定义的扩展名。常用的有*.do、*.action、*.mvc等
   2.使用斜杠"/"

<servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--    自定义springMVC读取的配置文件的位置    -->
        <init-param>
<!--     SpringMVC的配置文件的位置属性       -->
            <param-name>contextConfigLocation</param-name>
<!--     
    指定自定义文件的位置
    springmvc创建容器对象,读取的配置文件默认路径是[/WEB-INF/<servlet-name>-servlet.xml]
-->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

在这里插入图片描述

4.创建一个发起请求的页面,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>
5.创建控制器类
(1)在类上面加入@Controller注解,创建对象,并放入到springmvc容器中
(2)在类中的方法上加入@RequestMapping注解

      在springmvc中是使用方法来处理用户提交的信息的。方法是自定义的,可以有多种返回值、多种参数、方法名称自定义

准备使用doSome方法处理some.do请求。
@RequestMapping:请求映射,作用是吧一个请求地址和一个方法绑定在一起,一个请求指定一个方法处理

属性: value 是一个String类型的数组,表示请求的URL地址(some.do)
value的值必须是唯一的,不能重复的。在使用时,推荐以“/”开头


位置: 1.用在方法的上面(常用)
2.类的上面

说明:使用RequestMapping修饰的方法叫做处理器方法或者控制器方法
使用RequestMapping修饰的方法可以通过处理请求的,类似servlet中的doGet、doPost

返回值:ModelAndView
model :数据,请求处理完后,要显示给用户的数据
view:视图,如jsp页面等

@Controller
public class MyController {
    @RequestMapping(value = "/some.do") // "/"表示根地址
    public ModelAndView doSome() {  // 类似于Servlet中的doGet、doPost方法
        // 处理some.do的请求。相当于service调用处理完成了。
        ModelAndView mv = new ModelAndView();
        // 添加数据
        mv.addObject("fun", "value");
        mv.addObject("msg", "用springmvc执行的doSome方法");
        // 指定视图
        // 框架对视图执行forward操作,request.getRequestDispatcher("/show.jsp").forward(...);
        mv.setViewName("/show.jsp");
        // 返回值
        return mv;
    }
}
6.创建一个结果页面,显示请求的处理结果
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>show.jsp从request作用域获取数据</h3>
    <br/>
    <br/>
    <h3>msg : ${msg}</h3>
    <h3>fun : ${fun}</h3>
</body>
</html>
7.创建springmvc配置文件
(1)声明组件扫描器,指定@Controller注解所在的包名
(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 http://www.springframework.org/schema/context/spring-context.xsd">

<!--  创建组件扫描器  -->
    <context:component-scan base-package="cn.edu.pdsu"></context:component-scan>
</beans>
8.启动Tomcat进行测试

在这里插入图片描述
点击 发送some.do请求
在这里插入图片描述

springmvc请求的处理流程

(1)发起some.do
(2)tomcat(web.xml–url–pattern)知道*.do的请求给DisPatcherServlet
(3)DispatcherServlet(根据springmvc.xml配置文件知道some.do–doSome())
(4)DispatcherServlet把some.do转发给MyController.doSome()方法
(5)框架执行doSome()把得到ModelAndView进行处理,转发到show.jsp

简化过程即:some.do----DispatcherServlet----MyController

在这里插入图片描述

三、视图解析器

假如某个用户知道在外面的项目中存在show.jsp页面,他直接在地址栏中输入进行访问,那么就不会经过中央调度器DispatcherServlet的调度,也就不会得到数据
在这里插入图片描述
这时我们应该考虑如何让用户无法访问到我们的show.jsp页面,可以将其放入WEB-INF目录下,此时我们的项目也要发生改动

mv.setViewName("WEB-INF/view/show.jsp");

当我们要设置多个视图页面时,其代码WEB-INF/view/xxx.jsp的前后缀都是相同的,这时我们可以使用视图解析器让代码更简洁,也可以达到当视图目录发生改变时做到一改全改

1、在springmvc.xml文件中创建视图解析器
<!--  声明 springmvc框架中的视图解析器,用来设置视图文件的路径  -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--    前缀:视图文件的路径    -->
        <property name="prefix" value="/WEB-INF/view/"></property>
<!--    后缀:视图文件的扩展名    -->
        <property name="suffix" value=".jsp"></property>
    </bean>
2、视图解析器的使用

当配置了视图解析器之后,可以使用逻辑名称(文件名)指定视图
框架会根据试题解析器的前缀+逻辑名称+后缀组成完整的路径。
这就是字符连接操作 WEB-INF/view/ + show1 + .jsp

mv.setViewName("show");
一个控制类可以有多个方法,一个方法可以处理多个请求
@Controller
public class MyController {
    @RequestMapping(value = {"/some.do","/first.do"}) 
    public ModelAndView doSome() {  
        ModelAndView mv = new ModelAndView() ;
        mv.addObject("fun","value") ;
        mv.addObject("msg" , "用springmvc执行的doSome方法") ;
        mv.setViewName("show");
        return mv;
    }

    @RequestMapping(value = "/other.do")
    public ModelAndView doOther() {
        ModelAndView mv = new ModelAndView();

        mv.addObject("first", "由中心控制器调度到OtherController");
        mv.addObject("msg" , "执行doOther方法") ;
        mv.setViewName("show1");
        return mv;
    }
}

点击some.do
在这里插入图片描述
点击other.do在这里插入图片描述
在地址栏中直接发送first.do请求
在这里插入图片描述

@RequestMapping注解

@RequestMapping: 属性值value:所有请求地址的公共部分叫做模块名称,放在类的上面

@RequestMapping("/test")
@Controller
public class MyController 
<p><a href="test/some.do">点击发送doSome请求</a></p>
<p><a href="test/other.do">点击发送doOther请求</a></p>

在这里插入图片描述
在这里插入图片描述
这里的test就叫做模块名称

@RequestMapping:请求映射

属性:method 表示请求的方式,它的值是RequestMethod类的枚举值
例如:表示get请求方式,RequestMethod.GET
post方法:RequestMethod.POST

// 指定some.do使用get请求方式
@RequestMapping(value = "/some.do",method = RequestMethod.GET) 
public ModelAndView doSome() {  
    ModelAndView mv = new ModelAndView() ;
    // 添加数据
    mv.addObject("fun","value,使用get方法接收请求") ;
    mv.addObject("msg" , "用springmvc执行的doSome方法") ;
    mv.setViewName("show");

    return mv;
}
// 指定other.do使用post请求方式
@RequestMapping(value = "/other.do",method = RequestMethod.POST)
public ModelAndView doOther() {
    ModelAndView mv = new ModelAndView();
    // 添加数据
    mv.addObject("first", "由中心控制器调度到OtherController,使用post方法");
    mv.addObject("msg" , "执行doOther方法") ;

    mv.setViewName("show1");
    return mv;
}
// 不指定请求方式,post、get都可以访问
@RequestMapping(value = "/first.do")
public ModelAndView doFirst(HttpServletRequest httpServletRequest , HttpServletResponse httpServletResponse , HttpSession session) {

    // 创建ModelAndView对象
    ModelAndView mv = new ModelAndView();
    // 添加数据
    mv.addObject("first", "由中心控制器调度到OtherController,没指定get/post方法");
    mv.addObject("msg" , "执行doOther方法") ;

    mv.setViewName("show2");
    return mv;
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>第一个springmvc项目</p>
<%--    get是默认的请求方式--%>
    <p><a href="test/some.do">点击发送doSome请求</a></p>

<%--   指定post请求方式 --%>
    <form action="test/other.do" method="post">
        <input type="submit" value="点击发送doOther请求post请求方式">
    </form>

    <form action="test/first.do" method="post">
        <input type="submit" value="点击发送doFirst请求">
    </form>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当我们将doOther的请求方式改为get时看看会发生什么情况
在这里插入图片描述

HTTP Status 405 - Request method ‘GET’ not supported

不支持get请求方式
在这里插入图片描述

处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可以在方法内直接使用,他们是放处理器方法的形参中的
在这里插入图片描述

*HttpServletRequest
*HttpServletResponse
*HttpSession
*请求中所携带的请求参数

前三者的使用

public ModelAndView doFirst(HttpServletRequest httpServletRequest , HttpServletResponse httpServletResponse , HttpSession session) {

我们试着使用HttpServletRequest获取参数

mv.addObject("first", "由中心控制器调度到OtherController,没指定get/post方法====" + httpServletRequest.getParameter("name"));

在这里插入图片描述

1、逐个接收请求参数

要求:处理器(控制器)方法的形参名和请求中参数名必须一致

<form action="other.do" method="get">
    姓名:<input type="text" name="myName" /><br/>
    年龄:<input type="text" name="myAge" /><br/>
    <input type="submit" value="提交">
</form>
@RequestMapping(value = "/other.do",method = RequestMethod.GET)
public ModelAndView doOther(String myName , int myAge) {

    // 创建ModelAndView对象
    ModelAndView mv = new ModelAndView();
    // 添加数据
    mv.addObject("myName", myName);
    mv.addObject("myAge" , myAge) ;

    mv.setViewName("show1");
    return mv;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>OtherController控制器执行doOther方法</p>
    <p>myName数据:${myName}</p>
    <p>myAge数据:${myAge}</p>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
框架接收请求参数
1.使用request对象接收请求参数
String strName = request.getParameter(“myName”);
String strAge = request.getParameter(“myAge”);
2.springmvc框架通过DispatcherServlet中央控制器调用MyController的doOther方法
调用方法是,按名称对应,吧接受的参数赋值给形参
doOther(strName,Integer.valueOf(strAge));
框架会提供类型转换的功能,将String类型转换为int,long,double等类型

提交参数时只写年龄不写姓名时不会出现问题
在这里插入图片描述
当只写姓名不写年龄时
在这里插入图片描述
HTTP Status 400 -
400的状态码是客户端错误,表示提交表单过程中发生了问题
由于框架提供的类型转换,当我们在填写表单时,没有填写age(或填写“abc”一类的字符串时)这一项,则此时strAge这一项将为空,Integer.valueOf("");将会出现类型转换异常,出现400的错误码

我们可以将int改为Integer类型,这样可以解决空串的问题,它将自动转换为null

public ModelAndView doOther(String myName , Integer myAge)

再次只填写姓名提交数据,此时的myAge值就相当于null
在这里插入图片描述

post请求方法乱码

在提交请求参数过程中,get请求方法中没有乱码
使用post方式提交请求有中文乱码,需要使用过滤器处理乱码问题
在这里插入图片描述
可以自定义,也可以使用框架中提供的过滤器CharacterEncodingFilter
web.xml

<!--  注册声明过滤器,解决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>

再次进行测试可以看到post请求的乱码情况被解决
在这里插入图片描述

请求中参数名和处理器方法的形参名不一样

@RequestParam:解决请求中参数名与形参名不一样的问题
属性:value 请求中的参数名
required 是一个boolean值,默认是true(当required为true时,其表示必须要有参数传进,否则将会产生400的错误码,
当required为false时,可以不传参数,不会产生错误,参数值默认为null)
位置:定义在处理器方法的形参前面

<form action="active.do">
    姓名:<input type="text" name="myName" /><br/>
    年龄:<input type="text" name="myAge" /><br/>
    <input type="submit" value="提交">
</form>
@RequestMapping(value = "/active.do")
public ModelAndView doSome(@RequestParam(value = "myName") String name , @RequestParam(value = "myAge") Integer age) {

    // 创建ModelAndView对象
    ModelAndView mv = new ModelAndView();
    // 添加数据
    mv.addObject("name", name);
    mv.addObject("age" , age) ;

    mv.setViewName("show2");
    return mv;
}

当我们直接访问active.do
在这里插入图片描述

没有参数的传入

当我们设置required属性为false时

public ModelAndView doSome(@RequestParam(value = "myName",required = false) String name , @RequestParam(value = "myAge",required = false) Integer age)

再次直接访问active.do可以看到没有抛出异常
在这里插入图片描述

2、对象接收参数

在实际开发中,逐个接收参数的方法当我们要接受多个参数时需要创建多个形参,所以就有了对象接收参数的方法
创建一个实体类

public class Student {
    private String name ;
    private int age ;

    public Student() {
        System.out.println("student的无参构造方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("setName方法");
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
          System.out.println("setAge方法");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
<form action="object.do">
    姓名:<input type="text" name="name" /><br/>
    年龄:<input type="text" name="age" /><br/>
    <input type="submit" value="提交">
</form>
@RequestMapping(value = "/object.do")
public ModelAndView doObject(Student student) {

    // 创建ModelAndView对象
    ModelAndView mv = new ModelAndView();
    // 添加数据
    mv.addObject("name", student.getName());
    mv.addObject("age" , student.getAge()) ;
    mv.addObject("student",student) ;

    mv.setViewName("show3");
    return mv;   // 表示本次请求的处理结果
}

从控制台可以看到,对象接收参数的方法是通过无参构造创建的对象,并通过set方法进行赋值的
在这里插入图片描述
在这里插入图片描述

处理器方法的返回值
1、返回ModelAndView(数据和视图)

若处理器方法处理完后,需要跳转到其他资源,且要在跳转的资源间传递数据,此时处理器方法返回ModelAndView比较好。当然,要返回ModelAndView,则处理器方法需要定义ModelAndView对象
在使用时,若该处理器方法只是进行跳转而不进行数据传递或只是传递数据而不进行资源的跳转(如对页面的Ajax异步请求),则此时若使用ModelAndView类型作为返回值则将有一部分是多余的:即要么Model多余,要么View多余。此时在使用ModelAndView作为返回值不太合适。
在我们上面的实例中所返回的值都是ModelAndView类型的,既有资源间的跳转,也有数据的传递

2、返回String表示视图

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器可以将其转换为物理视图地址。

返回内部资源逻辑视图名

若要跳转的资源为内部资源,则视图解析器可以使用InternalResourceViewResolver内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的prefix前缀和suffix后缀相结合即可形成要访问的URL。

Controller控制器

@Controller
public class MyController {
    @RequestMapping(value = "/returnString.do")
    public String doSome(String name,Integer age) {
        System.out.println("doReturnView,name =" + name + "  age= " + age);
        // 返回值,show在这里表示逻辑视图名称,项目中配置了视图解析器
        return "show";
    } 	

index.jsp页面代码

<body>
    <p>处理器方法返回String表示视图名称</p>
    <form action="returnString.do" method="post">
        姓名:<input type="text" name="name"><br>
        年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交">
    </form>
</body>

返回结果的页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>返回String类型</h3><br/>
    <h3>msg数据:${name}</h3>
    <h3>fun数据:${age}</h3>
</body>
</html>

测试
在这里插入图片描述
可以看到并无数据传输过来
在这里插入图片描述
若想要有返回值传输,者可以设置HttpServletRequst在形参列表中用于传输数据

@RequestMapping(value = "/returnString.do")
public String doSome(String name, Integer age, HttpServletRequest request) {
    System.out.println("doReturnView,name =" + name + "  age= " + age);
    request.setAttribute("name" , name);
    request.setAttribute("age" , age);
    // 返回值,show在这里表示逻辑视图名称,项目中配置了视图解析器
    return "show";
}

可以看到利用String的返回类型把数据传输到了跳转的页面
在这里插入图片描述
处理器方法返回String,使用完整视图路径,这时就不要用视图解析器了,否则将会报404,路径找不到

// 处理器方法返回String,使用完整视图路径,这时就不要用视图解析器了,否则将会报404,路径找不到
@RequestMapping(value = "/returnString1.do")
public String doSome1(String name, Integer age, HttpServletRequest request) {
    System.out.println("----doReturnView----,name =" + name + "  age= " + age);
    request.setAttribute("name" , name);
    request.setAttribute("age" , age);
    return "/WEB-INF/view/show.jsp";
}
3、返回void

对于处理器方法返回void的应用场景,Ajax响应
void不能表示数据,也不能表示视图,在处理Ajax的时候,可以使用void返回值。通过HttpServletResponse输出数据,响应Ajax请求
Ajax请求服务器端返回的就是数据,和试图无关

导入jQuery的类库
在这里插入图片描述
在pom文件中导入jackson的相关依赖

<!-- Jackson依赖 -->
<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>
</dependencies>
// 处理器方法返回void,响应Ajax请求
@RequestMapping(value = "/returnVoid-ajax.do")
public void doSome2(String name, Integer age, HttpServletResponse response) throws Exception {
    System.out.println("----doReturnVoid-ajax----,name =" + name + "  age= " + age);
    // 处理Ajax,使用json做数据格式
    // 假使service调用完成,使用Student表示处理结果
    Student student = new Student() ;
    student.setName(name);
    student.setAge(age);

    String json = "" ;
    // 把结果数据转为json格式数据
    if(student!=null) {
        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();
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-1.7.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $("button").click(function () {
                // alert("button click");
                $.ajax({
                    url:"returnVoid-ajax.do",
                    data:{
                        name:"zhangsan",
                        age:20
                    },
                    type:"post",
                    dataType:"json",
                    success:function (resp) {
                        // resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}
                        // jQuery会把字符串转为json对象,赋值给resp形参
                        alert(resp);
                        alert(resp.name + "   " + resp.age)
                    }
                })
            })
        })
    </script>
</head>
<body>
    <p>处理器方法返回String表示视图名称</p>
    <form action="returnString.do" method="post">
        姓名:<input type="text" name="name"><br>
        年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交">
    </form>

    <p>处理器方法返回String表示视图名称1(完整路径)</p>
    <form action="returnString1.do" method="post">
        姓名:<input type="text" name="name"><br>
        年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交">
    </form>

    <br>
    <button id="but">发起Ajax请求</button>
</body>
</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、返回对象Object

处理器方法也可以返回Object对象。这个Object可以是Integer、String、自定义对象、map、list等。但返回的对象不少作为逻辑视图显示的,而是作为直接显示在页面的数据出现的
返回对象需要使用@ResponseBody注解,将其转换为JSON格式的数据放到响应体中。

实现步骤:
1.加入JSON的工具库的依赖,springmvc默认使用Jackson
2.在springmvc的配置文件中加入<mvc:annotation-driven>注解驱动
3.在处理器方法的上面加入@ResponseBody注解

springmvc处理器方法返回Object,可以转为JSON输出到浏览器,相应ajax的内部原理
1.<mvc:annotation-driven>注解驱动
注解驱动的实现功能是完成java对象到JSON、xml、text、二进制等数据格式的转换
<mvc:annotation-driven>加入到springmvc的配置文件中后,会自动创建HttpMessageConverter接口的7个实现类对象,包括MappingJackson2HttpMessageConverter(使用jackson工具库中的ObjectMapper实现java对象转换为字符串)
HttpMessageConveter接口:消息转换器
功能:定义了java转为json、xml等数据格式的方法。这个接口有很多实现类。这些实现类完
成java对象到json、java对象到xml、java对象到二进制数据的转换

以下两个方法是控制器类把结果输出给浏览器时所使用的的
在这里插入图片描述
例如有一个处理器方法

/**
 * 处理器方法返回一个student,通过框架转为json数据格式,响应Ajax请求
 * @ResponceBody:把处理器方法返回对象转化位json对象后,通过HttpServletResponse输出给浏览器
 *  写在在方法的上面
 */
@RequestMapping("/returnStudent.do")
@ResponseBody
public Student doStudentObject(String name , Integer age) {
    Student student = new Student() ;
    student.setName("张三");
    student.setAge(20);
    return student ;
}

(1)canWrite作用检查处理器方法的返回值,能不能转换为var2表示的数据格式。检查student(“张三”,20)能不能转换为var2表示的数据格式。如果能转为json格式,canWrite返回true
MediaType:表示数据格式的,例如jsom、xml等等。
(2)write:把处理器方法的返回值对象调用Jackson中的ObjectMapper转换为json字符串
json = om.writeValueAsString(student);

2.@ResponseBody注解
放到处理器方法上面,通过HttpServletResponse输出数据,相应ajax的请求

在springmvc 的配置文件中添加注解驱动

<!--  添加注解驱动  -->
<mvc:annotation-driven></mvc:annotation-driven>

修改ajax请求的地址
在这里插入图片描述
测试获得数据
在这里插入图片描述

5、返回List集合

创建控制方法返回List集合类型

/**
 * 处理器方法返回List集合
 */
@RequestMapping("returnStudentList.do")
@ResponseBody
public List<Student> doReturnList(String name , Integer age) {
    List<Student> studentList = new ArrayList<>() ;
    Student student = new Student() ;
    student.setName("张三");
    student.setAge(25);
    studentList.add(student) ;

    student = new Student() ;
    student.setName("李四");
    student.setAge(30);
    studentList.add(student) ;

    return studentList ;
}

我们可以先不去该ajax请求的请求地址,先去直接访问 returnStudentList.do 这个地址,看看会返回什么格式的数据类型。
在这里插入图片描述
在这里插入图片描述
可以看到返回的是一个数组类型的学生对象

这时我们再去更改Ajax的请求地址,以及使用each()函数循环输出list集合中的数据
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
list集合中的数据是有序输出的

6、返回String类型的文本数据
/**
 * 处理器方法返回String类型表示数据
 * 如何区分返回值String表示的是数据还是视图呢?
 * 只需要我们看一下方法上有没有@ResponseBody注解即可
 * 如果有@ResponseBody注解,返回String就是数据,反之就是视图
 */
@RequestMapping("/returnStringData.do")
@ResponseBody
public String returnString(String name , Integer age) {
    return "这里返回的String是一个String类型的数据" ;
}

index.jsp页面,修改请求地址和数据的输出的形式
在这里插入图片描述
启动程序,到页面上点击按钮发送请求却并没有窗口弹出,原因是因为浏览器无法将一个字符串转化为json格式类型的数据,而它为什么要将字符串转化为json类型的数据呢?
在这里插入图片描述
可以看到我们在index.jsp页面中ajax请求中的设置,我们只需要将这个数据格式的设置去掉或者将json改为text即可
在这里插入图片描述
再次点击按钮发送请求即可的到数据
在这里插入图片描述
至于得到得数据得乱码问题,是由于浏览器使用的是Content-Type的默认值是text/plain;charset=ISO-8859-1,我们需要使用@RequestMapping中的produces属性来指定新的Content-Type属性
在这里插入图片描述
可以看到解除了乱码,Content-Type的值也改变了
在这里插入图片描述

解读<url-pattern/>

配置详解
(1).do
在没有特殊要求的情况下,SpringMVC的中央调度器DispatcherServlet的常使用后缀匹配方式,如
.do、*.action等。
(2)/
可以写为/。因为DispatcherServlet会将静态资源的获取请求,如css、js、jpg、png等资源的获取请求当作是一个普通的Controller请求。中央调度器会调用处理器映射器为其查找响应的处理器。当然也是找不到的,所以在这种情况下,所以的静态资源获取请求也均会报404错误。

tomcat本身是能处理静态资源的访问的,静态资源包括html、css、图片、js文件等
tomcat的web.xml文件中有一个名叫default的servlet,是在服务器启动时创建的

<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的请求

当我们在DispatcherServlet中的<url-pattern/>中使用’/‘时,就会无法访问到静态资源,因为当项目中使用了’/'后,就会替代tomcat中的default。导致所有的静态资源都交给DispatcherServlet处理,但是默认情况下DispatcherServlet没有处理静态资源的能力

示例:
当我们的web.xml配置文件中未使用’/'时,访问index.jsp页面,其中index.jsp页面包括两个静态资源,一个是一张图片,另一个是html网页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>静态资源</p>
    <form action="some" method="post">
        姓名:<input type="text" name="name"><br>
        年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交">
    </form>
    <a href="html/test.html">一个html页面</a>
    <img src="img/1.jpg">
</body>
</html>

在这里插入图片描述
启动程序,访问结果
在这里插入图片描述
在这里插入图片描述
可以访问到静态资源

当我们在项目中使用’/'时
在这里插入图片描述
再次启动程序进行静态资源的访问,无论是图片还是html 的页面都访问不到了。
在这里插入图片描述
在这里插入图片描述

1、处理静态资源的第一种方式

在springmvc的配置文件中加入

<mvc:default-servlet-handler />

原理是加入这个标签后,框架会创建控制器对象DefaultServletHttpRequestHandler(类似我们创建的Controller控制器)
DefaultServletHttpRequestHandler这个对象可以把接收的请求转发给tomcat的default这个servlet
源码
在这里插入图片描述

2、处理静态资源的第二种方式

<mvc:resources mapping="" location="">加入这个标签后框架会创建ResourceHttpRequestHandler这个处理器对象,让这个处理器对象访问静态资源,不依赖于tomcat服务器。
mapping : 访问静态资源的uri地址,使用通配符**
location : 静态资源在项目中的目录位置

img/** : 表示img目录下的任意资源,比如img/1.jpg、img/user/2.jpg、img/user/stu/3.jpg

<mvc:resources mapping="img/**" location="img/" />
<mvc:resources mapping="html/**" location="html/" />

当静态资源较多时,我们就需要写多个相同的标签了,这时我们可以创建一个static包,将静态资源都放到static包下,这样就可以将静态资源整合到一起了,只写一个<mvc:resources>标签

<mvc:resources mapping="static/**" location="static/" />

在这里插入图片描述

绝对路径和相对路径

地址的分类:
1、绝对地址:带有协议名称的是绝对地址,如:http://www.baidu.com、ftp://202.123.32.2
2、相对地址:开头不带协议的,例如user/some.do、static/img/1.jpg。相对地址不能独立使用,必须要有一个参考地址,通过参考地址+相对地址本身才能找到指定的资源。
3、参考地址
在页面中访问的地址不加“/”
当地址没有以斜杠开头,例如 user/some ,当点击链接时,访问的地址是当前页面的地址加上链接的地址。

在页面中访问的地址加“/”
当访问地址以斜杠开头时,如 /user/some ,当点击链接时,访问的参考地址是服务器地址,也就是http://localhost:8080,则其访问的地址就是 http://localhost:8080/user/some

1、el表达式 ${pageContext.request.contentType}解决路径访问的问题
2、base标签,是html语言中的标签,表示当前页面中访问地址的基地址。页面中的所有没有 斜杠 开头的地址都以base标签中的地址为参考地址。

<%
    String basePath = request.getScheme() + "://" +
    request.getServletPath() + ":" + request.getServerPort() + 
    request.getContextPath() + "/" ;
%>

<base href="<%=basePath%>" />
请求转发(forward)

处理器方法返回ModelAndView时,需要在setViewName()指定的视图前添加forward,且此时的视图不再于视图解析器一起工作,这样可以在配置了解析器时指定不同位置的视图。视图页面必须写出相对于项目跟的路径。forward操作不需要视图解析器。
处理器方法返回String,在视图路径前面加入 forward: 视图完整路径

代码演示

@Controller
public class MyController {
    /**
     * 处理器方法返回ModelAndView,实现转发forward
     * 语法: setViewName("forward:视图文件完整路径")
     * forward特点:无论项目中有无视图解析器,都不和视图解析器一同使用
     * @param name
     * @param password
     * @return
     */
    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name , String password) {
        ModelAndView mv = new ModelAndView() ;
        mv.addObject("name",name) ;
        mv.addObject("password" , password) ;
        mv.setViewName("forward:/WEB-INF/view/show.jsp");
        return mv;
    }
}

在这里插入图片描述
index.jsp页面
在这里插入图片描述
show.jsp页面
在这里插入图片描述
请求转发的最主要的作用就是在我们配置了视图解析器之后,想要访问一个不在视图解析器解析路径下的页面资源时要用到,
例如下面的这个资源文件,它并不在WEB-INF/view路径下,我们要对其进行访问就需要用到请求转发操作。
在这里插入图片描述
只需要将上面的Controller类中的代码修改为下面即可
在这里插入图片描述

重定向(redirect)

框架对重定向的操作:
1、框架会把Model中的简单类型的数据转化为String使用,作为hello.jsp的get请求参数使用。目的是在doRedirect.do和hello.jsp两次请求之间传递参数
2、在目标hello.jsp页面可以使用参数集合对象 p a r a m 获 取 请 求 参 数 值 , 格 式 : {param}获取请求参数值,格式: param{pa}
代码:

/**
 * 处理器方法返回ModelAndView,实现重定向redirect
 * 语法setViewName("redirect:视图完整路径")
 * redirect特点:无论项目中有无视图解析器,都不和视图解析器一同使用
 * @param name
 * @param password
 * @return
 */
@RequestMapping(value = "/redirect.do")
public ModelAndView doRedirect(String name , String password) {
    ModelAndView mv = new ModelAndView() ;
    mv.addObject("name",name) ;
    mv.addObject("password" , password) ;
    mv.setViewName("redirect:/hello.jsp");
    return mv;
}

在这里插入图片描述
当我们通过重定向访问hello.jsp页面时,发现通过request作用域传输的数据没有被获取到,这是因为request的特殊性,request作用域只作用在一次的请求上,而重定向是发送了两次请求的
在这里插入图片描述
我们虽然不能通过request作用域获取到参数,但是我们可以看到在地址栏中存在参数name、password,而我们可以通过param获取到参数,param表示的是参数集合
在这里插入图片描述

<h3>姓名:${param.name}</h3>
<h3>密码:${param.password}</h3>

需要注意的是,redirect并不能访问到WEB-INF目录下的资源
在这里插入图片描述

四、异常处理

在springmvc框架中,使用的是统一的、全局的异常处理。把controller中的所以异常都集中到一个地方,采用的是aop的思想,把业务逻辑和异常分开,进行解耦操作。

注解:
@ExceptionHandler
@ControllerAdvice
异常处理步骤:
1、新建一个自定义的异常类MyException,在定义它的子类NameException、PasswordException
2、在Controller类中抛出异常
3、创建一个全局异常处理类
(1)在类上面加入@ControllerAdvice注解
(2)在类中定义的方法上加入@ExceptionHandler
4、创建处理异常的视图页面
5、创建springmvc的配置文件
(1)逐渐扫描器,扫描@Controller注解
(2)逐渐扫描器,扫描@ControllerAdvice注解
(3)声明注解驱动

MyController中代码

@Controller
public class MyController {

    @RequestMapping(value = "/some.do")
    public ModelAndView doRedirect(String name , String password) throws MyException {
        ModelAndView mv = new ModelAndView() ;
        // 根据请求参数抛出异常
        if(!"zs".equals(name)) {
            throw new NameException("姓名不是  zs  ") ;
        }
        if(!"123456".equals(password)) {
            throw new PasswordException("密码不是  123456  ") ;
        }
        mv.addObject("name",name) ;
        mv.addObject("password" , password) ;
        mv.setViewName("show");
        return mv;
    }
}

MyException类

public class MyException extends Exception{
    public MyException() {
        super();
    }

    public MyException(String message) {
        super(message);
    }
}

NameException类

/**
 * 当用户的姓名异常时抛出NameException
 */
public class NameException extends MyException{
    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }
}

PasswordException类

/**
 * 当用户的密码异常时抛出PasswordException
 */
public class PasswordException extends MyException{
    public PasswordException() {
        super();
    }

    public PasswordException(String message) {
        super(message);
    }
}

GlobalExceptionHandler类

/*
   @ControllerAdvice : 控制器增强 ,给控制器增加异常处理功能
   特点:需要让框架知道这个注解所在的包名,在springmvc配置文件中声明组件扫描器,
   指定@ControllerAdvice所在包
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    // 定义方法处理发生的异常
    /*
        处理异常的方法和控制器方法的定义方式一样,可以有多个参数,
        可以定义ModelAndView、String、void等类型的返回值

        形参:Exception,表示Controller中抛出的异常对象,通过形参可以获取发生的异常信息
        @ExceptionHandler(异常的class):表示异常类型,当发生此类异常时,有当前方法处理
     */
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception ex) {
        // 处理NameException的异常
        /*
            发生异常的处理逻辑
                1、记录日志文件
                2、发送通知
                3、给用户友好提示
         */
        ModelAndView mv = new ModelAndView() ;
        mv.addObject("msg","非zs用户不能访问") ;
        mv.addObject("exception",ex) ;
        mv.setViewName("nameError");
        return mv ;
    }

    // 处理PasswordException的异常
    @ExceptionHandler(value = PasswordException.class)
    public ModelAndView doPasswordException(Exception ex) {
        ModelAndView mv = new ModelAndView() ;
        mv.addObject("msg","密码非123456") ;
        mv.addObject("exception",ex) ;
        mv.setViewName("passwordError");
        return mv ;
    }

    // 处理其他不知名的异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception ex) {
        ModelAndView mv = new ModelAndView() ;
        mv.addObject("msg","发生了异常") ;
        mv.addObject("exception",ex) ;
        mv.setViewName("otherError");
        return mv ;
    }
}

jsp页面
index.jsp页面

<%@ 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>实现重定向</p>
    <form action="some.do">
        姓名:<input type="text" name="name" /><br/>
        密码:<input type="password" name="password" /><br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>

nameError.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>nameError.jsp页面</p>
    提示信息:${msg}<br/>
    系统消息:${exception.message}
</body>
</html>

passwordError.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>passwordError.jsp页面</p>
    提示信息:${msg}<br/>
    系统消息:${exception.message}
</body>
</html>

otherError.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>otherError.jsp页面</p>
    提示信息:${msg}<br/>
    系统消息:${exception.message}
</body>
</html>

show.jsp

<%@ 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>
    <h3>异常处理</h3><br/>
    <h3>姓名:${name}</h3>
    <h3>密码:${password}</h3>
</body>
</html>

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 http://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="cn.edu.pdsu"></context:component-scan>

<!--  声明 springmvc框架中的视图解析器,用来设置视图文件的路径  -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--    前缀:视图文件的路径    -->
        <property name="prefix" value="/WEB-INF/view/"></property>
<!--    后缀:视图文件的扩展名    -->
        <property name="suffix" value=".jsp"></property>
    </bean>

<!--  处理异常的注解驱动  -->
    <context:component-scan base-package="handler"></context:component-scan>
    <mvc:annotation-driven></mvc:annotation-driven>
</beans><?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 http://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="cn.edu.pdsu"></context:component-scan>

<!--  声明 springmvc框架中的视图解析器,用来设置视图文件的路径  -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--    前缀:视图文件的路径    -->
        <property name="prefix" value="/WEB-INF/view/"></property>
<!--    后缀:视图文件的扩展名    -->
        <property name="suffix" value=".jsp"></property>
    </bean>

<!--  处理异常的注解驱动  -->
    <context:component-scan base-package="handler"></context:component-scan>
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

当我们输入正确的姓名和密码时
在这里插入图片描述
姓名错误时
在这里插入图片描述


springmvc中的拦截器

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值