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/hello3?email=978964140@qq.com, 一定要注意提交参数名和后台方法的形参名保持一致, 否则后端接收不到参数


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=xx@qq.com

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=xx@qq.com

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底层机制, 敬请期待...

相关推荐
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿3 小时前
List深拷贝后,数据还是被串改
java
xlsw_7 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹7 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭8 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫8 小时前
泛型(2)
java
超爱吃士力架8 小时前
邀请逻辑
java·linux·后端
南宫生8 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石8 小时前
12/21java基础
java
李小白668 小时前
Spring MVC(上)
java·spring·mvc