SpringMVC知识

目录

💞 SpringMVC文档

资源已上传-离线文档 spring-framework-5.3.8\docs\reference\html

💞 SpringMVC特点&概述

1.SpringMVC从易用性, 效率上, 比曾经流行的Struts2更好

2.SpringMVCWEB层框架 [解读: SpringMVC接管了Web层组件, 比如控制器, 视图, 视图解析, 返回给用户的数据格式, 同时支持MVC的开发模式/开发架构]

3.SpringMVC通过注解, 让POJO成为控制器, 不需要继承类或者实现接口

4.SpringMVC采用低耦合的组件设计方式, 具有更好地扩展性和灵活性

5.支持REST格式的URL请求

6.SpringMVC是基于Spring的, 也就是SpringMVC是在Spring基础上的. SpringMVC的核心包是 spring-web-5.3.8.jar, spring-webmvc-5.3.8.jar

💞 Spring SpringMVC SpringBoot的关系

1.Spring MVC只是Spring处理WEB层请求的一个模块/组件, Spring MVC的基石是Servlet[Java WEB]

2.Spring Boot是为了简化开发者的使用, 推出的封神框架(约定优于配置, 简化了Spring的配置流程), SpringBoot包含很多组件/框架. Spring就是最核心的内容之一, 也包含Spring MVC

3.它们大概的关系是: SpringBoot > Spring > Spring MVC

🍜Spring MVC 快速入门

🍜思路分析

🍜具体实现

1.创建springmvc web工程, 导入jar新建javaweb项目, 参考👉

2.创建src/applicationContext-mvc.xml容器文件

3.配置web.xml

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

    <!--配置前端控制器/中央控制器/分发控制器
    1.用户的请求都会经过它的处理
    -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置属性 contextConfigLocation, 指定DispatcherServlet 去操作的spring配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext-mvc.xml</param-value>
        </init-param>
        <!--在web项目启动时, 就自动地加载DispatcherServlet-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <!--说明
        1.这里我们配置的url-pattern是 /, 表示用户的请求都经过 DispatcherServlet
        2.这样配置也支持rest 风格的url请求
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

说明: 这两个配置文件不一样

4.web目录新建login.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<%--后面再填写?--%>
<form action="?">
    username: <input type="text" name="username"/><br/>
    password: <input type="password" name="password"/><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

5.在com.zzw.web包下新建UserServlet

java 复制代码
/*
1.如果我们使用了SpringMVC, 在一个类上标识@Controller
2.表示将该类视为一个控制器, 注入到容器
3.比原生servlet开发要简化很多
*/
@Controller
public class UserServlet {

     //编写方法, 响应用户的请求
    /**
     * 解读
     * 1. login() 方法是用于响应用户的登录请求
     * 2. @RequestMapper(value="/login"), 类似于我们原生Servlet
     *    配置的url-pattern, 就是给方法配置一个url映射
     * 3. 即当用户在浏览器输入 http://localhost:8080/web工程路径/login 就能够访问到login()
     * 4. return "login ok"; 表示返回结果给视图解析器(InternalResourceViewResolver)
     *    , 视图解析器会根据配置, 来决定跳转到哪个页面
     *
     *     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     *
     *         <property name="prefix" value="/WEB-INF/pages/"/>
     *         <property name="suffix" value=".jsp"/>
     *     </bean>
     *
     *     根据上面配置的 return "login_ok"; 就会转发到 /WEB-INF/pages/login_ok.jsp
     */
    @RequestMapping(value = "/login")
    public String login() {
        System.out.println("login ok...");
        return "login_ok";
    }
}

6.在web路径/WEB-INF/pages目录下 新建login_ok.jsp`

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆成功</title>
</head>
<body>
<h1>登陆成功~</h1>
</body>
</html>

7.配置applicationContext-mvc.xml

说明: InternalResourceViewResolver的父类UrlBasedViewResovler, 有属性prefix, suffix

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.zzw.web"/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置属性prefix 和 suffix-->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

8.配置Tomcat

9.配置login.jsp页面访问路径 参考->web工程路径专题

html 复制代码
<body>
<%--
这里需要回顾 javaweb-web工程路径专题
1. action="login" 表示url 是 http://localhost:8080/springmvc/login
   前提: 当前页面在web路径下, 相对路径为 http://localhost:8080/springmvc/login.jsp
2. action="/login" 表示url 是 http://localhost:8080/login
--%>
<form action="login">
    username: <input type="text" name="username"/><br/>
    password: <input type="password" name="password"/><br/>
    <input type="submit" value="登录">
</form>
</body>

🍜注意事项和细节

1.学习如何搭建一个springmvc项目, 初步理解springmvc工作流程

2.这里的UserServlet需要注解成@Controller, 我们称之为一个Handler处理器

3.UserServlet指定url时, 是可以省略的

4.关于SpringMVCDispatcherServlet的配置文件, 如果不在web.xml中指定applicationContext-mvc.xml, 默认在/WEB-INF/springDispatcherServlet-servlet.xml找这个配置文件 (推荐使用, 做修改, 并完成测试)

查看DispatcherServlet父类FrameworkServlet的源码

1)修改web.xml, 注销init-param配置节点

xml 复制代码
<!--配置前端控制器/中央控制器/分发控制器
1.用户的请求都会经过它的处理
-->
<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--解读
    1.如果我们没有配置 contextConfigLocation
    2.默认按照这样的位置去定位spring配置文件 /WEB-INF/springDispatcherServlet-servlet.xml
    -->

    <!--配置属性 contextConfigLocation, 指定DispatcherServlet 去操作的spring配置文件-->
    <!--<init-param>-->
    <!--    <param-name>contextConfigLocation</param-name>-->
    <!--    <param-value>classpath:springDispatcherServlet-servlet.xml</param-value>-->
    <!--</init-param>-->

    <!--在web项目启动时, 就自动地加载DispatcherServlet-->
    <load-on-startup>1</load-on-startup>
</servlet>

这时运行会报错. 因为在WEB-INF目录下找不到springDispatcherServlet-servlet.xml文件

2)剪切原applicationContext-mvc.xml/WEB-INF目录下, 文件名为: 你配置的DispatcherServlet名字-servlet.xml, 即/WEB-INF/springDispatcherServlet-servlet.xml

运行, 正常.

🍜SpringMVC执行流程分析

🍜RequestMapping

RequestMapping注解可以指定控制器/处理器的某个方法的请求的url

🍜可以修饰类和方法

1.说明: @RequestMapping注解可以修饰方法, 还可以修饰类. 当同时修饰类和方法时, 请求的url 就是组合 /类请求值/方法请求值

案例

1.com.zzw.web包下 新建UserHandler

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {

    /**
     * 1.method=RequestMethod.POST: 表示请求buy目标方法必须是 post
     * 2.RequestMethod 四个常用选项 POST, GET, PUT, DELETE[后面会详解]
     * 3.SpringMVC 控制器默认支持GET和POST两种方式
     *
     * buy()方法请求的url: http://ip:port/工程路径/user/buy
     * @return
     */
    @RequestMapping(value = "/buy", method = RequestMethod.POST)
    public String buy() {
        System.out.println("购买.");
        return "success";
    }
}

2.web路径/WEB-INF/pages目录 新建success.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>操作成功</title>
</head>
<body>
<h1>恭喜, 操作成功~</h1>
</body>
</html>

3.web路径下 新建request.jsp, 测试

html 复制代码
<body>
<%--解读
1. action="user/buy" 对应 url http://localhost:8080/工程路径/user/buy
--%>
<form action="user/buy" method="post">
    购买人: <input type="text" name="username"/><br/>
    购买量: <input type="password" name="nums"/><br/>
    <input type="submit" value="购买">
</form>
</body>

4.访问成功

🍜可以指定请求方式

1.说明: @RequestMapping可以指定请求方式(post/get/put/delete..), 请求的方式要和指定的一样, 否则报错.

2.SpringMVC控制器默认支持GETPOST两种方式, 也就是你不指定method, 可以接收GETPOST请求

3.应用案例

4.当你明确指定了method, 则需要按指定方式请求, 否则会报错.

🍜可以指定params和headers支持简单表达式

1.param1:表示请求必须包含名为param1的请求参数. 比如 params = "bookId"

2.!param1:表示请求不能包含名为param1的请求参数. 比如 params = "!bookId"

3.param1 = value1:表示请求包含名为param1的请求参数, 且其值必须为value1. 比如 params = "bookId=100"

4.param1 != value1:表示请求包含名为param1的请求参数, 但其值不能为value1. 比如 params = "bookId!=100"

5.{"param1=value1", "param2"}:请求必须包含名为param1, param2的两个请求参数, 且param1参数的值必须为value1. 比如params = {"bookId=100", "price"}

案例

1.修改UserHandler.java增加方法search

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    /**
     * 解读
     * 1. params="bookId" 表示请求该目标方法时, 必须给一个bookId参数, 值没有限定
     * 2. search(String bookId) 表示请求目标方法时, 携带的bookId=100, 就会将请求携带的 bookId
     *    对应的值, 赋给 String bookId
     * @param bookId
     * @return
     */
    @RequestMapping(value = "/find", params = "bookId", method = RequestMethod.GET)
    public String search(String bookId) {
        System.out.println("查询书籍 bookId=" + bookId);
        return "success";
    }
}

2.修改request.jsp

html 复制代码
<body>
<h1>演示params的使用</h1>
<a href="user/find?bookId=100">查询书籍</a>
</body>

3.操作成功

如果bookId改为bookIdx, 报错

细节1:如果需要有bookId参数, 并且值为100. 否则报错.

java 复制代码
@RequestMapping(value = "/find", params = "bookId=100", method = RequestMethod.GET)

修改request.jsp

html 复制代码
<body>
<h1>演示params的使用</h1>
<a href="user/find?bookId=200">查询书籍</a>
</body>

报错

细节2:需要有bookId参数, 并且值不为100. 否则报错.

java 复制代码
@RequestMapping(value = "/find", params = "bookId!=100", method = RequestMethod.GET)

修改request.jsp

html 复制代码
<body>
<h1>演示params的使用</h1>
<a href="user/find?bookId=100">查询书籍</a>
</body>

报错

🍜支持Ant 风格资源地址

1.?: 匹配文件名中的一个字符

2.*: 匹配文件名中的任意字符

3.**: 匹配多层路径

4.举例
/user/*/createUser:匹配/user/aaa/createUser, /user/bbb/createUserURL
/user/**/createUser:匹配/user/createUser, /user/aaa/bbb/createUserURL
/user/createUser??:匹配/user/createUseraa, /user/createUserbbURL

案例

1.修改UserHandler.java增加方法im

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {

    /**
     * 要求: 可以配置 /user/message/aa, /user/message/aa/bb/cc
     * @RequestMapping(value="/message/**") 表示可以匹配多层路径
     */
    @RequestMapping(value = "/message/**")
    public String im() {
        System.out.println("发送消息");
        return "success";
    }
}

2.修改request.jsp

html 复制代码
<body>
<hr><h1>演示Ant风格的请求资源方式</h1>
<a href="user/message/aa">发送消息1</a>
<a href="user/message/aa/bb/cc">发送消息2</a>
</body>

3.测试成功...

🍜配合@PathVariable 映射 URL 绑定的占位符

1.@RequestMapping可以配合@PathVariable映射URL绑定的占位符

2.这样就不需要在url地址上带参数名了, 更加的简洁明了.

案例

1.修改UserHandler.java增加方法register

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {

    /**
     * 前端页面: <a href="user/reg/Kristina/300">占位符的演示</a>
     * (value="/reg/{username}/{userId}"): 表示Kristina=>{username} 300=>{userId}
     *
     * @return
     */
    @RequestMapping(value = "/reg/{username}/{userId}")
    public String register(@PathVariable("username") String name,
                           @PathVariable("userId") int id) {
        System.out.println("接收到参数--" + "username=" + name + "--" + "userId=" + id);
        return "success";
    }
}

2.修改request.jsp

html 复制代码
<body>
<hr/><h1>占位符的演示</h1>
<a href="user/reg/Kristina/300">占位符的演示</a>
</body>

3.测试成功...

🍜注意事项和使用细节

1.映射的URL, 不能重复

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {

    @RequestMapping(value = "/hi")
    public String hi() {
        System.out.println("hi");
        return "success";
    }

    @RequestMapping(value = "/hi")
    public String hi2() {
        System.out.println("hi");
        return "success";
    }
}

启动或重新发布时, 会报错. to { [/user/hi]}: There is already 'userHandler' bean method

2.各种简写的方式

@RequestMapping(value="/buy",method=RequestMethod.POST) 等价 @PostMapping(value="/buy")

简写方式一览: @GetMapping @PostMapping @PutMapping @DeleteMapping

案例

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {

    /**
     * 5.@PostMapping(value="/buy") 等价 @Request(value="/buy", method=RequestMapping.POST)
     * @return
     */
    //@RequestMapping(value = "/buy", method = RequestMethod.POST)
    @PostMapping(value = "/buy")
    public String buy() {
        System.out.println("购买.");
        return "success";
    }
}

测试request.jsp

html 复制代码
<body>
<form action="user/buy" method="get">
    购买人: <input type="text" name="username"/><br/>
    购买量: <input type="password" name="nums"/><br/>
    <input type="submit" value="购买">
</form>
</body>

报错

3.如果我们确定表单或者超链接会提交某个字段数据比如email, 要求提交的参数名和目标方法的参数名保持一致.

案例

1.修改UserHandler.java增加方法hello3

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    /**
     * hello3(String email) 表示如果我们的请求参数有 email=xx, 就会将传递的值, 赋给String email
     * , 要求名称保持一致, 如果不一致, 那么接收不到数据, 而是null
     * @param email
     * @return
     */
    @RequestMapping(value = "/hello3")
    public String hello3(String email) {
        System.out.println("email=" + email);
        return "success";
    }
}

2.测试 浏览器地址栏 输入http://localhost:8080/springmvc/user/[email protected], 一定要注意提交参数名和后台方法的形参名保持一致, 否则后端接收不到参数


3.如果输入一个错误的参数名, 那么后端接收不到数据

🍜作业布置

1.熟悉SpringMVC的执行流程图

2.熟悉@RequestMapping注解的使用方式

3.编写一个表单, 以Post的方式提交Computer信息, 后端编写ComputerHandler, 可以接收到信息.

代码实现

1.修改request.jsp

html 复制代码
<body>
<h1>电脑信息</h1>
<form action="?" method="post">
    品牌:<input type="text" name="brand"/><br/>
    价格:<input type="text" name="price"/><br/>
    数量:<input type="text" name="nums"/><br/>
    <input type="submit" value="提交">
</form>
</body>

2.com.zzw.web包下 新建ComputerHandler

java 复制代码
@RequestMapping(value = "/computer")
@Controller
public class ComputerHandler {

    //这里一定要注意, info方法的形参名需要和请求的参数名保持一致
    @PostMapping(value = "/info", params = {"brand", "price", "nums"})
    public String info(String brand, String price, String nums) {
        System.out.println("电脑信息--brand=" +  brand
                + "--price=" + price + "--nums" + nums);
        return "success";
    }
}

3.配置页面访问路径

html 复制代码
<form action="computer/info" method="post">

4.测试成功...

🍜Postman

🍜Postman是什么

1.Postman是一款功能超级强大的用于发送HTTP请求的 测试工具.

2.做WEB页面开发和测试的人员常用工具.

3.创建和发送任何的HTTP请求(GET/Post/Put/Delete)

🍜Postman相关资源

官方网站: https://www.postman.com/

文档: https://learning.postman.com/docs/introduction/overview/

下载地址: https://www.postman.com/downloads/.

资料已上传...

🍜Postman安装

1.资料已上传, 下载后右键管理员身份打开即可安装(非常简单), Postman不会让你选择安装路径, 会直接安装, 一般安装在系统盘.

2.安装成功, 在桌面上有快捷图标. 双击打开Postman.

🍜Postman快速入门

●要求:使用Postmanhttp://www.baidu.com发出get请求, 得到返回的html格式数据

调整字体大小: File--Settings

调整页面大小: ctrl++, ctrl+ -

注册账号:(可选, 不注册不影响使用) 输入邮件, 用户名, 密码

1.创建Http Request, 如果你已经创建过, 会直接进入Workspace

File--New





2.发出请求

🍜Controller测试案例

需求说明: 使用Postman, 完成UserHandler方法的请求

1.完成请求

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/buy

2.请求的方式 -Post

3.确定请求的参数/数据 -无

4.确定Header有没有特殊的指定 -无 http协议

2.完成请求


使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/find

2.请求的方式 -Get

3.确定请求的参数/数据 -bookId=100

4.确定Header有没有特殊的指定 -无

3.完成请求


使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/message/aa/bb/cc

2.请求的方式 -Get/Post

3.确定请求的参数/数据 -无

4.确定Header有没有特殊的指定 -无

4.完成请求

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/reg/zzw/23

2.请求的方式 -Get/Post

3.确定请求的参数/数据 -无

4.确定Header有没有特殊的指定 -无

5.完成请求

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/hello3

2.请求的方式 -Get

3.确定请求的参数/数据 [email protected]

4.确定Header有没有特殊的指定 -无

🍜其它说明

1.创建 对应的Http Request, 放到已有的Collection


2.在Headers选项页, 增加 Content-Type applicatoin/json

3.因为是Post请求, 在Body选项填写Json数据/Furn数据

🍜作业布置

1.创建新的Collection, 命名为你的名字, 比如 zzwCollection

2.创建多个http request, 完成对UserHandler的各个方法的请求

测试1

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    @PostMapping(value = "/buy")
    public String buy() {
        System.out.println("购买.");
        return "success";
    }
}

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/buy

2.请求的方式 -Post

3.确定请求的参数/数据 -无

4.确定Header有没有特殊的指定 -无

测试2

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    @RequestMapping(value = "/find", params = "bookId=100", method = RequestMethod.GET)
    public String search(String bookId) {
        System.out.println("查询书籍 bookId=" + bookId);
        return "success";
    }
}

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/find

2.请求的方式 -Get

3.确定请求的参数/数据 -bookId=100

4.确定Header有没有特殊的指定 -无

测试3

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    @RequestMapping(value = "/message/**")
    public String im() {
        System.out.println("发送消息");
        return "success";
    }
}

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/message/aa/bb/cc

2.请求的方式 -Get/Post

3.确定请求的参数/数据

4.确定Header有没有特殊的指定 -无

测试4

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    @RequestMapping(value = "/reg/{username}/{userId}")
    public String register(@PathVariable("username") String name,
                           @PathVariable("userId") int id) {
        System.out.println("接收到参数--" + "username=" + name + "--" + "userId=" + id);
        return "success";
    }
}

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/reg/star/3000000

2.请求的方式 -Get/Post

3.确定请求的参数/数据 -无

4.确定Header有没有特殊的指定 -无

测试5

java 复制代码
@RequestMapping(value = "/user")
@Controller //UserHandler就是一个处理器/控制器, 会注入到容器
public class UserHandler {
    @GetMapping(value = "/hello3")
    public String hello3(String email) {
        System.out.println("email=" + email);
        return "success";
    }
}

使用Postman测试Controller方法的步骤

1.确定请求的地址 url: http://localhost:8080/springmvc/user/hello3

2.请求的方式 -Get

3.确定请求的参数/数据 [email protected]

4.确定Header有没有特殊的指定 -无

🍜Rest

🍜Rest基本介绍

●说明

1.REST:Representational State Transfer. (资源)表现层状态转化. 是目前流行的请求方式. 它结构清晰, 很多网站使用.

2.HTTP协议里面, 四个表示操作方式的动词: GET, POST, PUT, DELETE, 它们分别对应四种基本操作: GET用来获取资源, POST用来新建资源, PUT用来更新资源, DELETE用来删除资源

3.实例. 传统的请求方法:
getBook?id=1 GET
delete?id=1 GET
update POST
add POST

4.说明: 传统的url是通过参数来说明crud的类型, rest是通过get/post/put/delete来说明crud的类型

REST的核心过滤器

1.当前的浏览器只支持post/get请求, 因此为了得到put/delete的请求方式需要使用Spring提供的HiddenHttpMethodFilter过滤器进行转换.

2.HiddenHttpMethodFilter:浏览器form表单只支持GETPOST请求, 而DELETE, PUTmethod并不支持, Spring添加了一个过滤器, 可以将这些请求转换为标准的http方法, 使得支持GET, POST, PUTDELETE请求.

3.HiddenHttpMethodFilter只能对post请求方式进行转换.

4.这个过滤器需要在web.xml中配置

🍜Rest实例

需求说明

小明去书店买书, 完成购买书籍的增删改查

1.修改web.xml, 配置HiddenHttpMethodFilter

xml 复制代码
<!--配置HiddenHttpMethodFilter
1.作用是 把 以post方式提交的delete和put请求, 进行转换
2.配置url-pattern 是 /* 表示请求都经过 hiddenHttpMethodFilter的过滤
3.后面通过debug源码, 会看的很清楚.
-->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2.修改springDispatcherServlet-serlvet.xml 添加配置

xml 复制代码
<!--加入两个常规配置-->
<!--支持SpringMVC的高级功能, 比如JSR303校验, 映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将springmvc不能处理的请求, 交给tomcat处理, 比如css, js-->
<mvc:default-servlet-handler/>

3.在web路径下创建rest.jsp, 注意引入jquery, 测试查询/添加/删除/修改

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>rest测试</title>
</head>
<body>
<h3>Rest风格的crud操作案例</h3>
<br/><hr>
<h3>rest风格的url 查询书籍[get]</h3>
<a href="?">点击查询书籍</a>
<br/><hr>
<h3>rest风格的url 添加书籍[post]</h3>
<form action="?" method="?">
    name:<input name="bookName" type="text"/><br/>
    <input type="submit" value="添加书籍">
</form>
<br/><hr>
<h3>rest风格的url 删除一本书</h3>
<a href="?" id="?">删除指定id的书</a>
<br/><hr>
<h3>rest风格的url 修改书籍[put]</h3>
<form action="?">
    <input type="submit" value="修改书籍">
</form>
</body>
</html>

4.在com.zzw.web.rest下, 创建BookHandler.java

1.完成查询

java 复制代码
 //BookHandler 处理rest风格的请求-增删改查
@RequestMapping(value = "/user")
@Controller
public class BookHandler {

    //查询[GET]
    @GetMapping(value = "/book/{id}")
    public String getBook(@PathVariable("id") String id) {
        System.out.println("查询书籍 id=" + id);
        return "success";
    }
}

5.前端修改请求地址

html 复制代码
<h3>rest风格的url 查询书籍[get]</h3>
<a href="user/book/200">点击查询书籍</a>

2.完成添加
@PostMapping(value = "/book") @GetMapping(value = "/book/{id}")不重复

java 复制代码
//BookHandler 处理rest风格的请求-增删改查
@RequestMapping(value = "/user")
@Controller
public class BookHandler {

    //查询[GET]
    @GetMapping(value = "/book/{id}")
    public String getBook(@PathVariable("id") String id) {
        System.out.println("查询书籍 id=" + id);
        return "success";
    }

    //添加[POST]
    @PostMapping(value = "/book")
    public String addBook(String bookName) {
        System.out.println("添加书籍 bookName=" + bookName);
        return "success";
    }
}

前端修改请求地址

html 复制代码
<h3>rest风格的url 添加书籍[post]</h3>
<form action="user/book" method="post">
    name:<input name="bookName" type="text"/><br/>
    <input type="submit" value="添加书籍">
</form>

3.完成删除

java 复制代码
//BookHandler 处理rest风格的请求-增删改查
@RequestMapping(value = "/user")
@Controller
public class BookHandler {
    //查询[GET]
    @GetMapping(value = "/book/{id}")
    public String getBook(@PathVariable("id") String id) {
        System.out.println("查询书籍 id=" + id);
        return "success";
    }

    //添加[POST]
    @PostMapping(value = "/book")
    public String addBook(String bookName) {
        System.out.println("添加书籍 bookName=" + bookName);
        return "success";
    }

    //删除[DELETE]
    @RequestMapping(value = "/book/{id}", method = RequestMethod.DELETE)
    public String deleteBook(@PathVariable("id") String id) {
        System.out.println("删除书籍 id=" + id);
        //return "success";//[如果这样写会报错 JSP 只允许 GET、POST 或 HEAD]
        //解读
        //1. redirect:/user/success重定向
        //2. 会被解析成 /工程路径/user/success
        return "redirect:/user/success";//提示: 重定向不能重定向到WEB-INF下的资源, 所以需要借助successGeneral方法
    }

    //如果请求时 /user/success, 就转发到 success.jsp
    //successGeneral 对应的url http://localhost:8080/springmvc/user/success
    @RequestMapping(value = "/success")
    public String successGeneral() {
        return "success";//由该方法 转发到success.jsp页面
    }
}

知识点:

1.web路径/script目录下存放jquery文件, jquery复习

2.为什么前端做了一个操作后, 就可以被过滤器过滤处理, 定位到后端delete方法? 回答: HiddenHttpMethodFilter机制

3.return "success"; 会报以下错误

前端修改

html 复制代码
<head>
    <title>rest测试</title>
    <%--script标签建议放在head内--引入jquery--%>
    <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
    <script type="text/javascript">
        $(function () {//当页面加载完成后就执行
            // alert("ok...");
            //给删除超链接绑定一个点击事件
            $("#deleteBook").click(function () {
                // alert("点击....");
                //我们自己定义一个提交的行为
                $("#hiddenForm").attr("action", this.href);
                $("input:hidden").val("DELETE");
                $("#hiddenForm").submit();
                return false;//改变点击超链接的行为, 不再提交
            })
        });
    </script>
</head>
<body>
<h3>rest风格的url 删除一本书</h3>
<%--解读
 1. 默认情况下, <a href="user/book/600">删除指定id的书</a> 是get请求
 2. 怎么样将 get 请求转成 springmvc 可以识别的 delete 请求, 就要考虑HiddenHttpMethodFilter
    public static final String DEFAULT_METHOD_PARAM = "_method";
    -------------------------------------------------------------------------------------------------------------
    if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
        String paramValue = request.getParameter(this.methodParam);
        if (StringUtils.hasLength(paramValue)) {
            String method = paramValue.toUpperCase(Locale.ENGLISH);
            if (ALLOWED_METHODS.contains(method)) {
                requestToUse = new HttpMethodRequestWrapper(request, method);
            }
        }
    }
    -------------------------------------------------------------------------------------------------------------
    private static final List<String> ALLOWED_METHODS =
			Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
					HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
 3. 从上面代码可以看到 HiddenHttpMethodFilter 过滤器可以对以Post方式提交的delete, put, patch进行转换
    , 转换称springmvc可以识别的 RequestMethod.DELETE / RequestMethod.PUT...
 4. 我们需要将 get <a href="user/book/600">删除指定id的书</a> 以 post方式提交给后端handler, 这样过滤器才会生效
 5. 我们可以通过jquery来处理--引入jquery
--%>
<a href="user/book/600" id="deleteBook">删除指定id的书</a>
<form action="" method="post" id="hiddenForm">
    <input type="hidden" name="_method"/>
</form>
</body>

4.完成修改

java 复制代码
//BookHandler 处理rest风格的请求-增删改查
@RequestMapping(value = "/user")
@Controller
public class BookHandler {
    //修改[PUT]
    @PutMapping(value = "/book/{id}")
    public String updateBook(@PathVariable("id") String id) {
        System.out.println("修改书籍 id=" + id);
        return "redirect:/user/success";
    }
}

前端修改请求代码

html 复制代码
<h3>rest风格的url 修改书籍[put]</h3>
<form action="user/book/666" method="post">
    <input type="button" name="_method" value="PUT"/>
    <input type="submit" value="修改书籍"/>
</form>

🍜HiddenHttpMethodFilter机制

打断点, 进行debug




🍜注意事项和细节

1.HiddenHttpMethodFilter, 在将post转成delete / put请求时, 是按_method参数名 来读取的

2.如果web项目是运行在Tomcat8及以上, 会发现被过滤成DELETEPUT请求, 到达控制器时能顺利执行, 但是返回时(forward)会报HTTP 405的错误提示: JSP 只允许 GET、POST 或 HEAD

  1. 解决方式1: 使用Tomcat7
  2. 解决方式2: 将请求转发(forward)改为请求重定向(redirect): 重定向到一个Handler, 由Handler转发到页面

3.页面测试时, 如果出现点击修改书籍, 仍然走的是删除url, 是因为浏览器原因(缓存等原因).

🍜课后作业

需求说明

小王去商超买衣服, 完成购买衣服的增删改查

1.在web路径下创建restBuyClothes.jsp, 注意引入jquery, 测试查询/添加/删除/修改

html 复制代码
<head>
    <title>购买衣服</title>
    <%--引入jquery--%>
    <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
</head>
<body>
<h3>rest风格的url 挑选衣服[get]</h3>
<a href="?">点击挑选衣服</a>
<br/><hr>
<h3>rest风格的url 添加衣服[post]</h3>
<a href="?">点击添加衣服</a>
<form action="?" method="post">
    clothes: <input name="clothes" type="text"><br/>
    <input type="submit" value="添加衣服">
</form>
<br/><hr>
<h3>rest风格的url 删除一件衣服[delete]</h3>
<a href="?" id="deleteClothes">删除一件衣服</a>
<form action="" method="post" id="deleteForm">
    <input type="hidden" name="_method"/>
</form>
<br/><hr>
<h3>rest风格的url 修改衣服[get]</h3>
<form action="?" method="post">
    <input type="hidden" name="_method">
</form>
</body>

2.在com.zzw.web.rest下, 创建ClothesHandler.java

1.完成查询
@PostMapping(value = "/clothes") @GetMapping(value = "/clothes/{brand}")不重复

java 复制代码
@RequestMapping(value = "/user")//处理Rest风格的请求 增删改查
@Controller
public class ClothesHandler {

    //挑选[GET]
    @GetMapping(value = "/clothes/{brand}")
    public String queryClothes(@PathVariable("brand") String brand) {
        System.out.println("挑选衣服 brand=" + brand);
        return "success";
    }
}

.前端修改请求

html 复制代码
<h3>rest风格的url 挑选衣服[get]</h3>
<a href="user/clothes/阿迪达斯">点击挑选衣服</a>

2.完成添加

java 复制代码
@RequestMapping(value = "/user")
@Controller
public class ClothesHandler {//演示Rest风格的请求

    //挑选[GET]
    @GetMapping(value = "/clothes/{brand}")
    public String queryClothes(@PathVariable("brand") String brand) {
        System.out.println("挑选衣服 brand=" + brand);
        return "success";
    }

    //添加[POST]
    @PostMapping(value = "/clothes")
    public String addClothes(String brand) {
        System.out.println("添加衣服 brand=" + brand);
        return "success";
    }
}

.前端修改请求

html 复制代码
<h3>rest风格的url 添加衣服[post]</h3>
<form action="user/clothes" method="post">
    clothes: <input name="brand" type="text"><br/>
    <input type="submit" value="添加衣服">
</form>

3.完成删除

java 复制代码
@RequestMapping(value = "/user")
@Controller
public class ClothesHandler {//演示Rest风格的请求

    //挑选[GET]
    @GetMapping(value = "/clothes/{brand}")
    public String queryClothes(@PathVariable("brand") String brand) {
        System.out.println("挑选衣服 brand=" + brand);
        return "success";
    }

    //添加[POST]
    @PostMapping(value = "/clothes")
    public String addClothes(String brand) {
        System.out.println("添加衣服 brand=" + brand);
		//return "success";
        return "redirect:/user/success";
    }

    @RequestMapping(value = "/success2")
    public String successGeneral() {
        return "success";
    }
}

.前端修改请求

html 复制代码
<head>
    <title>购买衣服</title>
    <%--引入jquery--%>
    <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
    <script type="text/javascript">
        $(function () {
            // alert("123");
            $("#deleteClothes").click(function () {
                // alert("ok..");
                $("#deleteForm")[0].action = this.href;
                $("input:hidden")[0].value = "DELETE";
                $("#deleteForm").submit();
                return false;
            })
        })
    </script>
</head>
<body>
<h3>rest风格的url 删除一件衣服[delete]</h3>
<a href="user/clothes/361" id="deleteClothes">删除一件衣服</a>
<form action="" method="post" id="deleteForm">
    <input type="hidden" name="_method"/>
</form>
</body>

4.完成修改

java 复制代码
@RequestMapping(value = "/user")
@Controller
public class ClothesHandler {//演示Rest风格的请求

    //挑选[GET]
    @GetMapping(value = "/clothes/{brand}")
    public String queryClothes(@PathVariable("brand") String brand) {
        System.out.println("挑选衣服 brand=" + brand);
        return "success";
    }

    //添加[POST]
    @PostMapping(value = "/clothes")
    public String addClothes(String brand) {
        System.out.println("添加衣服 brand=" + brand);
		//return "success";
        return "redirect:/user/success";
    }

    @RequestMapping(value = "/success2")
    public String successGeneral() {
        return "success";
    }

    //修改[PUT]
    @PutMapping(value = "/clothes/{brand}")
    public String updateClothes(@PathVariable("brand") String brand) {
        System.out.println("修改衣服 brand=" + brand);
        return "redirect:/user/success2";
    }
}

.前端修改请求

html 复制代码
<body>
<h3>rest风格的url 修改衣服[get]</h3>
<form action="user/clothes/李宁" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="修改衣服"/>
</form>
</body>

🍜SpringMVC映射请求数据

🍜RequestParam - 获取参数值

开发中, 如何获取到http://ip:port/url?参数名=参数值&参数名=参数值

1.com.zzw.web.requestParam新建VoteHandler

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {

    /**
     * 解读
     * 1.获取到超链接传递的数据 请求 http://localhost:8080/springmvc/vote/vote01?name=xx
     * 2.@RequestParam 表示会接收提交的参数
     * 3,value="name" 表示提交的参数名是name
     * 4.required=false 表示该参数可以没有; 默认是true, 表示必须有这个参数
     * 5.当我们使用了 @RequestParam(value="name", required=false)后 请求的参数名和方法的形参名可以不一致
     */
    @RequestMapping(value = "/vote01")
    public String test01(@RequestParam(value = "name", required = false) String username) {
        System.out.println("得到username=" + username);
        return "success";
    }
}

2.web路径下新建request_parameter.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试 request parameter</title>
</head>
<body>
<h2>获取到超链接参数值</h2>
<hr>
<a href="vote/vote01?name=zzw">获取超链接的参数</a>
</body>
</html>

3.Postman测试

get方式提交
post方式提交

🍜获取http请求消息头

●说明

1.在开发中, 如何获取到http请求的消息头信息

2.使用较少

案例

1.修改VoteHandler.java, 增加方法 test02

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 需求: 获取http请求头信息, 获取到Accept-Encoding 和 Host
     * [涉及到知识点 http协议]
     * @param a1
     * @param a2
     * @return
     */
    @RequestMapping(value = "/vote02")
    public String test02(@RequestHeader("Accept-Encoding") String a1, @RequestHeader("Host") String a2) {
        System.out.println("Accept-Encoding=" + a1);
        System.out.println("Host=" + a2);
        //返回一个结果
        return "success";
    }
}

修改request_parameter.jsp

html 复制代码
<h2>获取到消息头</h2>
<a href="vote/vote02">获取到Http消息头信息</a>

3.Postman测试

🍜获取JavaBean对象

开发中, 如何获取到javabean对象, 就是以前的entity/pojo对象数据

案例

1.com.zzw.web.requestParam.entity包下新建Pet

java 复制代码
public class Pet {
    private Integer id;
    private String name;

	//setter, getter, toString方法
}

同包下新建Master

java 复制代码
public class Master {
    private Integer id;
    private String name;
    private Pet pet;//对象的属性是另外一个对象[涉及到属性级联]
    
	//setter, getter, toString方法
}

2.修改VoteHandler.java, 增加方法 test03

java 复制代码
public class VoteHandler {
    /**
     * 演示如何获取到提交数据->封装成java对象
     * 说明
     * 1.方法的形参用对应的类型来指定即可, SpringMVC会自动的进行封装
     * 2.如果要自动地完成封装, 要求提交的数据, 参数名和对象的字段名保持一致
     * 3.如果属性是对象, 这里仍然是通过 字段名.字段名 比如Master [pet]
     *   , 即提交的数据 参数名 是 pet.id pet.name, 这就是级联操作
     * 4.如果提交的数据 的参数名和对象的字段名不匹配, 则对象的属性值就是null
     * @return
     */
    @RequestMapping(value = "/vote03")
    public String test03(Master master) {
        System.out.println("master=" + master);
        //返回结果
        return "success";
    }
}

3.修改request_parameter.jsp

html 复制代码
<%--解读
1.这是一个表单, 表单的数据对应Master对象
2.提交的数据参数名和对象的字段名一致即可
--%>
<h1>添加主人信息</h1>
<form action="vote/vote03" method="post">
    主人:<input type="text" name="id"/><br/>
    主人:<input type="text" name="name"/><br/>
    宠物名:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

3.Postman测试

get方式提交
post方式提交

注意事项和细节

1.支持级联数据获取

2.表单的控件名称name需要和javabean对象名称呼应, 否则就是null

🍜使用Servlet API

●说明

1.开发中, 我们可以需要使用到原生的servlet api

2.使用servlet api, 需要引入tomcat/lib下的servlet-api.jar

案例

1.修改VoteHandler.java, 增加方法 test04

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 使用servlet api, 来获取提交的数据
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/vote04")
    public String test04(HttpServletRequest request,
                         HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("username=" + username);
        System.out.println("password=" + password);
        //返回结果
        return "success";
    }
}

2.修改request_parameter.jsp

html 复制代码
<h1>演示 servlet api的使用</h1>
<form action="vote/vote04" method="post">
    用户名:<input type="text" name="username"/><br/>
    密 码:<input type="password" name="password"/><br/>
    <input type="submit" value="提交"/>
</form>

3.Postman测试

post方式提交

注意事项和细节

1.除了HttpServletRequest, HttpServletResponse其它对象还可以以这样的方式获取

2.HttpSession, java.security.Principal, InputStream, OutputStream, Reader, Writer

3.其中一些对象也可以通过 HttpServletRequest / HttpServletResponse对象获取, 比如Session对象, 既可以通过参数传入, 也可以通过request.getSession()获取, 效果一样, 推荐使用参数形式传入, 更加简单明了

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 使用servlet api, 来获取提交的数据
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/vote04")
    public String test04(HttpServletRequest request,
                         HttpServletResponse response,
                         HttpSession session2) {
        //获取到session
        //servlet的原生的方式
        HttpSession session = request.getSession();
        System.out.println("session=" + session);
        //注意: 通过参数传入的 session2 和 通过request.getSession()得到的 session对象是同一个
        System.out.println("session2=" + session2);
        //返回结果
        return "success";
    }
}

🍜模型数据

💞模型数据处理 - 数据放入request

●说明

1.开发中, 控制器/处理器中获取的数据如何放入request域, 然后在前端(VUE/JSP/...)中显示

方式1:

通过HttpServletRequest放入request

方式2:

通过请求的方法参数Map<String, Object> 放入request

方式3:

通过返回 ModelAndView 对象实现request域数据

🍜默认机制放入Request域

1.web路径下新建model_data.jsp

html 复制代码
<head>
    <title>测试 模型数据</title>
</head>
<body>
<h1>添加主人信息</h1>
<form action="?" method="?">
  主人号:<input type="text" name="?"/><br/>
  主人名:<input type="text" name="?"/><br/>
  宠物号:<input type="text" name="?"/><br/>
  宠物名:<input type="text" name="?"/><br/>
  <input type="submit" value="添加主人和动物"/>
</form>
</body>

2.修改VoteHandler.java, 增加方法 test05

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 1.演示将提交的数据->springmvc 封装到java对象->springmvc 会自动的将其放入到request域 [springmvc底层机制]
     * 2.这样我们就可以在跳转到的页面取出数据.
     * @return 参数名和对象的字段名保持一致
     */
    @RequestMapping(value = "/vote05")
    public String test05(Master master) {

        //解读
        //1.springmvc会自动地把获取的model模型, 放入到request域中, 名字就是master
        //  在request域中: ("master", master对象)
        //2.也可以手动将master放入到request...

        //返回一个结果
        return "vote_ok";
    }
}

3.web路径/WEB-INF/pages目录 新建vote_ok.jsp el表达式

html 复制代码
<body>
<h1>获取到的数据显示页面</h1>
<hr>
取出 request域的数据-通过el表达式获取即可
<br/>
address: <br/>
主人名字= ${requestScope.master.name}
主人id= ${requestScope.master.id}
宠物名字= ${requestScope.master.pet.name}
</body>

4.完善model_data.jsp

html 复制代码
<h1>添加主人信息</h1>
<form action="vote/vote05" method="post">
    主人号:<input type="text" name="id"/><br/>
    主人名:<input type="text" name="name"/><br/>
    宠物号:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

🍜通过HttpServletRequest放入request域

1.修改VoteHandler.java, 增加代码

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
        /**
     * 1.演示将提交的数据->springmvc 封装到java对象->springmvc 会自动的将其放入到request域 [springmvc底层机制]
     * 2.这样我们就可以在跳转到的页面取出数据.
     * @return 参数名和对象的字段名保持一致
     */
    @RequestMapping(value = "/vote05")
    public String test05(Master master, HttpServletRequest request) {

        //解读
        //1.springmvc会自动地把获取的model模型, 放入到request域中, 名字就是master
        //  在request域中: ("master", master对象)

        //2.也可以手动将master放入到request
        request.setAttribute("address", "济南");
        //3.如果我们希望修改master的属性值
        master.setName("coco");
        //4.分析一下springmvc默认存放对象到request域中, 属性名是如何确定的
        //  request域 ("master", master) 属性名是类名/类型名 首字母小写

        //返回一个结果
        return "vote_ok";
    }
}

2.vote_ok.jsp

html 复制代码
<body>
<h1>获取到的数据显示页面</h1>
<hr>
取出 request域的数据-通过el表达式获取即可
<br/>
address: <br/>
主人名字= ${requestScope.master.name}
主人id= ${requestScope.master.id}
宠物名字= ${requestScope.master.pet.name}
</body>

测试

🍜通过方法参数Map<String, Object> 放入request域

1.修改model_data.jsp, 增加代码

html 复制代码
<h1>添加主人信息[测试 Map ]</h1>
<form action="vote/vote06" method="post">
    主人号:<input type="text" name="id"/><br/>
    主人名:<input type="text" name="name"/><br/>
    宠物号:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

2.修改VoteHandler.java, 增加方法test06

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 演示通过Map<String, Object> 设置数据到request域
     * @return
     */
    @RequestMapping(value = "/vote06")
    public String test06(Master master, Map<String, Object> map) {
        //解读
        //1.需求是通过map对象, 添加属性到request中
        //2.原理分析: springmvc会遍历map, 然后将map的k-v存放到request域中
        map.put("address", "beijing...");
        map.put("master", null);
        //返回一个结果
        return "vote_ok";
    }
}

3.Postman测试

post方式提交

🍜ModelAndView使用

1.修改model_data.jsp, 增加代码

html 复制代码
<h1>添加主人信息[测试ModelAndView]</h1>
<form action="?" method="?">
    主人号:<input type="text" name="id"/><br/>
    主人名:<input type="text" name="name"/><br/>
    宠物号:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

2.修改VoteHandler.java, 增加方法test07

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 演示通过返回ModelAndView对象, 将数据放入request中
     * @return
     */
    @RequestMapping(value = "/vote07")
    public ModelAndView test07(Master master) {

        ModelAndView modelAndView = new ModelAndView();
        //放入属性到modelAndView对象
        modelAndView.addObject("address", "shanghai~");
        modelAndView.addObject("master", null);
        //可以把从数据库得到的数据->对象->放入到modelAndView[service-dao-db]
        //这里指定要跳转的视图名称
        modelAndView.setViewName("vote_ok");
        //返回结果
        return modelAndView;
    }
}

3.完善model_data.jsp

html 复制代码
<h1>添加主人信息[测试ModelAndView]</h1>
<form action="vote/vote07" method="post">
    主人号:<input type="text" name="id"/><br/>
    主人名:<input type="text" name="name"/><br/>
    宠物号:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

4.Postman测试

post方式提交


注意事项和细节

1.从本质看, 请求响应的方法return xx时返回了一个字符串, 其实本质是返回了一个ModeAndView对象, 只是默认被封装起来的.

2.ModelAndView即可以包含model数据, 也可以包含视图信息

3.ModelAndView对象的addObject方法可以添加key-val数据.

4.ModelAndView对象setView方法可以指定视图名称

💞模型数据处理 - 数据放入session

说明

开发中, 控制器 / 处理器中获取的数据如何放入session域, 然后在前端(VUE/JSP/...)取出显示.

案例

1.修改model_data.jsp, 增加代码

html 复制代码
<h1>添加主人信息[测试session]</h1>
<form action="?" method="?">
    主人号:<input type="text" name="id"/><br/>
    主人名:<input type="text" name="name"/><br/>
    宠物号:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

2.修改VoteHandler.java, 增加方法test08

java 复制代码
@RequestMapping("/vote")
@Controller
public class VoteHandler {
    /**
     * 演示如何将数据设置到session域中
     * @return
     */
    @RequestMapping(value = "/vote08")
    public String test08(Master master, HttpSession httpSession) {
        //master对象是默认放在request域中

        //我们将master对象放入到session
        httpSession.setAttribute("master", master);
        httpSession.setAttribute("address", "beijing``");
        return "vote_ok";
    }
}

3.vote_ok.jsp增加代码

html 复制代码
<head>
    <title>vote_ok</title>
</head>
<body>
取出 session域的数据<br/>
address: ${sessionScope.address}<br/>
主人名字= ${sessionScope.master.name}<br/>
主人信息= ${sessionScope.master}<br/>
</body>

4.完善model_data.jsp

html 复制代码
<h1>添加主人信息[测试session]</h1>
<form action="vote/vote08" method="post">
    主人号:<input type="text" name="id"/><br/>
    主人名:<input type="text" name="name"/><br/>
    宠物号:<input type="text" name="pet.id"/><br/>
    宠物名:<input type="text" name="pet.name"/><br/>
    <input type="submit" value="添加主人和动物"/>
</form>

4.Postman测试

post方式提交

💞模型数据 - @ModelAttribute

●基本说明

开发中, 有时需要使用某个前置方法(比如prepareXxx(), 方法名由程序员定)给目标方法准备一个模型对象

1.@ModelAttribute注解可以实现 这样的需求

2.在某个方法上, 增加了@ModelAttribute注解后

3.那么在调用该Handler的任何一个方法时, 都会先调用这个方法

案例

1.修改VoteHandler.java, 增加方法prepareModel

java 复制代码
    /**
     * 解读
     * 1.当Handler的方法被标识 @ModelAttribute, 就视为一个前置方法
     * 2.当调用Handler的其它方法时, 都会先执行该前置方法
     * 3.类似我们前面学习Spring时, AOP的前置通知
     * 4.prepareModel 前置方法, 会切入到其它方法前执行. 底层是AOP机制
     */
    @ModelAttribute
    public void prepareModel() {
        System.out.println("prepareModel.........完成准备工作.........");
    }

@ModelAttribute最佳实践

修改用户信息(就是经典的使用这种机制的应用), 流程如下

1.在修改前, 在前置方法中从数据库查出这个用户

2.在修改方法(目标方法)中, 可以使用前置方法从数据库查询这个对象

3.如果表单中对用户的某个属性修改了, 则以新的数据为准, 如果没有修改, 则以数据库的信息为准. 比如, 用户的某个属性不能修改, 就保持原来的值.

🍜视图和视图解析器

基本介绍

1.在SpirngMVC中的目标方法最终返回都是一个视图(有各种视图)

2.返回的视图都会由一个视图解析器来处理(视图解析器有很多种)

🍜 自定义视图

1.在默认情况下, 我们都是返回默认的视图, 然后返回的视图交由SpringMVCInternalResourceViewResolver视图解析器来处理的.

xml 复制代码
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>

2.在实际开发中, 我们有时需要自定义视图, 这样可以满足更多更复杂的需求.

实例-代码实现

1.在web路径目录下 新建view.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>自定义视图测试</title>
</head>
<body>
<h2>自定义视图测试</h2>
<a href="?">点击到自定义视图</a>
</body>
</html>

2.com.zzw.web.viewresolver包下 新建GoodsHandler

java 复制代码
@RequestMapping("/goods")
@Controller
public class GoodsHandler {

    @RequestMapping("/buy")
    public String buy() {
        System.out.println("buy() 被调用...");
        return "?";//待会再填写自定义视图名称
    }
}

3.com.zzw.web.viewresolver包下 新建MyView

java 复制代码
/**
 * 解读
 * 1.MyView 继承了AbstractView, 就可以作为一个视图使用
 * 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
 */
@Component(value = "zzwView")
public class MyView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //完成视图渲染
        //并且可以确定我们要跳转的页面
        System.out.println("进入到自己的视图");
    }
}

4.在web路径/WEB-INF/pages目录下 新建my_view.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>my_view页面</title>
</head>
<body>
<h2>进入到my_view页面</h2>
<p>是从自定义视图来的</p>
</body>
</html>

5.补充MyView的代码

java 复制代码
/**
 * 解读
 * 1.MyView 继承了AbstractView, 就可以作为一个视图使用
 * 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
 */
@Component(value = "zzwView")
public class MyView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        //完成视图渲染
        //并且可以确定我们要跳转的页面[请求转发] /WEB-INF/pages/my_view.jsp
        System.out.println("进入到自己的视图");
        //解读
        //1.下面就是进行请求转发到 /WEB-INF/pages/my_view.jsp
        //2./WEB-INF/pages/my_view.jsp 会被springmvc解析
        //  /springmvc/WEB-INF/pages/my_view.jsp
        request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
                .forward(request, response);
    }
}

6.配置springDispatcherServlet-servlet.xml, 增加自定义视图解析器

xml 复制代码
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>
<!--

解读
1.配置自定义视图解析器BeanNameViewResolver
2.BeanNameViewResolver可以去解析我们自定义的视图
3.配置 属性 order, 表示视图解析器执行的顺序, 值越小, 优先级越高
4.属性 order 的默认值是最低优先级, 值为 Integer.MAX_VALUE
  int LOWEST_PRECEDENCE = 2,147,483,647;
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <property name="order" value="99"/>
</bean>

7.view.jsp修改请求路径

html 复制代码
<h2>自定义视图测试</h2>
<a href="goods/buy">点击到自定义视图</a>

8.GoodsHandler.java完善代码

java 复制代码
@RequestMapping("/goods")
@Controller
public class GoodsHandler {

    @RequestMapping("/buy")
    public String buy() {
        System.out.println("buy() 被调用...");
        return "zzwView";//待会再填写自定义视图名称
    }
}

9.Postman测试

🍜 自定义视图-步骤梳理

1.创建一个Viewbean, 该bean需要继承自AbstractView, 并实现renderMergedOutputModel方法

2.并把自定义View加入到IOC容器中

3.自定义视图的视图处理器, 使用BeanNameViewResolver, 这个视图处理器也需要配置到ioc容器

4.BeanNameViewResolver的调用优先级需要设置一下, 设置orderInteger.MAX_VALUE小的值, 以确保其在InternalResourceViewResolver之前被调用

🍜 自定义视图解析器执行流程

1.SpringMVC调用目标方法, 返回自定义ViewIOC容器中的id

2.SpringMVC调用BeanNameViewResolver解析视图: 从IOC容器中获取 返回id值对应的bean, 即自定义View的对象

3,SpringMVC调用自定义视图的renderMergedOutputModel方法渲染视图

4.说明:如果在SpringMVC调用目标方法, 返回自定义View容器中的id, 不存在, 则仍然按照默认的视图处理器机制处理.

debug源码



判断是否实现了View接口, 如果实现了, 返回View对象



进入到my_view.jsp

🍜 默认视图解析器执行流程

1.配置默认视图解析器的优先级

xml 复制代码
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
    <!--调整优先级-->
    <property name="order" value="10"/>
</bean>

2.debug源码

进入到默认视图解析器

由于没有zzwView.jsp页面, 页面会报错

🍜 多个视图解析器执行流程

案例1: 假设自定义视图解析器的优先级大于默认视图解析器

1.将默认视图解析器的优先级大小设置为默认

xml 复制代码
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
    <!--调整优先级-->
    <!--<property name="order" value="10"/>-->
</bean>

2.将自定义视图MyView.java的beanName改为zzwViewx

java 复制代码
/**
 * 解读
 * 1.MyView 继承了AbstractView, 就可以作为一个视图使用
 * 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
 */
@Component(value = "zzwViewx")
public class MyView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        //完成视图渲染
        //并且可以确定我们要跳转的页面[请求转发] /WEB-INF/pages/my_view.jsp
        System.out.println("进入到自己的视图");
        //解读
        //1.下面就是进行请求转发到 /WEB-INF/pages/my_view.jsp
        //2./WEB-INF/pages/my_view.jsp 会被springmvc解析
        //  /springmvc/WEB-INF/pages/my_view.jsp
        request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
                .forward(request, response);
    }
}

3.debug源码


在容器中找不到id为zzwView的bean对象. 参数viewNamezzwView,
BeanName视图解析器返回了空, 开始循环第二个默认视图解析器
对返回的view对象进行估算. 没有 /WEB-INF/pages/zzwView.jsp 这个文件, 会报404错误.

案例2: 假设默认视图解析器的优先级大于自定义视图解析器

1.将默认视图解析器的优先级大小设置为10, 此时自定义视图解析器的优先级是99.

xml 复制代码
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
    <!--调整优先级-->
    <property name="order" value="10"/>
</bean>

2.将自定义视图MyView.java的beanName改为zzwView

java 复制代码
/**
 * 解读
 * 1.MyView 继承了AbstractView, 就可以作为一个视图使用
 * 2.@Component(value="myView"), 该视图会注入到容器中, 名字/id 是 zzwView
 */
@Component(value = "zzwView")
public class MyView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        //完成视图渲染
        //并且可以确定我们要跳转的页面[请求转发] /WEB-INF/pages/my_view.jsp
        System.out.println("进入到自己的视图");
        //解读
        //1.下面就是进行请求转发到 /WEB-INF/pages/my_view.jsp
        //2./WEB-INF/pages/my_view.jsp 会被springmvc解析
        //  /springmvc/WEB-INF/pages/my_view.jsp
        request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
                .forward(request, response);
    }
}

3.debug源码

只要执行了默认视图解析器, 找不到页面, 会报404, 不会再执行下一个视图解析器.

🍜 指定请求转发或者重定向

● 目标方法中指定转发或者重定向

1.默认返回的方法是请求转发, 然后用视图处理器进行处理, 比如在目标方法中这样写:

2.也可以在目标方法中直接指定重定向或请求转发的url地址

3.如果指定重定向, 可以定向到web路径下的资源

但是有个例外: 不能重定向到/WEB-INF目录.

原因: WEB-INF下的资源是服务器内部访问的, 浏览器不能访问

案例

1.修改com.zzw.web.viewresolver.GoodsHandler.java, 增加方法order

java 复制代码
@RequestMapping("/goods")
@Controller
public class GoodsHandler {

    @RequestMapping("/order")
    public String order() {
        System.out.println("===========order()===========");
        //请求转发到 /WEB-INF/pages/my_view.jsp
        //下面的这个路径 /WEB-INF/pages/my_view.jsp 会被解析成 /springmvc/WEB-INF/pages/my_view.jsp
        //return "forward:/WEB-INF/pages/my_view.jsp";
        //return "forward:/aa/bb/ok.jsp";

        //直接指定要重定向的页面
        //1. 对应重定向来说, 不能重定向到 /WEB-INF/ 目录下
        //2. redirect 关键字, 表示进行重定向
        //3. /login.jsp 在服务器解析成 /springmvc/login.jsp
        return "redirect:/login.jsp";

        //http://localhost:8080/springmvc/WEB-INF/pages/my_view.jsp
        //浏览器不能访问WEB-INF下的资源
        //return "redirect:/WEB-INF/pages/my_view.jsp";
    }
}

2.view.jsp增加代码

html 复制代码
<body>
<h2>自定义视图测试</h2>
<a href="goods/buy">点击到自定义视图</a><br/>
<a href="goods/order">测试在目标方法中指定请求转发或者重定向到页面</a>
</body>

测试1

1.请求转发到 /WEB-INF/pages/my_view.jsp

2.下面的这个路径 /WEB-INF/pages/my_view.jsp 会被解析成 /springmvc/WEB-INF/pages/my_view.jsp

java 复制代码
return "forward:/WEB-INF/pages/my_view.jsp";

测试2

1.请求转发到 /aa/bb/ok.jsp

2.下面的这个路径 /aa/bb/ok.jsp 会被解析成 /springmvc/aa/bb/ok.jsp

java 复制代码
return "forward:/aa/bb/ok.jsp";

测试3

直接指定要重定向的页面

1.对应重定向来说, 不能重定向到 /WEB-INF/ 目录下

2.redirect 关键字, 表示进行重定向

3./login.jsp 在服务器解析成 /springmvc/login.jsp

java 复制代码
return "redirect:/login.jsp";


测试4

1.重定向到 /WEB-INF/pages/my_view.jsp

2.下面的这个路径 /WEB-INF/pages/my_view.jsp 在服务器会被解析成 /springmvc/WEB-INF/pages/my_view.jsp

java 复制代码
//http://localhost:8080/springmvc/WEB-INF/pages/my_view.jsp
return "redirect:/WEB-INF/pages/my_view.jsp";

WEB-INF下的资源是服务器内部访问的, 浏览器不能访问

💞指定请求转发流程分析

打断点

debug源码


调用默认的视图解析器

已经作为一个bean对象存储在容器中

开始渲染

renderMergedOutputModel()MyView.java中的相仿

rd = request.getRequestDispatcher(dispatcherPath);

💞指定重定向流程分析

打断点

debug源码



SpringMVC有很多视图种类

调用RedirectView的视图渲染方法

💞作业布置

1.熟悉前面的SpringMVC映射数据请求, 模型数据, 视图和视图解析的案例

2.清晰Debug源码的流程

3.完成一个简单的用户登录案例

1)编写登陆页面login.jsp

2)LoginHandler [doLogin方法], 如果用户输入用户名是zzw, 密码123, 就可以登陆成功; 否则登陆失败

3)创建JavaBean: User.java

4)表单提交数据到doLogin方法, 以User对象形式接收

5)登陆成功到, 页面 login_ok.jsp, 并显示登陆欢迎信息 [显示用户名, 模型数据会自动填充到request域中]

6)登陆失败到, 页面 login_err.jsp, 并给出重新登录的超链接 [考察web工程路径应用问题]

代码实现

1.在web路径/homework下 新建login.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆页面</title>
</head>
<body>
<form action="?">
    用户名: <input name="username" type="text"/><br/>
    密 码: <input name="password" type="password"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

2.在com.zzw.web.homework下新建LoginHandler

java 复制代码
@Controller
@RequestMapping(value = "/user")
public class LoginHandler {

	//处理登录
    @RequestMapping(value = "/doLogin", params ={"username","password"}, method = RequestMethod.GET)
    public String doLogin(String username, String password) {
   		 System.out.println(username);//测试
   		 System.out.println(password);//测试
   		 
        if ("zzw".equals(username) && "123".equals(password)) {
            System.out.println("登录成功");
        } else {
            System.out.println("登陆失败");
        }
        return "";
    }
}

3.填充login.jsp的请求路径 涉及-web工程路径问题

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆页面</title>
</head>
<body>
<form action="../user/doLogin">
    用户名: <input name="username" type="text"/><br/>
    密 码: <input name="password" type="password"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

也可以这么写

html 复制代码
<%--
    <%=request.getContextPath()%>/user/doLogin => /springmvc/user/doLogin
    /springmvc/user/doLogin 被浏览器解析成 http://localhost:8080/springmvc/user/doLogin
--%>
<form action="<%=request.getContextPath()%>/user/doLogin">

4.在com.zzw.web.homework.entity下新建User.java

java 复制代码
public class User {
    private String username;
    private String password;

	//setter, getter, toString方法
}

5.表单登录提交到doLogin方法, 以User对象形式接收 封装成javaBean. 修改UserHandler

java 复制代码
@Controller
@RequestMapping(value = "/user")
public class LoginHandler {

    @RequestMapping(value = "/doLogin", params ={"username","password"}, method = RequestMethod.GET)
    public String doLogin(User user) {
        System.out.println(user.getUsername());//测试
        System.out.println(user.getPassword());//测试

        if ("zzw".equals(user.getUsername()) && "123".equals(user.getPassword())) {
            System.out.println("登录成功");
        } else {
            System.out.println("登陆失败");
        }
        return "";
    }
}

6.登录成功到login_ok.jsp, 并显示登陆欢迎信息 模型数据默认放入request域. 在web路径/WEB-INF/pages/homework下 新建login_ok.jsp

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆成功</title>
</head>
<body>
<h1>欢迎 ${requestScope.user.username} 登陆成功</h1>
</body>
</html>

7.登陆失败到login_err.jsp, 并给出重新登陆的超链接. 在web路径/WEB-INF/pages/homework下 新建login_err.jsp

java 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆失败</title>
</head>
<body>
<h1>登陆失败</h1>
<a href="?">重新登陆</a>
</body>
</html>

8.在springDispatcherServlet-servlet.xml中增加一个优先级为9解析器

xml 复制代码
<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver02">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/pages/homework/"/>
    <property name="suffix" value=".jsp"/>
    <!--调整优先级-->
    <property name="order" value="9"/>
</bean>

9.修改UserHandler 注释部分参考服务器对路径的解析

java 复制代码
@Controller
@RequestMapping(value = "/user")
public class LoginHandler {

    @RequestMapping(value = "/doLogin", params ={"username","password"}, method = RequestMethod.GET)
    public String doLogin(User user) {
        if ("zzw".equals(user.getUsername()) && "123".equals(user.getPassword())) {
        	//也可以这么写
        	//return "forward:/WEB-INF/pages/homework/login_ok.jsp";
            return "login_ok";
        } else {
      	    //也可以这么写
        	//return "forward:/WEB-INF/pages/homework/login_err.jsp";
            return "login_err";
        }
    }
}

10.测试

11.完成重新登录的超链接

我们可以查看到当前浏览器地址栏的路径, 根据 相对路径的知识点, 我们完善login_err.jsp重新登陆的超链接

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆失败</title>
</head>
<body>
<h1>登陆失败</h1>
<a href="../homework/login.jsp">重新登陆</a>
</body>
</html>

也可以这么写

html 复制代码
<%--
    <%=request.getContextPath()%>/homework/login.jsp
    即 /springmvc/homework/login.jsp
    会被浏览器解析成 http://localhost:8080/springmvc/homework/login.jsp
--%>
<a href="<%=request.getContextPath()%>/homework/login.jsp">重新登陆</a>

下一篇: 手动实现SpringMVC底层机制, 敬请期待...

相关推荐
暮乘白帝过重山2 分钟前
Singleton和Prototype的作用域与饿汉式/懒汉式的初始化方式
spring·原型模式·prototype·饿汉式·singleton·懒汉式
腥臭腐朽的日子熠熠生辉31 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian33 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之38 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息1 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存