SpringMVC(基于 Spring 的 Web 层 MVC 框架)

这里写自定义目录标题

1、 SpringMVC 介绍

1.2 SpringMVC-基本介绍

1.2.1 SpringMVC 特点&概述

  1. SpringMVC 从易用性,效率上 比曾经流行的 Struts2 更好
  2. SpringMVC 是 WEB 层框架【解读: SpringMVC 接管了 Web 层组件, 比如控制器, 视图, 视图解析, 返回给用户的数据格式, 同时支持 MVC 的开发模式/开发架构】
  3. SpringMVC 通过注解,让 POJO 成为控制器,不需要继承类或者实现接口
  4. SpringMVC 采用低耦合的组件设计方式,具有更好扩展和灵活性
  5. 支持 REST 格式的 URL 请求.
  6. SpringMVC 是基于 Spring 的, 也就是 SpringMVC 是在 Spring 基础上的。SpringMVC 的核心包 spring-webmvc-xx.jar 和 spring-web-xx.jar

1.2.2 梳理 Spring SpringMVC SpringBoot 的关系

  1. Spring MVC 只 是 Spring 处 理 WEB 层 请 求 的 一个 模 块 /组 件 , Spring MVC 的 基 石 是
    Servlet[Java WEB]
  2. Spring Boot 是为了简化开发者的使用, 推出的封神框架(约定优于配置,简化了 Spring的配置流程), SpringBoot 包含很多组件/框架,Spring 就是最核心的内容之一,也包含 SpringMVC
  3. 他们的关系大概是: Spring Boot > Spring > Spring MVC

1.3 SpringMVC-快速入门

1.3.1 需求说明
需求说明: 完成一个最基本的测试案例,登录案例, 使用 SpringMVC 完成

1.3.2 SpringMVC 登录流程分析
在这里插入图片描述
1.3.3 SpringMVC 登录-代码实现

  1. 创建 springmvc web 工程并配置 tomcat

  2. 导入 SpringMVC 开发需要的 jar 包

  3. 创建 src/applicationContext-mvc.xml 文件(就是 spring 的容器文件),文件名自己定
    在这里插入图片描述
    在这里插入图片描述

  4. 配置 WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--
    配置前端控制器/中央控制器/分发控制器
    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  -->
    </servlet>
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <!--
        1.这里我们配置的url-pattern是 /,表示用户的请求都经过DispatcherServ
        2.这样配置也这次rest风格的url请求
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

5.在 web下面创建login.jsp

<%--
  Created by IntelliJ IDEA.
  User: zxy
  Date: 2023/7/26
  Time: 19:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<form action="login">
    u:<input name="username" type="text"> <br/>
    p:<input name="password" type="password"><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

  1. 创建 D:\lxc\javaProject\springMVC\src\com\codeSE\web\UserServlet.java
package com.codeSE.web;

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

/**
 * 1.如果我们使用了SpringMVC,在一个类上标识@Controller
 * 2.表示将该类视为一个控制器,注入到容器
 * 3.比原生servlet开发要简化很多
 * 4.这里配置了一个 @Controller就已经被 springMvc框架接管
 */
@Controller
public class UserServlet {
    /**
     * 1. login()方法是用于响应用户的登录请求
     * 2. @RequestMapping(value = "/login")类似我们以前在原生的Servlet配置url-pattern
     * 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/page/"/>
     *         <property name="suffix" value=".jsp"/>
     *
     *     </bean>
     *     根据上面的醚置return "login_ok" ;就是转发到/WEB-INF/pages/login_ok.jspl
     *
     * @return login_ok  页面
     */
    @RequestMapping(value = "/login")
    public String login(){
        System.out.println("login OK...");
        return "login_ok";
    }
}

  1. 创建D:\lxc\javaProject\springMVC\web\WEB-INF\pages\login_ok.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login_ok</title>
</head>
<body>
<h1>登录成功</h1>
</body>
</html>
  1. 配置 applicationContext-mvc.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.codeSE.web"/>
    <!--    视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--    配置属性 suffix和prefix -->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

1.3.4 注意事项和细节说明

  1. 重点学习如何搭建一个 springmvc 项目,初步理解 springmvc 工作流程
  2. 这里的 UserServlet 需要注解成@Controller ,我们称为一个 Handler 处理器
  3. UserServlet 指定 url 时,还可以这样
    在这里插入图片描述
  4. 关 于 SpringMVC 的 DispatcherServlet 的 配 置 文 件 , 如 果 不 在 web.xml 指 定applicationContext-mvc.xml, 默 认 在 /WEB-INF/springDispatcherServlet-servlet.xml 找 这个配置文件【简单看下 DispatcherServlet 的源码】。(推荐使用, 我们做下修改 , 并完成测
    试)
    在这里插入图片描述

2、 SpingMVC 执行流程

在这里插入图片描述

3、@RequestMapping

3.1 基本使用

● @RequestMapping 注解可以指定控制器/处理器的某个方法的请求的 url, RequestMapping : 请求映射。
在这里插入图片描述

3.2 @RequestMapping 注解其它使用方式

3.2.1 @RequestMapping 可以修饰方法和类

  1. 说明: @RequestMapping 注解可以修饰方法,还可以修饰类 当同时修饰类和方法时,请求的 url 就是组合 /类请求值/方法请求值。
    1)创建D:\lxc\javaProject\springMVC\src\com\codeSE\web\UserHandler.java
package com.codeSE.web;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@RequestMapping(value = "/user")
@Controller  //UserHandler就是一个处理器/控制器,注入到容臧
public class UserHandler {
    /** 解读:
     * 1. method=RequestMethod.POST:表示请求 buy目标方法必须是 post
     * 2. RequestMethod,四个选项 POST, GET, PUT, DELETE
     *  3. SpringMVC控制器默认支持 GET和 POST两种方式
     *  4.buy()方法请求的url: http: //localhost:8080/工程路径/user/buy
     * @return String
     */
    @RequestMapping(value = "/buy",method = RequestMethod.POST)
    public String buy(){
        System.out.println("购买商品");
        return "success";
    }
}

  1. 创建D:\lxc\javaProject\springMVC\web\request.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>购买商品</title>
</head>
<body>
<h1>购买商品</h1>
<form action="user/buy" method="post">
    购买人:<input type="text" name="username"><br>
    购买量:<input type="text" name="nums"><br>
    <input type="submit" value="购买">
</form>
</body>
</html>

  1. 创建D:\lxc\javaProject\springMVC\web\WEB-INF\pages\success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>操作成功</title>
</head>
<body>
<h1>恭喜, 操作成功~</h1>
</body>
</html>
  1. 完成测试(页面方式)

3.2.2 @RequestMapping 可以指定请求方式

  1. 说明: @RequestMapping 还可以指定请求的方式(post/get/put/delete…), 请求的方式需要和指定的一样,否则报错
  2. SpringMVC 控制器默认支持 GET 和 POST 两种方式, 也就是你不指定 method , 可以接收GET 和 POST 请求
    在这里插入图片描述
    3.当明确指定了 method , 则需要按指定方式请求, 否则会报错
    在这里插入图片描述

3.2.3 @RequestMapping 可指定 params 和 headers 支持简单表达式

  1. param1: 表示请求必须包含名为 param1 的请求参数
  2. !=param1: 表示请求不能包含名为 param1 的请求参数
  3. param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
  4. {“param1=value1”, “param2”}: 请求必须包含名为 param1 和 param2 的两个请求参数,且 param1 参数的值必须为 value1
  5. 应用实例
  1. 修改 UserHandler.java , 增加方法
    在这里插入图片描述
  2. 修改 request.jsp , 增加代码
    在这里插入图片描述
  3. 页面方式完成测试
  1. 小的细节说明和测试
  1. 需要有 bookId 参数,并且值为 100, 否则报错
@RequestMapping(value = "/find", params = "bookId=100", method = RequestMethod.GET)
  1. 需要有 bookId 参数,并且值不为 100, 否则报错
@RequestMapping(value = "/find", params = "bookId!=100", method = RequestMethod.GET)

3.2.4 @RequestMapping 支持 Ant 风格资源地址

  1. ?:匹配文件名中的一个字符
  2. *:匹配文件名中的任意字符
  3. **:匹配多层路径
  4. Ant 风格的 url 地址举例
    /user/*/createUser: 匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL
    /user/**/createUser: 匹配 /user/createUser、/user/aaa/bbb/createUser 等 URL
    /user/createUser??: 匹配 /user/createUseraa、/user/createUserbb 等 URL
  5. 应用实例
  1. 修改 UserHandler.java, 增加方法
 /**
     * 要求可以配置 /user/message/aaa, /user/message/aaa/bbb/ccc
     * @return
     */
    @RequestMapping(value = "/message/**")
    public String im() {
        System.out.println("发送消息");
        return "success";
    }
  1. request.jsp, 增加代码
<hr>
<h1>演示 Ant 风格的请求资源方式 </h1>
<a href="user/message/aaa">发送消息 1</a><br>
<a href="user/message/aaa/bbb/ccc">发送消息 2</a><br>
  1. 页面完成测试

3.2.5 @RequestMapping 可配合 @PathVariable 映射 URL 绑定的占位符

  1. @RequestMapping 还可以配合 @PathVariable 映射 URL 绑定的占位符。
  2. 这样就不需要在 url 地址上带参数名了,更加的简洁明了
    比如: 我们的前端页面是 这样的, kristina 和 300 是参数值
<hr>
<h1>占位符的演示</h1>
<a href="user/reg/kristina/300">占位符的演示</a>
  1. 应用实例
  1. 修改 UserHandler.java, 增加方法, 注意 @PathVariable(“username”) 不能少
 /**
     * 我 们 希 望 目 标 方 法 获 取 到 username和 userid, value="/xx/{username}" ,---@PathVariable("username")
     * 前端页面: <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") String id){
        System.out.println("接收到参数--" + "username=" + name + " ,--" + "usreid=" + id);
        return "success";
    }
  1. 修改 request.jsp, 增加代码
<hr>
<h1>占位符的演示</h1>
<a href="user/reg/kristina/300">占位符的演示</a>
  1. 页面完成测试

3.2.6 注意事项和使用细节

  1. 映射的 URL, 不能重复
@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

  1. 各种请求的简写形式
  1. 说明
    @RequestMapping(value = “/buy”,method = RequestMethod.POST) 等 价@PostMapping(value = “/buy”)
    简写方式一览: @GetMapping@PostMapping@PutMapping@DeleteMapping
  2. 应用实例
//@RequestMapping(value = "/buy",method = RequestMethod.POST)
@PostMapping(value = "/buy")
public String buy() {
	System.out.println("postmapping.. 购买商品");
	return "success";
}
  1. 如果我们确定表单或者超链接会提交某个字段数据比如(email), 要求提交的参数名和目标方法的参数名保持一致.
    1)应用实例,增加方法
/**
     * hello3(String email):如果我们的请求参数有 email=xx,就会将传递的值,赋给
     * String email,要求名称保持一致,如果不一致,那么接收不到数据
     * @param email
     * @return
     */
    @GetMapping(value = "/hello")
    public String hello3(String email) {
        System.out.println("hello" + email);
        return "success";
    }
  1. 测试 输入 localhost:9998/user/hello3?email=1234567890@qq.com, 一定要注入提交参数名和后台方法的形参名保持一致, 否则后端接收不到参数,为null。

3.3 Postman(接口测试工具)

安装使用说明

4、 Rest-优雅的 url 请求风格

4.1 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 表单只支持 GET 与 POST 请求,而 DELETE、PUT等 method 并不支持,Spring 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。
  3. HiddenHttpMethodFilter 能对 post 请求方式进行转换,因此我们需要特别的注意这一点。
  4. 这个过滤器需要在 web.xml 中配置。

4.2 Rest 风格的 url-完成增删改查

4.2.1 需求说明
获取指定id的书
添加一本书bookname:
删除指定id的书
修改一本书

4.2.2 Rest 应用案例-代码实现

  1. 修改 web.xml 添加 HiddenHttpMethodFilter
    <!--
        1.配置 HiddenMethodFilter过滤器
        2.可以将以 post方式提交的 delete和 put请求进行转换
    -->
    <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>
  1. 修改 springDispatcherServlet-servlet.xml
<!--加入两个常规配置 -->
<!-- 能支持 SpringMVC高级功能,比如 JSR303校验,映射动态请求 -->
 <mvc:annotation-driven></mvc:annotation-driven>
 <!--将 SpringMVC不能处理的请求交给 Tomcat,比如请求 css,js等-->
 <mvc:default-servlet-handler/>
  1. 创建D:\lxc\javaProject\springMVC\web\rest.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>rest</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#deleteBook").click(function () {
                // alert("ok");
                $("#hiddenForm").attr("action", this.href);
                $(":hidden").val("DELETE");
                $("#hiddenForm").submit();//这里就是提交删除请求了
                //这里必须返回 false,否则会提交两次
                return false;
            });
        })
    </script>
</head>
<body>
<h3>Rest 风格的 crud 操作案例</h3>
<br>
<hr>
<h3>rest 风格的 url 查询书籍[get]</h3>
<a href="user/book/200">点击查询书籍</a>
<br>
<hr>
<h3>rest 风格的 url 添加书籍[post]</h3>
<form action="user/addBook" method="post">
    name:<input name="bookName" type="text"><br>
    <input type="submit" value="添加书籍">
</form>
<br>

<!--
    1.这里我们需要将删除方式(get)转成 delete的方式,需要使用过滤器和 jquery来完成
    2. name="_method"名字需要写成 _method,因为后台的 HiddenHttpMethodFilter
    就是按这个名字来获取 hidden域的值,从而进行请求转换的.
    3. public static final String DEFAULT_METHOD_PARAM = "_method";
    HiddenHttpMethodFilter过滤器可以对以Post方式提交的delete , put , patch进行转换,
    成springmvc识别的RequestMethod .DELETE / RequestMethod .PUT / ...
    4,我们需要将get <a href="user/book/600">删除指定id的书</a> 以post方式提交给后端handler,这样过滤器还会生效
    5.我们可以同jquery来处理
-->
<hr>
<h3>rest 风格的 url, 删除一本书[delete]</h3>
<a href="user/delBook/600" id="deleteBook">删除指定 id 的书</a>
<form action="" method="POST" id="hiddenForm">
    <input type="hidden" name="_method"/>
</form>
<br>
<hr>
<h3>rest 风格的 url 修改书籍[put]</h3>
<form action="user/updateBook/666" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="修改书籍~">
</form>
</body>
</html>

  1. 创建D:\lxc\javaProject\springMVC\src\com\codeSE\web\rest\BookHandler.java
package com.codeSE.web.rest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * BookHandler处理rest风格的请求-增删改查
 */
@RequestMapping("/user")
@Controller
public class BookHandler {
    //查询[GET]
    @RequestMapping(value = "/book/{id}",method = RequestMethod.GET)
    public String getBook(@PathVariable("id") String id) {
        System.out.println("查询书籍 id=" + id);
        return "success";
    }
    //添加[POST]
    @PostMapping("/addBook")
    public String addBook(String bookName){
        System.out.println("添加书籍 id=" + bookName);
        return "success";
    }
    //删除[DELETE]
    @RequestMapping(value = "/delBook/{id}",method = RequestMethod.DELETE)
    public String delBook(@PathVariable("id") String id){
        System.out.println("删除书籍 id=" + id);
        //return "success";// 如果这样返回会报错 JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS
        //  "redirect:/user/success" 会解析重定向为 springMVC/user/success
        return "redirect:/user/success" ;

    }
    //如果请求是/user/success ,就转发到 success.jsp
    @RequestMapping(value = "/success")
    public String successGenecal(){///由该方法转发到success.jsp页面
        return "success";
    }

    //修改[PUT]
    @PutMapping(value = "/updateBook/{id}")
    public String updateBook(@PathVariable("id") String id){
        System.out.println("修改书籍 id=" + id);
        //return "success";
        return "redirect:/user/success" ;

    }
}

  1. 页面完成测试

4.2.3 注意事项和细节说明
1、HiddenHttpMethodFilter, 在将 post 转成 delete / put 请求时,是按_method 参数名 来读取的
2、如果 web 项目是运行在 Tomcat 8 及以上,会发现被过滤成 DELETE 和 PUT 请求,到达控制器时能顺利执行,但是返回时(forward)会报 HTTP 405 的错误提示:消息 JSP 只允许 GET、POST 或 HEAD。

  1. 解决方式 1: 使用 Tomcat7
  2. 解决方式 2: 将请求转发(forward)改为请求重定向(redirect):重定向到一个 Handler,由 Handler 转发到页面
    在这里插入图片描述
    3、页面测试时, 如果出现点击修改书籍,仍然走的是删除 url ,是因为浏览器原因(缓存等原因), 换成 chrome 即可。

5、SpringMVC 映射请求数据

5.1 获取参数值

5.1.1 说明

  1. 开发中,如何获取到 http://xxx/url?参数名=参数值&参数名=参数值
  2. 创建D:\lxc\javaProject\springMVC\src\com\codeSE\web\requestparam\VoteHandler.java
package com.codeSE.web.requestparam;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RequestMapping(value = "/vote")
@Controller
public class VoteHandler {

    /*
     * @RequestParam ( value= "name " , required=false)
     * 1.获取到超链接传递的数据请求,http://localhost:8080/springMVC/vote/vote01?name=codeSE123
     * 2.@RequestParam表示会接收提交的参数
     * 3. value="name”表示提交的参数名是name
     * 4. required=false表示该参数可以没有,

     */
    @RequestMapping(value = "vote01")
    public String test01(@RequestParam(value = "name",required = false) String username){

        System.out.println("得到的username= " + username);
        return "success";
    }
}

5.2 获取 http 请求消息头

● 说明:

  1. 开发中,如何获取到 http 请求的消息头信息
  2. 使用较少
    ● 应用实例
    修改 VoteHandler.java, 增加方法
	/**
     *  需求:获取http 请求头信息,获取到Accept-Encoding和 Host
     * @RequestHeader( "Http请求头字段")
     */
    @RequestMapping(value = "vote02")
    public String test02(@RequestHeader("Accept-Encoding") String ae,
                         @RequestHeader("Host") String host){
        System.out.println("得到的Accept-Encoding=" + ae);//gzip, deflate, br
        System.out.println("得到的Host=" + host);//127.0.0.1:8080
        return "success";
    }

5.3 获取 javabean 形式的数据

5.3.1 使用场景说明
● 开发中,如何获取到 javaben 的数据,就是以前的 entity/pojo 对象数据
5.3.2 应用实例

  1. 创建一个entity包,包下面两个类Pet.java和Master.java
package com.codeSE.web.requestparam.entity;

public class Pet {
    private Integer id;
    private String name;

    public Pet() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

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

package com.codeSE.web.requestparam.entity;

public class Master {
    private Integer id;
    private String name;
    private Pet pet;//对象的属性是另外一个对象[涉及级联]

    public Master() {
    }

    public Master(Integer id, String name, Pet pet) {
        this.id = id;
        this.name = name;
        this.pet = pet;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    @Override
    public String toString() {
        return "Master{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pet=" + pet +
                '}';
    }
}

  1. 修改 VoteHandler.java, 增加方法
    /*
    * 演示如果获取到提交数据->封装成java对象
    * 1.方法的形参用对应的类型来指定即可,Sp正ingMVC会自动的进行封装
    * 2.如果自动的完成封装,要求提交的数据,参数名和对象的字段名保持一致
    * 3.如果属性是对象,这里就是字段名,比如Master [pet],
    *    即提交的数据参数名是pet.id pet.name,这就是级联操作
    * 4.如果提交的数据的参数名和对象的字段名不匹配,则对象的属性值就是null
     * */

    @RequestMapping(value = "/vote03")
    public String test03(Master master){
        System.out.println("master"+master);
        return "success";
    }
  1. postman完成测试

5.3.3 使用注意事项

  1. 支持级联数据获取
  2. 表单的控件名称 name 需要和 javabean 对象字段对应, 否则就是 null

5.4 获取 servlet api

  • 5.4.1 应用实例
    ● 说明
  1. 开发中, 我们可能需要使用到原生的 servlet api ,看看如何获取
  2. 使用 servlet api , 需要引入 tomcat/lib 下的 servlet-api.jar

在这里插入图片描述
● 应用实例

  1. 修改 VoteHandler.java, 增加方法
    /*
    * 使用原生servlet api,来获取提交的数据
     * */
    @RequestMapping(value = "/vote04")
    public String test04(HttpServletRequest request, HttpServletResponse response){
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("原生servlet api 获取 username="+username+"  password="+password);
        return "success";

    }
  1. postman完成测试
  • 5.4.2 使用注意事项
  1. 除了 HttpServletRequest, HttpServletResponse 还可以其它对象也可以这样的形式获取
  2. HttpSession、java.security.Principal,InputStream,OutputStream,Reader,Writer
  3. 其中一些对象也可以通过 HttpServletRequest / HttpServletResponse 对象获取,比如Session 对象 ,既可以通过参数传入,也以通过 request.getSession() 获取,效果一样,推荐使用参数形式传入,更加简单明了
  4. 举例说明
@RequestMapping(value = "/vote04")
    public String test04(HttpServletRequest request, HttpServletResponse response, HttpSession httpSession){
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("原生servlet api 获取 username="+username+"  password="+password);

        //可以看到 httpSession 和 request.getSession() 是同一个对象
        System.out.println("httpSession=" + httpSession);//httpSession=org.apache.catalina.session.StandardSessionFacade@92aae3
        System.out.println("httpSession2=" + request.getSession());//httpSession2=org.apache.catalina.session.StandardSessionFacade@92aae3
        return "success";

    }

6、 模型数据

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

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

6.1.2
方式 1: 通过 HttpServletRequest 放入 request 域

  1. 创建 …/web\model_data.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试 模型数据</title>
</head>
<body>
<h1>添加主人信息</h1>
<!--
1.
这里的字段名称和对象的属性名保持一致,级联添加属性也是一样保持名字对应关系
2.如果只是添加主人信息,则去掉宠物号和宠物名输入框即可 ,pet为 null
-->
<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>
</body>
</html>
  1. 创建 …c\web\WEB-INF\pages\vote_ok.jsp, 显示数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>vote_ok </title>
</head>
<body>
<h1>获取的的数据显示页面</h1>
<hr>
取出 request 域的数据
<br>
address:【${requestScope.address} 】<br>
主人名字= 【${requestScope.master.name }】
主人信息= 【${requestScope.master }】
宠物名字= 【${requestScope.master.pet.name }】
</body>
</html>


  1. 修改 VoteHandler.java, 增加方法
  /*
     * 1.演示将提交的数据-->封装到java对象-->springmvc会自动的将其放入到request域中
     *   在request域中:("master" , master)
     * 2.这样我们就可以再跳到到的页面取出数据.
     * */
    @RequestMapping(value = "/vote05")
    public String test05(Master master,HttpServletRequest httpServletRequest) {
        //1. springmvc会自动把获取model模型,放入到request域中,名字就是 master
        // 2.也可以手动将master放入到request
        httpServletRequest.setAttribute("address","beijing");
        //3.希望修改master的属性值
        master.setName("codeSE");
        // 4.分析析一下springmvc默认存放对象到request域中,属性名是
        // request域("master" , master100)属性名是类名/类型名  首字母小写
        return "vote_ok";
    }

postman完成测试

6.1.3
方式 2: 通过请求的方法参数 Map<String,Object> 放入 request 域

  1. 修改 model_data.jsp, 增加代码
<br/><hr/>
<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>
  1. 修改 VoteHandler.java , 增加方法
 @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";
    }

6.1.4
方式 3: 通过返回 ModelAndView 对象 实现 request 域数据

  1. 修改 model_data.jsp, 增加代码
<br/><hr/>
<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>
  1. 修改 VoteHandler.java , 增加方法
 /*
    * 演示通过返回ModelAndView对象,将数据放入到request域
     * */
    @RequestMapping(value = "/vote07")
    public ModelAndView test07(Master master) {
        ModelAndView modelAndView = new ModelAndView();
        //放入属性到modelAndView
        modelAndView.addObject("address","shanghai");
        modelAndView.addObject("master",null);//就会重新置空
        //指定跳转的视图名称
        modelAndView.setViewName("vote_ok");
        return modelAndView;
    }
  1. postman完成测试
  2. 使用注意事项
  1. 从本质看,请求响应的方法 return “xx”, 是返回了一个字符串,其实本质是返回了一个ModelAndView 对象,只是默认被封装起来的.
  2. ModelAndView 即可以包含 model 数据,也可以包含视图信息
  3. ModelAndView 对象的 addObject 方法可以添加 key-val 数据,默认在 request 域中
  4. ModelAndView 对象 setView 方法可以指定视图名称

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

6.2.1
● 说明
开发中, 控制器/处理器中获取的数据如何放入 session 域,然后在前端(VUE/JSP/…)取出显示
6.2.2 应用实例

  1. 修改 model_data.jsp, 增加代码
<br/><hr/>
<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>
  1. 修改 VoteHandler.java, 增加方法
/*
     * 将 model(master对象)放入到 session域中
     * */
    @RequestMapping(value = "/vote08")
    public String test08(Master master, HttpSession httpSession) {
        ModelAndView modelAndView = new ModelAndView();
        // master对象是默认放在request域,我们将master对象放入到session域
        httpSession.setAttribute("master", master);
        httpSession.setAttribute("address", "guangzhou");
        return "vote_ok";
    }
  1. 修改 vote_ok.jsp , 增加代码
<hr>
取出 session 域的数据 <br>
address:【${sessionScope.address} 】<br>
主人名字= 【${sessionScope.master.name }】
主人信息=【 ${sessionScope.master }】
宠物名字= 【${sessionScope.master.pet.name }】
  1. postman完成测试

6.3 @ModelAttribute 实现 prepare 方法

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

  1. @ModelAttribute 注解可以实现 这样的需求
  2. 在某个方法上,增加了@ModelAttribute 注解后
  3. 那么在调用该 Handler 的任何一个方法时,都会先调用这个方法
    ● 应用实例
    修改 VoteHandler.java, 增加方法 并测试
/*
    * 1.当在某个方法上,增加了@ModelAttribute注解
    * 2.那么在调用该 Handler的任何一个方法时,都会先调用这个方法
    * 3.类似spring 的AOP前置通知
    * 4. prepareModel前置方法,会切入到其它方法前执行.

     * */
    @ModelAttribute
    public void prepareModel() {
        System.out.println("在同一个VoteHandler下面就会调用到prepareModel()-----完成准备工作-----");
    }

6.3.2 @ModelAttribute 最佳实践
● 修改用户信息(就是经典的使用这种机制的应用),流程如下:

  1. 在修改前,在前置方法中从数据库查出这个用户
  2. 在修改方法(目标方法)中,可以使用前置方法从数据库查询的用户
  3. 如果表单中对用户的某个属性修改了,则以新的数据为准,如果没有修改,则以数据库的信息为准,比如,用户的某个属性不能修改,就保持原来的值

7、 视图和视图解析器

7.1 基本介绍

  1. 在 springMVC 中的目标方法最终返回都是一个视图(有各种视图)
  2. 返回的视图都会由一个视图解析器来处理 (视图解析器有很多种)

7.2 自定义视图

7.2.1 为什么需要自定义视图

  1. 在默认情况下,我们都是返回默认的视图, 然后这个返回的视图交由 SpringMVC 的InternalResourceViewResolver 视图处理器来处理的
    在这里插入图片描述
  2. 在实际开发中,我们有时需要自定义视图,这样可以满足更多更复杂的需求.
    7.2.2 自定义视图实例-代码实现
    1.在 /WEB-INF/ 配置 springDispatcherServlet-servlet.xml , 增加自定义视图解析器
    <!--配置自定义视图解析器-->
    <!-- 解读:
    1.配置自定义视图解析器BeanNameViewResolver
    2. BeanNameViewResolver可以去解析我们自定义的视图
    3.配置属性 order,表示视图解析器执行的顺序,值越小,优先级越高
    4.属性order的默认值是最低优先级﹐值为Integer.MAX_VALUE,
        int LOWEST_PRECEDENCE = 2147483647
    -->
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <property name="order" value="99"/>
    </bean>
  1. 创建 D:\lxc\javaProject\springMVC\src\com\codeSE\web\viewresolver\MyView.java
package com.codeSE.web.viewresolver;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/*
* 1. MyView继承了AbstractView,就可以作为一个视图使用
* 2. @Component(value = "myView"),该视图会注入到容器中,名字/id是myView
 * */
@Component(value = "codeSEView")
public class MyView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 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
        httpServletRequest.getRequestDispatcher("/WEB-INF/pages/my_view.jsp").forward(httpServletRequest,httpServletResponse);
    }
}

  1. 创建 D:\lxc\javaProject\springMVC\src\com\codeSE\web\viewresolver\GoodsHandler.java
package com.codeSE.web.viewresolver;

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

@RequestMapping("/goods")
@Controller
public class GoodsHandler {
    @RequestMapping(value = "/buy")
    public String buy() {
        System.out.println("=======buy()=====");
        return "codeSEView";//这里和自定义的名字保持一直
    }

}

  1. 创建 D:\idea_java_projects\springmvc\web\view.jsp 和 /WEB-INF/pages/my_view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>自定义视图测试</title>
</head>
<body>
<h1>自定义视图测试</h1>
<a href="goods/buy">点击到自定义视图</a>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>my_view自己的页面</title>
</head>
<body>
<h1>从自己的视图过来的</h1>
<hr/>
welcome to my_view!!!

</body>
</html>

  1. 完成测试(Postman 测试)

7.2.3 自定义视图工作流程小结

● 自定义视图-小结

  1. 自定义视图: 创建一个 View 的 bean, 该 bean 需要继承自 AbstractView, 并实现renderMergedOutputModel 方法.
  2. 并把自定义 View 加入到 IOC 容器中
  3. 自定义视图的视图处理器,使用 BeanNameViewResolver, 这个视图处理器也需要配置到 ioc 容器
  4. BeanNameViewResolver 的调用优先级需要设置一下,设置 order 比 Integer.MAX_VAL小的值. 以确保其在 InternalResourceViewResolver 之前被调用

● 自定义视图-工作流程

  1. SpringMVC 调用目标方法, 返回自定义 View 在 IOC 容器中的 id
  2. SpringMVC 调用 BeanNameViewResolver 解析视图: 从 IOC 容器中获取 返回 id 值对应的 bean, 即自定义的 View 的对象
  3. SpringMVC 调用自定义视图的 renderMergedOutputModel 方法渲染视图
  4. 说明: 如果在 SpringMVC 调用目标方法, 返回自定义 View 在 IOC 容器中的 id,不存在, 则仍然按照默认的视图处理器机制处理

7.3 目标方法直接指定转发或重定向

7.3.1 使用实例

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

  1. 默认返回的方式是请求转发,然后用视图处理器进行处理,比如在目标方法中这样写
    在这里插入图片描述
  2. 也可以在目标方法直接指定重定向或转发的 url 地址
  3. 如果指定重定向,不能定向到 /WEB-INF 目录中

● 应用实例-代码实现

  1. 修改 GoodsHandler.java, 增加方法 order()
@RequestMapping(value = "/order")
    public String order() {
        System.out.println("=======order()=====");
        //这里直接指定转发到哪个页面
        return "forward:/WEB-INF/pages/my_view.jsp";
        //重定向,如果是重定向,就不能重定向到 /WEB-INF目录中
        //return "redirect:/login.jsp";
    }
  1. 修改 view.jsp
<body>
<h1>自定义视图测试</h1>
<a href="goods/buy">点击到自定义视图</a><br/>
<a href="goods/order">测试目标方法直接指定 重定向&请求转发</a><br/>
</body>
  1. 完成测试(页面测试)

7.3.2 指定请求转发流程-Debug 源码
7.3.3 指定重定向流程-Debug 源码

练习:略

8、自己实现 SpringMVC 底层机制

【核心分发控制器+ Controller 和 Service 注入容器 + 对象自动装配 + 控制器方法获取参数 + 视图解析 + 返回 JSON 格式数据 】

8.1 搭建 SpringMVC 底层机制开发环境

在这里插入图片描述
注意:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
4、引入需要的基本的 jar 包

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!--引入原生servlet依赖的jar-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <!--
             1. scope标签表示引入的jar的作用范围
             2. provided :表示该项目在打包,放到生产环境的时候,不需要带上servlet-api.jar
             3.因为tomcat本身是有servlet的jar,到时直接使用tomcat本身的servlet-api.jar ,防止版本冲突
            -->
            <scope>provided</scope>
        </dependency>
        <!--引入原生servlet依赖的jar-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--引入常用工具类的jar-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
    </dependencies>

5、到此: 项目开发环境搭建 OK

8.2

【核心分发控制器+ Controller 和 Service 注入容器 + 对象自动装配 + 控制器方法获取参数 + 视图解析 + 返回 JSON 格式数据 】

8.2.1 实现任务阶段 1- 开发 HspDispatcherServlet
8.2.1.1 说明: 编写 HspDispatcherServlet 充当原生的 DispatcherServlet(即核心控制器)
8.2.1.2 分析+代码实现
● 分析示意图
在这里插入图片描述

● 代码实现

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
package com.codeSE.lxcspringmvc.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
*
* 1. HspDispatcherServlet充当原生DispatcherServlet
* 2.本质是一个Servlet,继承HttpServlet

 * */
public class LxcDispatcherServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("LxcDispatcherServlet的doGet()被调用...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("LxcDispatcherServlet的doPost()被调用...");

    }
}

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\resources\lxcspringmvc.xml,充当
    原生的 applicationContext-mvc.xml 文件(就是 spring 的容器配置文件, 比如指定要扫描哪些包下的类), 先创建给空的文件
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--配置 LxcDispatcherServlet -->
  <servlet>
    <servlet-name>LxcDispatcherServlet</servlet-name>
    <servlet-class>com.codeSE.lxcspringmvc.servlet.LxcDispatcherServlet</servlet-class>
    <!--给LxcDispatcherServlet配置参数,指定要操作的spring容器配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:lxcspringmvc.xml</param-value>
    </init-param>
    <!--LxcDispatcherServlet在tomcat启动时,就自动加载-->
    <load-on-startup>1</load-on-startup>

  </servlet>
  <servlet-mapping>
    <servlet-name>LxcDispatcherServlet</servlet-name>
    <!--因为HspDispatcherServlet作为前端控制器,所以需要拦截所有请求-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

8.2.1.3 配置 Tomcat, 完成测试

8.2.2 实现任务阶段 2-- 完成客户端/浏览器可以请求控制层
8.2.2.1 创建 自己的 Controller 和自定义注解
● 分析示意图
● 代码实现

  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\MonsterController.java
package com.codeSE.controller;

import com.codeSE.lxcspringmvc.annotation.Controller;
import com.codeSE.lxcspringmvc.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Controller
public class MonsterController {
    //springmvc是支持原生的servlet api,为看到底层机制
@RequestMapping(value = "/list/monster")
    public void monsterList(HttpServletRequest request, HttpServletResponse response){
        response.setContentType("text/html;charset=utf-8");

        try {
            PrintWriter writer = response.getWriter();
            writer.println("<h1>妖怪列表信邑</h1>");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

  1. 创 建 自 定 义 注 解 D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\annotation\Controller.java和RequestMapping.java的两个注解
package com.codeSE.lxcspringmvc.annotation;

import java.lang.annotation.*;

/*
* 注解用于标识一个控制器组件
 * */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    String value() default "";
}

package com.codeSE.lxcspringmvc.annotation;

import java.lang.annotation.*;

/*
* RequestMapping注解用于指定控制器-方法的映射路径

 * */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}

8.2.2.2 配置 lxcspringmvc.xml
1 在该文件指定,我们的 springmvc 要扫描的包

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <!--指定要扫描的基本包以及子包的java类-->
    <component-scan base-package="com.codeSE.controller,com.codeSE.service">

    </component-scan>
</beans>

8.2.2.3 编写 XMLParser 工具类,可以解析 hspspringmvc.xml
完成功能说明:
-编写 XMLParser 工具类, 可以解析 hspringmvc.xml, 得到要扫描的包

  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\xml\XMLParser.java
package com.codeSE.lxcspringmvc.xml;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;

/*
* XMLParser用于解析spring配置文件
 * */
public class XMLParser {
    public static String getBasePackage(String xmlFile){

        SAXReader saxReader = new SAXReader();
        //通过得到类的加载路径-->获取到spring配置文件.
        InputStream inputStream = XMLParser.class.getClassLoader().getResourceAsStream(xmlFile);
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            Element componentScanElement = rootElement.element("component-scan");
            Attribute attribute = componentScanElement.attribute("base-package");
            String basePackage = attribute.getText();
            return basePackage;
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }

    }
}

  1. D:\lxc\javaProject\lxc-springMVC\src\test\java\com\codeSE\test\LxcSpringMVCTest.java
package com.codeSE.test;

import com.codeSE.lxcspringmvc.xml.XMLParser;
import org.junit.Test;

public class LxcSpringMVCTest {
    @Test
    public void readXml(){
        String basePackage1 = XMLParser.getBasePackage("lxcspringmvc.xml");
        System.out.println("basePackage=="+basePackage1);
    }
}

8.2.2.4 开发 HspWebApplicationContext,充当 Spring 容器-得到扫描类的全路径列表
完成的功能说明

  • 把指定的目录包括子目录下的 java 类的全路径扫描到集合中,比如 ArrayList
  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\context\LxcWebApplicationContext.java
package com.codeSE.lxcspringmvc.context;

import com.codeSE.lxcspringmvc.xml.XMLParser;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;

/*
 * LxcWebApplicationContext表示我们自己的spring容器
 *
 * */
public class LxcWebApplicationContext {
    //定义属性 classFullPathList,保存扫描包/子包的类的全路径
    private ArrayList<String> classFullPathList = new ArrayList<>();

    //扫描包
    public void scanPackage(String pack) {
        //得到包所在的工作路径[绝对路径]
        //下面是通过类的加载器,得到你指定的包对应的工作路径[绝对路径]
        //"com.codeSE.controller"==>D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\
        //1.不要直接使用Junit测试,否则url null  2.启动tomcat来测试
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
        //System.out.println("url=="+url);

        //根据得到的路径,对其进行扫描,把类的全路径,保存到classFullPathList
        String path = url.getFile();
        System.out.println("path==" + path);
        //在io中,把目录,视为一个文件
        File dir = new File(path);
        //遍历dir[文件/子目录]
        for (File f : dir.listFiles()) {
            if (f.isDirectory()) { //是目录
                scanPackage(pack + "." + f.getName());//递归
            } else {
                //筛选扫描的文件(要.class,并且是有注解),目前先把文件的全路径都保存到集合,后面在注入对象到容器时,再处理
                String classFullPath = pack + "." + f.getName().replaceAll(".class", "");
                classFullPathList.add(classFullPath);
            }
        }
    }

    //自己的spring容器的初始化
    public void init() {
        String basePackage = XMLParser.getBasePackage("lxcspringmvc.xml");
        String[] packages = basePackage.split(",");
        if(basePackage.length()>0){
            for (String pack : packages) {
                scanPackage(pack);

            }
        }
        System.out.println("扫描后的classFullPathList=="+classFullPathList);

    }
}

  1. 在D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java增加 init()
package com.codeSE.lxcspringmvc.servlet;

import com.codeSE.lxcspringmvc.context.LxcWebApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
*
* 1. HspDispatcherServlet充当原生DispatcherServlet
* 2.本质是一个Servlet,继承HttpServlet

 * */
public class LxcDispatcherServlet extends HttpServlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
        LxcWebApplicationContext lxcWebApplicationContext = new LxcWebApplicationContext();
        lxcWebApplicationContext.init();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----LxcDispatcherServlet的doGet()被调用...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----LxcDispatcherServlet的doPost()被调用...");
    }
}

  1. 启动 Tomcat 完成测试,看看扫描是否成功. 需要使用 Tomcat 启动方式完成测试,直接用 Junit 测试 URL 是 null
    在这里插入图片描述
    8.2.2.5 完善 LxcWebApplicationContext,充当 Spring 容器-实例化对象到容器中
    完成功能说明
  • 将扫描到的类, 在满足条件的情况下(即有相应的注解@Controller @Service…), 反射注入到 ioc 容器
  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\context\LxcWebApplicationContext.java
package com.codeSE.lxcspringmvc.context;

import com.codeSE.lxcspringmvc.annotation.Controller;
import com.codeSE.lxcspringmvc.xml.XMLParser;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;

/*
 * LxcWebApplicationContext表示我们自己的spring容器
 *
 * */
public class LxcWebApplicationContext {
    //定义属性 classFullPathList,保存扫描包/子包的类的全路径
    private ArrayList<String> classFullPathList = new ArrayList<>();
    //定义属性ioc,存放反射生成的Bean对象:Controller/Service...
    public ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();

    //自己的spring容器的初始化
    public void init() {
        String basePackage = XMLParser.getBasePackage("lxcspringmvc.xml");
        String[] packages = basePackage.split(",");
        if (basePackage.length() > 0) {
            for (String pack : packages) {
                scanPackage(pack);

            }
        }
        System.out.println("扫描后的classFullPathList==" + classFullPathList);
        //将扫描到的类,反射到ico容器
        executeInstance();

        System.out.println("扫描后的ioc容器== "+ioc);
    }

    //扫描包
    public void scanPackage(String pack) {
        //得到包所在的工作路径[绝对路径]
        //下面是通过类的加载器,得到你指定的包对应的工作路径[绝对路径]
        //"com.codeSE.controller"==>D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\
        //1.不要直接使用Junit测试,否则url null  2.启动tomcat来测试
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
        //System.out.println("url=="+url);

        //根据得到的路径,对其进行扫描,把类的全路径,保存到classFullPathList
        String path = url.getFile();
        System.out.println("path==" + path);
        //在io中,把目录,视为一个文件
        File dir = new File(path);
        //遍历dir[文件/子目录]
        for (File f : dir.listFiles()) {
            if (f.isDirectory()) { //是目录
                scanPackage(pack + "." + f.getName());//递归
            } else {
                //筛选扫描的文件(要.class,并且是有注解),目前先把文件的全路径都保存到集合,后面在注入对象到容器时,再处理
                String classFullPath = pack + "." + f.getName().replaceAll(".class", "");
                classFullPathList.add(classFullPath);
            }
        }
    }

    //编写方法,将扫描到的类,在满足条件的情况下,反射到ioc容器
    public void executeInstance() {
        //判断是否扫描到类
        if (classFullPathList.size() == 0) {
            return;
        }

        try {
            for (String classFullPath : classFullPathList) {
                Class<?> aClass = Class.forName(classFullPath);
                if (aClass.isAnnotationPresent(Controller.class)) {//说明当前这个类有GController
                    //得到类名首字母小写
                    String beanName = aClass.getSimpleName().substring(0, 1).toLowerCase(Locale.ROOT) +
                            aClass.getSimpleName().substring(1);
                    ioc.put(beanName,aClass.newInstance());
                }
                //如果有其它的注解,可以扩展
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

  1. 完成测试 (提示: 启动 tomcat 来加载 LxcDispatcherServlet 的方式测试)

在这里插入图片描述

8.2.2.6 完成请求 URL 和控制器方法的映射关系
在这里插入图片描述
将配置的@RequestMapping 的 url 和 对应的 控制器-方法 映射关系保存到集合中

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\handler\LxcHandler.java
package com.codeSE.lxcspringmvc.handler;

import java.lang.reflect.Method;

/*
* LxcHandler 对象记录请求的url和控制器方法映射关系
 * */
public class LxcHandler {
    private String url;
    private Object controller;
    private Method method;

    public LxcHandler(String url, Object controller, Method method) {
        this.url = url;
        this.controller = controller;
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return "LxcHandler{" +
                "url='" + url + '\'' +
                ", controller=" + controller +
                ", method=" + method +
                '}';
    }
}

  1. 修 改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
package com.codeSE.lxcspringmvc.servlet;

import com.codeSE.lxcspringmvc.annotation.Controller;
import com.codeSE.lxcspringmvc.annotation.RequestMapping;
import com.codeSE.lxcspringmvc.context.LxcWebApplicationContext;
import com.codeSE.lxcspringmvc.handler.LxcHandler;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/*
*
* 1. HspDispatcherServlet充当原生DispatcherServlet
* 2.本质是一个Servlet,继承HttpServlet

 * */
public class LxcDispatcherServlet extends HttpServlet {

    //定义属性 handlerList ,保存LxcHandler
    private List<LxcHandler> lxcHandlerList = new ArrayList<>();
    //定义属性lxcWebApplicationContext,自己的spring容器
    LxcWebApplicationContext lxcWebApplicationContext = null;


    @Override
    public void init(ServletConfig config) throws ServletException {
        lxcWebApplicationContext = new LxcWebApplicationContext();
        lxcWebApplicationContext.init();
        //调用initHandlerMapping,完成url和控制器方法的映射
        initHandlerMapping();
        System.out.println("lxcHandlerList初始化结果=="+lxcHandlerList);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----LxcDispatcherServlet的doGet()被调用...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----LxcDispatcherServlet的doPost()被调用...");
    }

    //编写方法,完成url和控制器方法的映射
    public void initHandlerMapping(){
        if(lxcWebApplicationContext.ioc.isEmpty()){
            //判断当前的ioc容器是否为null
            return;
        }
        //遍历ioc容器的bean对象,然后进行url映射处理
        for (Map.Entry<String,Object> entry:lxcWebApplicationContext.ioc.entrySet()){
            //先取出注入的Object 的aClass对象
            Class<?> aClass = entry.getValue().getClass();
            if(aClass.isAnnotationPresent(Controller.class)){
            //取出所有方法
                Method[] declaredMethods = aClass.getDeclaredMethods();
                //遍历
                for (Method declaredMethod:declaredMethods){
                    //判断该方法是否有GRequestMapping
                    if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
                        //取出@RequestMapping 值->就是映射路径
                        RequestMapping requestMappingAnnotationl
                                = declaredMethod.getAnnotation(RequestMapping.class);
                        String url = requestMappingAnnotationl.value();
                        //创建LxcHandler对象
                        LxcHandler lxcHandler = new LxcHandler(url, entry.getValue(), declaredMethod);
                        lxcHandlerList.add(lxcHandler);
                    }


                }
            }
        }
    }
}

  1. 完成测试 (提示: 启动 tomcat 来加载 LxcDispatcherServlet 的方式测试)
    在这里插入图片描述

8.2.2.7 完成 LxcDispatcherServlet 分发请求到对应控制器方法
在这里插入图片描述
当用户发出请求,根据用户请求 url 找到对应的控制器-方法, 并反射调用

  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
package com.codeSE.lxcspringmvc.servlet;

import com.codeSE.lxcspringmvc.annotation.Controller;
import com.codeSE.lxcspringmvc.annotation.RequestMapping;
import com.codeSE.lxcspringmvc.context.LxcWebApplicationContext;
import com.codeSE.lxcspringmvc.handler.LxcHandler;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/*
 *
 * 1. HspDispatcherServlet充当原生DispatcherServlet
 * 2.本质是一个Servlet,继承HttpServlet

 * */
public class LxcDispatcherServlet extends HttpServlet {

    //定义属性 handlerList ,保存LxcHandler
    private List<LxcHandler> lxcHandlerList = new ArrayList<>();
    //定义属性lxcWebApplicationContext,自己的spring容器
    LxcWebApplicationContext lxcWebApplicationContext = null;


    @Override
    public void init(ServletConfig config) throws ServletException {
        lxcWebApplicationContext = new LxcWebApplicationContext();
        lxcWebApplicationContext.init();
        //调用initHandlerMapping,完成url和控制器方法的映射
        initHandlerMapping();
        System.out.println("lxcHandlerList初始化结果==" + lxcHandlerList);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----LxcDispatcherServlet的doGet()/doPost()被调用...");
        //凋用方法,完成分发请求
        executeDispatch(req,resp);
    }

    //编写方法,完成url和控制器方法的映射
    public void initHandlerMapping() {
        if (lxcWebApplicationContext.ioc.isEmpty()) {
            //判断当前的ioc容器是否为null
            return;
        }
        //遍历ioc容器的bean对象,然后进行url映射处理
        for (Map.Entry<String, Object> entry : lxcWebApplicationContext.ioc.entrySet()) {
            //先取出注入的Object 的aClass对象
            Class<?> aClass = entry.getValue().getClass();
            if (aClass.isAnnotationPresent(Controller.class)) {
                //取出所有方法
                Method[] declaredMethods = aClass.getDeclaredMethods();
                //遍历
                for (Method declaredMethod : declaredMethods) {
                    //判断该方法是否有GRequestMapping
                    if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
                        //取出@RequestMapping 值->就是映射路径
                        RequestMapping requestMappingAnnotationl
                                = declaredMethod.getAnnotation(RequestMapping.class);
                        String url = requestMappingAnnotationl.value();
                        //创建LxcHandler对象
                        LxcHandler lxcHandler = new LxcHandler(url, entry.getValue(), declaredMethod);
                        lxcHandlerList.add(lxcHandler);
                    }


                }
            }
        }
    }

    //编写方法,通过request对象,返回LxcHandler对象,没有就返回null
    private LxcHandler getLxcHandler(HttpServletRequest request) {
        //1.先获取的用户请求的uri,比如http://localhost:8080/springmvc/monster/list
        //uri = /springmvc/ monster/list
        //2.这里要注意得到uri和保存url是有一个工程路径的问题
        //两个方案解决=>第一个方案:简单tomcat直接配置application context =>/
        //第二个方案保存 lxchandler对象url 拼接getServletContext().getContextPath()

        String requestURI = request.getRequestURI();

        //遍历lxcHandlerList
        for (LxcHandler lxcHandler : lxcHandlerList) {
            if (requestURI.equals(lxcHandler.getUrl())) {//配置成功
                return lxcHandler;
            }
        }
        return  null;

    }

    //编写方法,完成分发请求任务
    private void executeDispatch(HttpServletRequest request,HttpServletResponse response){
        LxcHandler lxcHandler = getLxcHandler(request);
        try {
            if (null==lxcHandler){//说明用户请求的路径[资源不存在]
                response.getWriter().println("<h1>404 NOT FOUND!!</h1>");
            }else {//匹配成功
                lxcHandler.getMethod().invoke(lxcHandler.getController(),request,response);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}

  1. 完成测试(启动 Tomcat)
  2. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\OrderController.java增 加 方 法 和 控 制 器
package com.codeSE.controller;

import com.codeSE.lxcspringmvc.annotation.Controller;
import com.codeSE.lxcspringmvc.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Controller
public class OrderController {
    @RequestMapping(value = "/order/list")
    public void listOrder(HttpServletRequest request, HttpServletResponse response) {

        response.setContentType("text/html; charset=utf-8");
        try {
            PrintWriter printWriter = response.getWriter();
            printWriter.write("<h1>订单列表信息</h1>");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/order/add")
    public void addOrder(HttpServletRequest request, HttpServletResponse response) {

        response.setContentType("text/html; charset=utf-8");
        try {
            PrintWriter printWriter = response.getWriter();
            printWriter.write("<h1>添加订单信息</h1>");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

在这里插入图片描述
8.2.3 实现任务阶段 3- 从 web.xml 动态获取 lxcspringmvc.xml
8.2.3.1 说明: 前面我们加载 lxcspringmvc.xml 是硬编码, 现在做活, 从 web.xml 动态获取
8.2.3.2 分析+代码实现+测试
在这里插入图片描述
LxcDispatcherServlet 在创建并初始化 LxcWebApplicationContext,动态的从 web.xml 中获取到配置文件.
● 代码实现

  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\context\LxcWebApplicationContext.java
    在这里插入图片描述

  2. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
    在这里插入图片描述

  3. 完成测试(启动 tomcat 方式)

8.2.4 实现任务阶段 4- 完成自定义@Service 注解功能

8.2.4.1 功能说明: 如果给某个类加上@Service, 则可以将其注入到我们的 Spring 容器
8.2.4.2 分析+代码实现+测试
在这里插入图片描述

  • 给 Service 类标注@Service, 可以将对象注入到 Spring 容器中
  • 并可以通过接口名支持多级, 类名来获取到 Service Bean
  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\entity\Monster.java
package com.codeSE.entity;

public class Monster {
    private Integer id;
    private String name;
    private Integer age;
    private String skill;

    public Monster(Integer id, String name, Integer age, String skill) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.skill = skill;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }
}

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\annotation\Service.java
package com.codeSE.lxcspringmvc.annotation;

import java.lang.annotation.*;

/*
* Service 注解,用于标识一个Service对象,并注入到spring容器
 * */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\service\MonsterService.java
package com.codeSE.service;

import com.codeSE.entity.Monster;

import java.util.List;

public interface MonsterService {
    public List<Monster> listMonsters();
}
  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\service\impl\MonsterServiceImpl.java
package com.codeSE.service.impl;

import com.codeSE.entity.Monster;
import com.codeSE.lxcspringmvc.annotation.Service;
import com.codeSE.service.MonsterService;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/*
* MonsterServiceImpl
* */
@Service
public class MonsterServiceImpl implements MonsterService {
    @Override
    public List<Monster> listMonsters() {
        ArrayList<Monster> monsters =  new ArrayList<>();
        monsters.add(new Monster(100,"齐天大圣",599,"降妖除魔"));
        monsters.add(new Monster(200,"混世魔王",666,"攻占天庭"));
        return monsters;
    }
}

  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\resources\lxcspringmvc.xml
    在这里插入图片描述

  2. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\context\LxcWebApplicationContext.java
    在这里插入图片描述

  3. 完成测试(启动 Tomcat, 自动加载 LxcDispatcherServlet, 完成 IOC 容器的注入)

8.2.5 实现任务阶段 5- 完成 Spring 容器对象的自动装配 -@Autowried

8.2.5.1 说明: 完成 Spring 容器中对象的注入/自动装配
8.2.5.2 分析+代码实现
● 完成任务说明

  • 加入@AutoWired 注解, 进行对象属性的装配

1.创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\annotation\AutoWired.java

package com.codeSE.lxcspringmvc.annotation;

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoWired {
    String value() default "";
}
  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\MonsterController.java
package com.codeSE.controller;

import com.codeSE.entity.Monster;
import com.codeSE.lxcspringmvc.annotation.AutoWired;
import com.codeSE.lxcspringmvc.annotation.Controller;
import com.codeSE.lxcspringmvc.annotation.RequestMapping;
import com.codeSE.service.MonsterService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Controller
public class MonsterController {

    //属性
    //@AutoWired表示要完成同性的装配.
    @AutoWired
    private MonsterService monsterService;

    //springmvc是支持原生的servlet api,为看到底层机制
    @RequestMapping(value = "/list/monster")
    public void monsterList(HttpServletRequest request, HttpServletResponse response) {
        response.setContentType("text/html;charset=utf-8");
        //调用morterService


        try {
            List<Monster> monsters = monsterService.listMonsters();

            StringBuilder content = new StringBuilder("<h1>妖怪列表</h1>");
            content.append("<table width='500px' style='border-collapse:collapse' border='1px'>");
            for (Monster monster : monsters) {
                content.append("<tr><td>" + monster.getId()+ "</td><td>"
                        + monster.getName() + "</td><td>" + monster.getSkill()
                        + "</td></tr>");
            }
            content.append("</table>");
            PrintWriter printWriter = response.getWriter();
            printWriter.println(content.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\context\LxcWebApplicationContext.java增加方法,在init()调用
    在这里插入图片描述
//编写属性的自动装配
    public void executeAutowired() {
        //判断ioc有没有要装配的对象
        if (ioc.isEmpty()) {
            return;//可以抛出异常throw new RuntimeException("ioc 容器没有bean对象)
        }
        //遍历ioc容器中的所有注入的bean对象,然后获取到bean的所有字段/属性,判断是否需要装配
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //String key = entry.getKey();
            Object bean = entry.getValue();
            Field[] declaredFields = bean.getClass().getDeclaredFields();

            for (Field declaredField : declaredFields) {
                //当前这个字段的@AutoWired是否有
                if (declaredField.isAnnotationPresent(AutoWired.class)) {
                    AutoWired autoWiredAnnotation = declaredField.getAnnotation(AutoWired.class);
                    String beanName = autoWiredAnnotation.value();
                    if ("".equals(beanName)) {//如果没有设置value,按照默认规则
                        //即得到字段类型的名称的首字母小写,作为名字来进行装配
                        Class<?> type = declaredField.getType();
                        beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);

                    }
                    //如果设置value,直接按照beanName来进行装配
                    //从ioc容器中获取到bean
                    if (null == ioc.get(beanName)) {//ioc容器中,不存在你要装配的bean
                        throw new RuntimeException("ioc容器中,不存在你要装配的bean");
                    }
                    //防止属性是private,我们需要暴力破解
                    declaredField.setAccessible(true);
                    //以装配属性
                    try {
                        declaredField.set(bean, ioc.get(beanName));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);

                    }
                }
            }
        }


    }
  1. 启动 Tomcat, 完成测试,浏览器输入 http://localhost:8080/monster/list
    在这里插入图片描述

8.2.6 实现任务阶段 6- 完成控制器方法获取参数-@RequestParam

8.2.6.1 功能说明:自定义@RequestParam 和 方法参数名获取参数

8.2.6.2 完成: 将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到参数数组,进行反射调用
● 代码实现, 老师说明,整个实现思路,就是参考 SpringMVC 规范

  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
 //编写方法,完成分发请求任务
    private void executeDispatch(HttpServletRequest request,HttpServletResponse response){
        // 将需要传递给目标方法的实参
        LxcHandler lxcHandler = getLxcHandler(request);
        try {
            if (null==lxcHandler){//说明用户请求的路径[资源不存在]
                response.getWriter().println("<h1>404 NOT FOUND!!</h1>");
            }else {//匹配成功
                //目标将: HttpServletRequest和 HttpServletResponse装到参数数组
                //通过反射得到的参数数组->在反射调用方法时会使用到
                //getParameterTypes或得到当前这个方法的所有参数信息

                //1.得到目标方法的参数信息[对应的数组]
                Class<?>[] parameterTypes = lxcHandler.getMethod().getParameterTypes();
                //2.创建一个参数数组[对应实参数组],后面反射调用目标方法时,会使用到
                Object[] params = new Object[parameterTypes.length];
                //遍历parameterTypes形参数组,根据形参数组信息,将实参填充到实参数组
                for (int i = 0;i<parameterTypes.length;i++){
                //取出每一个形参类型
                    Class<?> parameterType = parameterTypes[i];
                    //如果这个形参是HttpServletRequest,将request填充到params
                    if("HttpServletRequest".equals(parameterType.getSimpleName())){
                        params[i] = request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = response;
                    }
                }

                //改进: lxcHandler.getMethod().invoke(lxcHandler.getController(),request,response);
                lxcHandler.getMethod().invoke(lxcHandler.getController(),params);

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
  1. 完成测试(启动 tomcat), 浏览器输入 http://localhost:8080/monster/list , 仍然可以看到正确的返回

8.2.6.3 完成: 在方法参数 指定 @RequestParam 的参数封装到参数数组,进行反射调用

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\annotation\RequestParam.java
package com.codeSE.lxcspringmvc.annotation;
import java.lang.annotation.*;

/*
 * RequestParam注解标注在目标方法的参数上,表示对应http请求的参数
 * */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    String value() default "";
}
  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\service\MonsterService.java新增
    public List<Monster> findMonsterByName(String name);

  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\service\impl\MonsterServiceImpl.java新增
@Override
    public List<Monster> findMonsterByName(String name) {
        ArrayList<Monster> monsters =  new ArrayList<>();
        monsters.add(new Monster(100,"齐天大圣",599,"降妖除魔"));
        monsters.add(new Monster(200,"混世魔王",666,"攻占天庭"));
        monsters.add(new Monster(300,"白骨精",599,"降妖除魔"));
        monsters.add(new Monster(400,"蜘蛛精",666,"攻占天庭"));
        monsters.add(new Monster(500,"超天大圣",599,"降妖除魔"));
        monsters.add(new Monster(600,"混世魔王1",666,"攻占天庭"));

        List<Monster> findMonsters = new ArrayList<>();
        for (Monster monster : monsters) {
            if (monster.getName().contains(name)){
                findMonsters.add(monster);
            }
        }

        return findMonsters;
    }
  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\MonsterController.java新增
//增加方法,通过name返回对应的monster集合
    @RequestMapping(value = "/monster/byName")
    public void findMonsterByName(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "name") String name) {
        response.setContentType("text/html;charset=utf-8");
        System.out.println("————接收到的参数name—————" + name);
        //调用monsterService
        try {
            List<Monster> monsters = monsterService.findMonsterByName(name);

            StringBuilder content = new StringBuilder("<h1>妖怪列表</h1>");
            content.append("<table width='500px' style='border-collapse:collapse' border='1px'>");
            for (Monster monster : monsters) {
                content.append("<tr><td>" + monster.getId() + "</td><td>"
                        + monster.getName() + "</td><td>" + monster.getSkill()
                        + "</td></tr>");
            }
            content.append("</table>");
            PrintWriter printWriter = response.getWriter();
            printWriter.println(content.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
  1. D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java新增
private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
        // 将需要传递给目标方法的实参
        LxcHandler lxcHandler = getLxcHandler(request);
        try {
            if (null == lxcHandler) {//说明用户请求的路径[资源不存在]
                response.getWriter().println("<h1>404 NOT FOUND!!</h1>");
            } else {//匹配成功
                //目标将: HttpServletRequest和 HttpServletResponse装到参数数组
                //通过反射得到的参数数组->在反射调用方法时会使用到
                //getParameterTypes或得到当前这个方法的所有参数信息

                //1.得到目标方法的参数信息[对应的数组]
                Class<?>[] parameterTypes = lxcHandler.getMethod().getParameterTypes();
                //2.创建一个参数数组[对应实参数组],后面反射调用目标方法时,会使用到
                Object[] params = new Object[parameterTypes.length];
                //遍历parameterTypes形参数组,根据形参数组信息,将实参填充到实参数组
                for (int i = 0; i < parameterTypes.length; i++) {
                    //取出每一个形参类型
                    Class<?> parameterType = parameterTypes[i];
                    //如果这个形参是HttpServletRequest,将request填充到params
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = response;
                    }
                }
                //将http请求参数封装到params数组中,老韩提示,要注意填充实参的时候,顺序问题
                //1.获取http 请求的参数集合
                //返回的Map<String ,String[]> String:表示http请求的参数名,string[]:表示http 请求的参数值

                //这里统一处理中文乱码问题
                request.setCharacterEncoding("utf-8");

                Map<String, String[]> parameterMap = request.getParameterMap();
                //2.遍历parameterMap将请求参数,按照顺序填充到实参数组params
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    //取出key,这name就是对应请求的参数名
                    String name = entry.getKey();
                    //这里只考虑提交的参数是单值的情况,即不考虑类似checkbox提示的数据
                    String value = entry.getValue()[0];
                    //得到请求的参数对应目标方法的第几个形参,然后将其填充
                    //这里专门编写一个方法,得到请求的参数对应的是第几个形参
                    int indexRequestParameterIndex = getIndexRequestParameterIndex(lxcHandler.getMethod(), name);
                    if (indexRequestParameterIndex != -1) { //找到对应的位置
                        params[indexRequestParameterIndex] = value;
                    } else {//说明并没有找到@RequestParam注解对应的参数,就会使用默认的机制进行配置[待..]
                        // 1.得到目标方法的所有形参名称
                        // 2.对得到目标方法的所有形参名进行遍历,如果匹配就把当前请求的参数值,填充到params
                        List<String> parameterNames = getParameterNames(lxcHandler.getMethod());
                        for (int i = 0; i < parameterNames.size(); i++) {
                            //如果请求参数名和目标方法的形参名一样,说明匹配成功
                            if (name.equals(parameterNames.get(i))) {
                                params[i] = value;//填充到实参数组
                                break;
                            }
                        }

                    }

                }

                //改进: lxcHandler.getMethod().invoke(lxcHandler.getController(),request,response);
			lxcHandler.getMethod().invoke(lxcHandler.getController(), params);

           }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //编写方法,返回请求参数是目标方法的第几个形参
    /**
     * @param method 目标方法
     * @param name   请求的参数
     * @return 是目标方法的第几个形参
     */
    public int getIndexRequestParameterIndex(Method method, String name) {
        //1.得到method的所有形参参数
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            boolean annotationPresent = parameter.isAnnotationPresent(RequestParam.class);
            if (annotationPresent) {
                RequestParam requestParamAnnotation = parameter.getAnnotation(RequestParam.class);
                String value = requestParamAnnotation.value();
                if (name.equals(value)) {
                    return i;//找到请求的参数,对应的目标方法的形参的位置
                }
            }
        }
        return -1;

    }
  1. 完 成 测 试 (Redeploy Tomcat 即 可 ) , 浏 览 器 输 入
    http://localhost:8080/monster/find?name=XXX
    在这里插入图片描述
    8.2.6.4 完成: 在方法参数 没有指定 @RequestParam ,按照默认参数名获取值, 进行反射调用

  2. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\MonsterController.java

//增加方法,通过name返回对应的monster集合
    @RequestMapping(value = "/monster/byName")
    public void findMonsterByName(HttpServletRequest request, HttpServletResponse response,
                                  String name) {
        response.setContentType("text/html;charset=utf-8");
        System.out.println("————接收到的参数name—————" + name);
        //调用monsterService
        try {
            List<Monster> monsters = monsterService.findMonsterByName(name);

            StringBuilder content = new StringBuilder("<h1>妖怪列表</h1>");
            content.append("<table width='500px' style='border-collapse:collapse' border='1px'>");
            for (Monster monster : monsters) {
                content.append("<tr><td>" + monster.getId() + "</td><td>"
                        + monster.getName() + "</td><td>" + monster.getSkill()
                        + "</td></tr>");
            }
            content.append("</table>");
            PrintWriter printWriter = response.getWriter();
            printWriter.println(content.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
   private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
        // 将需要传递给目标方法的实参
        LxcHandler lxcHandler = getLxcHandler(request);
        try {
            if (null == lxcHandler) {//说明用户请求的路径[资源不存在]
                response.getWriter().println("<h1>404 NOT FOUND!!</h1>");
            } else {//匹配成功
                //目标将: HttpServletRequest和 HttpServletResponse装到参数数组
                //通过反射得到的参数数组->在反射调用方法时会使用到
                //getParameterTypes或得到当前这个方法的所有参数信息

                //1.得到目标方法的参数信息[对应的数组]
                Class<?>[] parameterTypes = lxcHandler.getMethod().getParameterTypes();
                //2.创建一个参数数组[对应实参数组],后面反射调用目标方法时,会使用到
                Object[] params = new Object[parameterTypes.length];
                //遍历parameterTypes形参数组,根据形参数组信息,将实参填充到实参数组
                for (int i = 0; i < parameterTypes.length; i++) {
                    //取出每一个形参类型
                    Class<?> parameterType = parameterTypes[i];
                    //如果这个形参是HttpServletRequest,将request填充到params
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = response;
                    }
                }
                //将http请求参数封装到params数组中,老韩提示,要注意填充实参的时候,顺序问题
                //1.获取http 请求的参数集合
                //返回的Map<String ,String[]> String:表示http请求的参数名,string[]:表示http 请求的参数值

                //这里统一处理中文乱码问题
                request.setCharacterEncoding("utf-8");

                Map<String, String[]> parameterMap = request.getParameterMap();
                //2.遍历parameterMap将请求参数,按照顺序填充到实参数组params
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    //取出key,这name就是对应请求的参数名
                    String name = entry.getKey();
                    //这里只考虑提交的参数是单值的情况,即不考虑类似checkbox提示的数据
                    String value = entry.getValue()[0];
                    //得到请求的参数对应目标方法的第几个形参,然后将其填充
                    //这里专门编写一个方法,得到请求的参数对应的是第几个形参
                    int indexRequestParameterIndex = getIndexRequestParameterIndex(lxcHandler.getMethod(), name);
                    if (indexRequestParameterIndex != -1) { //找到对应的位置
                        params[indexRequestParameterIndex] = value;
                    } else {//说明并没有找到@RequestParam注解对应的参数,就会使用默认的机制进行配置[待..]
                        // 1.得到目标方法的所有形参名称
                        // 2.对得到目标方法的所有形参名进行遍历,如果匹配就把当前请求的参数值,填充到params
                        List<String> parameterNames = getParameterNames(lxcHandler.getMethod());
                        for (int i = 0; i < parameterNames.size(); i++) {
                            //如果请求参数名和目标方法的形参名一样,说明匹配成功
                            if (name.equals(parameterNames.get(i))) {
                                params[i] = value;//填充到实参数组
                                break;
                            }
                        }

                    }

                }

                //改进: lxcHandler.getMethod().invoke(lxcHandler.getController(),request,response);
                //反射调用目标方法
                Object result =lxcHandler.getMethod().invoke(lxcHandler.getController(), params);

                //这里就是对返回的结果进行解析=>原生springmvc可以通过视图解析器来完成
          
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
     //编写方法,得到目标方法的所有形参的名称,并放入到集合中返回
    public List<String> getParameterNames(Method method) {
        List<String> parametersList = new ArrayList<>();
        //获取到所以的参数名->这里有一个小细节
        //在默认情况下 parameter.getName()得到的名字不是形参真正名字
        //而是[arg0, arg1, arg2.. .],这里我们要引入一个插件,使用java8特性,这样才能解读
        Parameter[] parameters = method.getParameters();
        //遍历parameters取出名称,放入parametersList
        for (Parameter parameter : parameters) {
            parametersList.add(parameter.getName());
        }
        System.out.println("目标方法的形参参数列表==" + parametersList);
        return parametersList;
    }
  1. 修改D:\lxc\javaProject\lxc-springMVC\pom.xml
* request, response, name
*
注意:
在默认情况下,返回的并不是 request, response ,name
而是 arg0, arg1,arg2
需要使用到 jdk8的新特性,并需要在 pom.xml配置 maven
编译插件(可以百度搜索到),才能得到 request, response, name
* <plugin>
* <groupId>org.apache.maven.plugins</groupId>
* <artifactId>maven-compiler-plugin</artifactId>
* <version>3.7.0</version>
* <configuration>
* <source>1.8</source>
* <target>1.8</target>
* <compilerArgs>
* <arg>-parameters</arg>
* </compilerArgs>
* <encoding>utf-8</encoding>
* </configuration>
* </plugin>
  1. 点击 maven 管理,clean 项目,在重启一下 tomcat ,完成测试

8.2.7 实现任务阶段 7- 完成简单视图解析

8.2.7.1 功能说明:通过方法返回的 String, 转发或者重定向到指定页面

  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\service\MonsterService.java
package com.codeSE.service;

import com.codeSE.entity.Monster;

import java.util.List;

public interface MonsterService {
    public List<Monster> listMonsters();
    public List<Monster> findMonsterByName(String name);
    public boolean login(String name);
}

  1. 修 改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\service\impl\MonsterServiceImpl.java
//处理登录的方法
    @Override
    public boolean login(String name) {
        if ("齐天大圣".equals(name)){
            return true;
        }
        return false;
    }
  1. 增加
//处理妖怪登录的方法,返回要请求转发/重定向的字符串
    @RequestMapping("/monster/login")
    public String login(HttpServletRequest request,HttpServletResponse response,  String mName) {
        System.out.println( "--接收到mName---" + mName);
        request.setAttribute("mName",mName);//将mNam且没置到request域

        boolean b = monsterService.login(mName);
        if (b) {
            //return "forward:/login_ok.jsp";
            //return "redirect:/login_ok.jsp";
            return "/login_ok.jsp";
        } else {
            return "forward:/login_error.jsp";
        }


    }
  1. 增加D:\lxc\javaProject\lxc-springMVC\src\main\webapp\login_ok.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>登录成功</title>
</head>
<body>
<h1>登录成功</h1>
欢迎您:${requestScope.mName}
</body>
</html>
  1. 增加D:\lxc\javaProject\lxc-springMVC\src\main\webapp\login_error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"  %>
<html>
<head>
    <title>登录失败</title>
</head>
<body>
<h1>登录失败</h1>
sorry:${requestScope.mName}

</body>
</html>
  1. 增加D:\lxc\javaProject\lxc-springMVC\src\main\webapp\login_ok.jsp
    //编写方法,完成分发请求任务
    private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
        // 将需要传递给目标方法的实参
        LxcHandler lxcHandler = getLxcHandler(request);
        try {
            if (null == lxcHandler) {//说明用户请求的路径[资源不存在]
                response.getWriter().println("<h1>404 NOT FOUND!!</h1>");
            } else {//匹配成功
                //目标将: HttpServletRequest和 HttpServletResponse装到参数数组
                //通过反射得到的参数数组->在反射调用方法时会使用到
                //getParameterTypes或得到当前这个方法的所有参数信息

                //1.得到目标方法的参数信息[对应的数组]
                Class<?>[] parameterTypes = lxcHandler.getMethod().getParameterTypes();
                //2.创建一个参数数组[对应实参数组],后面反射调用目标方法时,会使用到
                Object[] params = new Object[parameterTypes.length];
                //遍历parameterTypes形参数组,根据形参数组信息,将实参填充到实参数组
                for (int i = 0; i < parameterTypes.length; i++) {
                    //取出每一个形参类型
                    Class<?> parameterType = parameterTypes[i];
                    //如果这个形参是HttpServletRequest,将request填充到params
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = response;
                    }
                }
                //将http请求参数封装到params数组中,老韩提示,要注意填充实参的时候,顺序问题
                //1.获取http 请求的参数集合
                //返回的Map<String ,String[]> String:表示http请求的参数名,string[]:表示http 请求的参数值

                //这里统一处理中文乱码问题
                request.setCharacterEncoding("utf-8");

                Map<String, String[]> parameterMap = request.getParameterMap();
                //2.遍历parameterMap将请求参数,按照顺序填充到实参数组params
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    //取出key,这name就是对应请求的参数名
                    String name = entry.getKey();
                    //这里只考虑提交的参数是单值的情况,即不考虑类似checkbox提示的数据
                    String value = entry.getValue()[0];
                    //得到请求的参数对应目标方法的第几个形参,然后将其填充
                    //这里专门编写一个方法,得到请求的参数对应的是第几个形参
                    int indexRequestParameterIndex = getIndexRequestParameterIndex(lxcHandler.getMethod(), name);
                    if (indexRequestParameterIndex != -1) { //找到对应的位置
                        params[indexRequestParameterIndex] = value;
                    } else {//说明并没有找到@RequestParam注解对应的参数,就会使用默认的机制进行配置[待..]
                        // 1.得到目标方法的所有形参名称
                        // 2.对得到目标方法的所有形参名进行遍历,如果匹配就把当前请求的参数值,填充到params
                        List<String> parameterNames = getParameterNames(lxcHandler.getMethod());
                        for (int i = 0; i < parameterNames.size(); i++) {
                            //如果请求参数名和目标方法的形参名一样,说明匹配成功
                            if (name.equals(parameterNames.get(i))) {
                                params[i] = value;//填充到实参数组
                                break;
                            }
                        }

                    }

                }

                //改进: lxcHandler.getMethod().invoke(lxcHandler.getController(),request,response);
                //反射调用目标方法
                Object result =lxcHandler.getMethod().invoke(lxcHandler.getController(), params);

                //这里就是对返回的结果进行解析=>原生springmvc可以通过视图解析器来完成
                //这里直接解析,只要把视图解析的核心机制讲清楚就0K
                if (result instanceof  String){
                    System.out.println(result);
                    String viewName = (String) result;
                    if (viewName.contains(":")){//说明你返回的String结果forward:/login_ok.jsp或者redirect:/login_ok.jsp
                        String viewType = viewName.split(":")[0]; // forward | redirect
                        String viewPage = viewName.split(":")[1]; // 是你要跳转的页面名

                        //判断是forward还是redirect
                        if("forward".equals(viewType)){
                            request.getRequestDispatcher(viewPage).forward(request,response);
                        } else if ("redirect".equals(viewType)) {
                            response.sendRedirect(viewPage);
                        }
                    }else { //默认是请求转发
                        request.getRequestDispatcher(viewName).forward(request,response);
                    }
                } else if (result instanceof ArrayList) {
                    //判断目标方法是否有@ResponseBody
                    Method method = lxcHandler.getMethod();
                    if (method.isAnnotationPresent(ResponseBody.class)){
                        //把result [ArrayList]转成json格式数据-->返回
                        //这里我们需要使用jackson包下的工具类可以轻松的搞定.
                        ObjectMapper objectMapper = new ObjectMapper();
                        String resultJson = objectMapper.writeValueAsString(result);
                        response.setContentType("text/html; charset=utf-8");
                        //简单处理就直接返回
                        PrintWriter writer = response.getWriter();
                        writer.println(resultJson);
                        writer.flush();
                        writer.close();
                    }

                }

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

8.2.7.3 完成测试
1 启动 tomcat
2. 浏 览 器 输 入http://localhost:8080/monster/login?mName=XXX

8.2.8 实现任务阶段 8- 完成返回 JSON 格式数据-@ResponseBody

8.2.8.1 功能说明:通自定义@ResponseBody 返回 JSON 格式数据

  1. 创建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\annotation\ResponseBody.java
package com.codeSE.lxcspringmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
    String value() default "";
}
  1. 创 建D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\controller\MonsterController.java,增加方法
 //编写方法,返回json格式的数据

    /**
     * 目标方法返回的结果是给springmvc底层通过反射调用的位置
     * 我们在springmvc底层反射调用的位置,接收到结果并解析即可
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/monster/list/json")
    @ResponseBody
    public List<Monster> listMonsterByJson(HttpServletRequest request,HttpServletResponse response){
        List<Monster> monsters = monsterService.listMonsters();
        return monsters;
    }
 <!--引入jackson使用他的工具类操作json-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.1</version>
        </dependency>

  1. 修改D:\lxc\javaProject\lxc-springMVC\src\main\java\com\codeSE\lxcspringmvc\servlet\LxcDispatcherServlet.java
/编写方法,完成分发请求任务
    private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
        // 将需要传递给目标方法的实参
        LxcHandler lxcHandler = getLxcHandler(request);
        try {
            if (null == lxcHandler) {//说明用户请求的路径[资源不存在]
                response.getWriter().println("<h1>404 NOT FOUND!!</h1>");
            } else {//匹配成功
                //目标将: HttpServletRequest和 HttpServletResponse装到参数数组
                //通过反射得到的参数数组->在反射调用方法时会使用到
                //getParameterTypes或得到当前这个方法的所有参数信息

                //1.得到目标方法的参数信息[对应的数组]
                Class<?>[] parameterTypes = lxcHandler.getMethod().getParameterTypes();
                //2.创建一个参数数组[对应实参数组],后面反射调用目标方法时,会使用到
                Object[] params = new Object[parameterTypes.length];
                //遍历parameterTypes形参数组,根据形参数组信息,将实参填充到实参数组
                for (int i = 0; i < parameterTypes.length; i++) {
                    //取出每一个形参类型
                    Class<?> parameterType = parameterTypes[i];
                    //如果这个形参是HttpServletRequest,将request填充到params
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = request;
                    } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                        params[i] = response;
                    }
                }
                //将http请求参数封装到params数组中,老韩提示,要注意填充实参的时候,顺序问题
                //1.获取http 请求的参数集合
                //返回的Map<String ,String[]> String:表示http请求的参数名,string[]:表示http 请求的参数值

                //这里统一处理中文乱码问题
                request.setCharacterEncoding("utf-8");

                Map<String, String[]> parameterMap = request.getParameterMap();
                //2.遍历parameterMap将请求参数,按照顺序填充到实参数组params
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    //取出key,这name就是对应请求的参数名
                    String name = entry.getKey();
                    //这里只考虑提交的参数是单值的情况,即不考虑类似checkbox提示的数据
                    String value = entry.getValue()[0];
                    //得到请求的参数对应目标方法的第几个形参,然后将其填充
                    //这里专门编写一个方法,得到请求的参数对应的是第几个形参
                    int indexRequestParameterIndex = getIndexRequestParameterIndex(lxcHandler.getMethod(), name);
                    if (indexRequestParameterIndex != -1) { //找到对应的位置
                        params[indexRequestParameterIndex] = value;
                    } else {//说明并没有找到@RequestParam注解对应的参数,就会使用默认的机制进行配置[待..]
                        // 1.得到目标方法的所有形参名称
                        // 2.对得到目标方法的所有形参名进行遍历,如果匹配就把当前请求的参数值,填充到params
                        List<String> parameterNames = getParameterNames(lxcHandler.getMethod());
                        for (int i = 0; i < parameterNames.size(); i++) {
                            //如果请求参数名和目标方法的形参名一样,说明匹配成功
                            if (name.equals(parameterNames.get(i))) {
                                params[i] = value;//填充到实参数组
                                break;
                            }
                        }

                    }

                }

                //改进: lxcHandler.getMethod().invoke(lxcHandler.getController(),request,response);
                //反射调用目标方法
                Object result =lxcHandler.getMethod().invoke(lxcHandler.getController(), params);

                //这里就是对返回的结果进行解析=>原生springmvc可以通过视图解析器来完成
                //这里直接解析,只要把视图解析的核心机制讲清楚就0K
                if (result instanceof  String){
                    System.out.println(result);
                    String viewName = (String) result;
                    if (viewName.contains(":")){//说明你返回的String结果forward:/login_ok.jsp或者redirect:/login_ok.jsp
                        String viewType = viewName.split(":")[0]; // forward | redirect
                        String viewPage = viewName.split(":")[1]; // 是你要跳转的页面名

                        //判断是forward还是redirect
                        if("forward".equals(viewType)){
                            request.getRequestDispatcher(viewPage).forward(request,response);
                        } else if ("redirect".equals(viewType)) {
                            response.sendRedirect(viewPage);
                        }
                    }else { //默认是请求转发
                        request.getRequestDispatcher(viewName).forward(request,response);
                    }
                } else if (result instanceof ArrayList) {
                    //判断目标方法是否有@ResponseBody
                    Method method = lxcHandler.getMethod();
                    if (method.isAnnotationPresent(ResponseBody.class)){
                        //把result [ArrayList]转成json格式数据-->返回
                        //这里我们需要使用jackson包下的工具类可以轻松的搞定.
                        ObjectMapper objectMapper = new ObjectMapper();
                        String resultJson = objectMapper.writeValueAsString(result);
                        response.setContentType("text/html; charset=utf-8");
                        //简单处理就直接返回
                        PrintWriter writer = response.getWriter();
                        writer.println(resultJson);
                        writer.flush();
                        writer.close();
                    }

                }

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

8.2.8.3 完成测试

  1. 启动 Tomcat
  2. 浏览器输入 http://localhost:8080/monster/list/json
    在这里插入图片描述

9、数据格式化

9.1 基本介绍

说明: 在我们提交数据(比如表单时)SpringMVC 怎样对提交的数据进行转换和处理的

  1. 基本数据类型可以和字符串之间自动完成转换,比如:
    Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作。[相互转换,这里只列出部分]
2. ConversionService converters =java.lang.Boolean -> java.lang.String :
org.springframework.core.convert.support.ObjectToStringConverter@f874ca
java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f 

9.2 基本数据类型和字符串自动转换

9.2.1 应用实例 -页面演示方式
● 说明: 基本数据类型可以和字符串之间自动完成转换

  1. 创建D:\lxc\javaProject\springMVC\src\com\codeSE\web\datavalid\entity\Monster.java
package com.codeSE.web.datavalid.entity;

public class Monster {
    private Integer id;
    private String email;
    private Integer age;
    private String name;

    public Monster() {
    }

    public Monster(Integer id, String email, Integer age, String name) {
        this.id = id;
        this.email = email;
        this.age = age;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

  1. 创建D:\lxc\javaProject\springMVC\web\WEB-INF\pages\atavalid\data_valid.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>添加妖怪~~</h3>
<!--
这里的表单,我们使用 springMVC
的标签来完成
特别说明几点:
1. SpringMVC
表单标签在显示之前必须在 request中有一个 bean,该 bean的属性和表单标签的字段要对应!
request中的 key为: form标签的 modelAttrite属性值, 比如这里的 monsters
2. SpringMVC的 form:form标签的 action属性值中的 /不代表 WEB应用的根目录.
-->
<form:form action="?" method="post" modelAttribute="monster">
  妖怪名字: <form:input path="name"/> <br><br>
  妖怪年龄~: <form:input path="age"/> <br><br>
  电子邮件: <form:input path="email"/> <br><br>
  <input type="submit" value="添加妖怪"/>
</form:form>
</body>
</html>

  1. 创建D:\lxc\javaProject\springMVC\src\com\codeSE\web\datavalid\MonsterHandler.java
package com.codeSE.web.datavalid;

import com.codeSE.web.datavalid.entity.Monster;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

/**
 * MonsterHandler处理器响应用户提交数据
 *
 * @Scope(value="prototype")表示每次请求NonsterHandler会生成一个新的对簇
 */
@Controller
@Scope(value = "prototype")
public class MonsterHandler {
    //显示添力monster的界面

    /**
     * 1.这里Map <String,Object> map
     * 当我们向map添加的数据时,会默认存放至request域中
     * @param map
     * @return
     */
    @RequestMapping(value = "addMonsterUI")
    public String addMonsterUI(Map<String, Object> map) {
        /*
        1.这里的表单,我们使用 springMVC的标签来完成
        2. SpringMVC表单标签在显示之前必须在 request中有一个 bean,该 bean的属性和表单标签的字段要对应!
            request中 的 key,为 : form标 签 的 modelAttribute属 性 值 , 比 如 这 里 的monsters
        3. SpringMVC的 form:form标签的 action属性值中的 /不代表 WEB应用的根目录.
        4. <form:form action="monster" method="POST" modelAttribute="monster">
            //这里需要 给 request增加一个 monster, 因为jsp页面的modelAttribute="monster"需要
            //这时是 springMVC的内部的检测机制 即使是一个空的也需要,否则报错.
        * */
        map.put("monster",new Monster());
        return "datavalid/monster_addUI";
    }
}

  1. 创建D:\lxc\javaProject\springMVC\web\WEB-INF\pages\atavalid\monster_addUI.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>添加妖怪~~</h3>
<!--
这里的表单,我们使用 springMVC
的标签来完成
特别说明几点:
1. SpringMVC
表单标签在显示之前必须在 request中有一个 bean,该 bean的属性和表单标签的字段要对应!
request中的 key为: form标签的 modelAttrite属性值, 比如这里的 monsters
2. SpringMVC的 form:form标签的 action属性值中的 /不代表 WEB应用的根目录.
-->
<form:form action="save" method="post" modelAttribute="monster">
  妖怪名字: <form:input path="name"/> <br><br>
  妖怪年龄~: <form:input path="age"/> <br><br>
  电子邮件: <form:input path="email"/> <br><br>
  <input type="submit" value="添加妖怪"/>
</form:form>
</body>
</html>

  1. 创建D:\lxc\javaProject\springMVC\web\WEB-INF\pages\atavalid\success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>操作成功</title>
</head>
<body>
<h1>恭喜, 操作成功~</h1>
</body>
</html>
  1. 测试,看看是否可以正确显示添加要求界面(页面测试)
    浏览器: http://localhost:8080/springmvc/data_valid.jsp

  2. 修改 MonsterHandler.java , 增加处理添加请求

 //保存方法
    /*
        1. springmvc可以将提交的数据,按照参数名和对象的属性名匹配
        2. 直接封装到对象中
        string => Integer
    */
    @RequestMapping(value = "save")
    public String save(Monster monster) {
        System.out.println("---monster---" + monster);
        return "datavalid/success";
    }
  1. 测试 , 浏览器: http://localhost:8080/springmvc/data_valid.jsp
  1. 如果 age 输入的是 数字,则通过, 说明 SpringMVC 可以将提交的字符串 数字,比如"28",转成 Integer/int
  2. 如果不是数字,则给出 400 的页面

9.3 特殊数据类型和字符串间的转换

9.3.1 应用实例 -页面演示方式

  1. 特殊数据类型和字符串之间的转换使用注解(比如日期,规定格式的小数比如货币形式等)

  2. 对于日期和货币可以使用 @DateTimeFormat 和 @NumberFormat 注解. 把这两个注解标记在字段上即可.

  3. 修改 Monster.java , 增加 birthday 和 salary 字段

 @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birthday;
    @NumberFormat(pattern="###,###.##")
    private float salary;
  1. 修改 monster_addUI, 增加 birthday 和 salary 字段
妖怪年龄~: <form:input path="age"/> <br><br>
妖怪生日: <form:input path="birthday"/> 要求以"9999-11-11"的形式<br><br>
妖怪工资: <form:input path="salary"/> 要求以"123,890.12"的形式<br><br>
  1. 测试
  1. 如果 birthday 和 salary 是按照指定格式输入,则通过, 说明 SpringMVC 可以按注解指定格式转换
  2. 如果没有按照注解指定格式,则给出 400 的页面

9.3.2 应用实例 -Postman 完成测试

  • 测试
  1. 如果 birthday 和 email 是按照指定格式输入,则通过, 说明 SpringMVC 可以按注解指定格式转换
  2. 如果没有按照注解指定格式,则给出 400 的页面
  3. 如何给出对应的提示,后面马上讲解

10. 验证以及国际化

10.1 概述
● 概述

  1. 对输入的数据(比如表单数据),进行必要的验证,并给出相应的提示信息。
  2. 对于验证表单数据,springMVC 提供了很多实用的注解, 这些注解由 JSR 303 验证框架提供.
    ● JSR 303 验证框架
    1.JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 中
    2.JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,
    并通过标准的验证接口对 Bean 进行验证
    3.JSR 303 提供的基本验证注解有:
    在这里插入图片描述
    ● Hibernate Validator 扩展注解
    1.Hibernate Validator 和 Hibernate 没有关系,只是 JSR 303 实现的一个扩展.
    2.Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
    在这里插入图片描述
    10.2 应用实例-代码实现
    1.引入验证和国际化相关的 jar 包
    在这里插入图片描述
    在这里插入图片描述

2.修改 Monster.java
在这里插入图片描述

3.修改 MonsterHandler.java

 /*
        1. springmvc可以将提交的数据,按照参数名和对象的属性名匹配
        2. 直接封装到对象中
        string => Integer
        3. @Valid Monster monster:表示对monster接收的数据进行校验
        4. Errors errors表示如果校验出现错误,将校验的错误信息保存errors
        5. Map<String,Object> map_表示如果校验出现错误,将校验的错误信息保存 map
        6.校验发生的时机:在springmvc底层,反射调用目标方法时,会接收到http请求的数据,然后根据注解来进行验证
            ,在验证过程中,如果出现了错误,就把错误信息填充errors 和 map
    */
@RequestMapping(value = "/save", method = RequestMethod.POST)
    public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
        System.out.println(" ----monster---" + monster);
        //我们为了看到验证的情况,我们输出map和 errors
        System.out.println("===== map ======");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println(" key= " + entry.getKey() + " value=" + entry.getValue());
        }
        System.out.println("===== errors ======");

        if (errors.hasErrors()) {
            List<ObjectError> allErrors = errors.getAllErrors();
            for (ObjectError error : allErrors) {
                System.out.println("error=" + error);
            }
            return "datavalid/monster_addUI";
        }


        return "datavalid/success";

    }
---这里可以测试一下,看看效果----
=====map=====
key=monster value=Monster{id=null, email='jack@sohu.com', age=900, name='',
birthday=Thu Nov 11 00:00:00 CST 1999, salary=11.11}
key=org.springframework.validation.BindingResult.monster
value=org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'monster' on field 'age': rejected value [900]; codes
[Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.age,age]; arguments []; default message [age],100,1]; default message
[需要在 1 和 100 之间]
Field error in object 'monster' on field 'name': rejected value []; codes
[NotEmpty.monster.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.name,name]; arguments []; default message [name]]; default message
[不能为空]
=====errors=====
验证出现错误
验证错误=Field error in object 'monster' on field 'age': rejected value [900]; codes
[Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.age,age]; arguments []; default message [age],100,1]; default message
[需要在 1 和 100 之间]
验证 错误=Field error in object 'monster' on field 'name': rejected value []; codes
[NotEmpty.monster.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.name,name]; arguments []; default message [name]]; default message
[不能为空]

在这里插入图片描述
4. 配置国际化文件 springDispatcherServlet-servlet.xml

 <!--配置国际化错误信息的资源处理 bean -->
    <bean id="messageSource" class=
   "org.springframework.context.support.ResourceBundleMessageSource">
        <!--
       配置国际化文件名字如果你这样配的话表示 messageSource回到src/i18nXXX.properties去读取错误信息
        -->
        <property name="basename" value="i18n"></property>
    </bean>
  1. 创建国际化文件D:\lxc\javaProject\springMVC\src\i18n.properties
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\u0030\u4e4b\u95f4
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e
  1. 修改 monster_addUI.jsp , 回显错误信息
<form:form action="save" method="POST" modelAttribute="monster">
妖怪名字: <form:input path="name"/> <form:errors path="name"/><br><br>
妖怪年龄~: <form:input path="age"/> <form:errors path="age"/><br><br>
妖怪生日: <form:input path="birthday"/> <form:errors path="birthday"/> 要求
以"9999-11-11"的形式<br><br>
妖 怪 工 资 : <form:input path="salary"/> <form:errors path="salary"/> 要 求 以
"123,890.12"的形式<br><br>
电子邮件: <form:input path="email"/><br><br>
<input type="submit" value="添加妖怪"/>
</form:form>
  1. 完成测试
    在这里插入图片描述
    10.2.2 细节说明和注意事项
  2. 在需要验证的 Javabean/POJO 的字段上加上相应的验证注解.
  3. 目标方法上,在 JavaBean/POJO 类型的参数前, 添加 @Valid 注解. 告知 SpringMVC该 bean 是需要验证的
  4. 在 @Valid 注解之后, 添加一个 Errors 或 BindingResult 类型的参数, 可以获取到验证的错误信息
  5. 需要使用 <form:errors path=“email”></form:errors> 标签来显示错误消息, 这个标签,需要写在form:form 标签内生效.
  6. 错误消息的国际化文件 i18n.properties , 中文需要是 Unicode 编码,使用工具转码.
    √格式: 验证规则.表单 modelAttribute 值.属性名=消息信息
    √NotEmpty.monster.name=\u540D\u5B57\u4E0D\u80FD\u4E3A\u7A7A
    √typeMismatch.monster.age=\u7C7B\u578B\u4E0D\u5339\u914D
  7. 注解@NotNull 和 @NotEmpty 的区别说明
  1. 查看源码可以知道 : @NotEmpty Asserts that the annotated string, collection, mapor array is not {@code null} or empty.
  2. 查看源码可以知道 : @NotNull The annotated element must not be {@code null}
    Accepts any type.
  3. 如果是字符串验证空, 建议使用 @NotEmpty
  1. SpingMVC 验证时,会根据不同的验证错误, 返回对应的信息

10.3 注解的结合使用

● 问题提出, age 没有, 是空的,提交确是成功了

  1. 使用@NotNull + @Range 组合使用解决
  2. 具体代码
public class Monster {
private Integer id;
@NotEmpty(message = "电子邮件不为空~~~~")
private String email;
@NotNull(message = "年龄必须填写 1-100")
@Range(min = 1, max = 100)
private Integer age;
@NotNull(message = "生日不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NotNull(message = "薪水不能为空")
@NumberFormat(pattern = "###,###.##")
private Float salary;
}
  1. 测试(页面方式),这时 age 不能为空,同时必须是 1-100, (也不能输入 haha, hello 等不能转成数字的内容)

10.4 数据类型转换校验核心类-DataBinder

● DataBinder 工作机制-了解
图例 Spring MVC 通过反射机制对目标方法进行解析,将请求消息绑定到处理方法的入参中。
数据绑定的核心部件是 DataBinder,运行机制如下
在这里插入图片描述

  • Debug 一下 validate 得到验证 errors 信息

10.5 取消某个属性的绑定

10.5.1 使用实例
● 说明
在默认情况下,表单提交的数据都会和 pojo 类型的 javabean 属性绑定,如果程序员在开发中,希望取消某个属性的绑定,也就是说,不希望接收到某个表单对应的属性的值,则可以通过 @InitBinder 注解取消绑定.

  1. 编写一个方法, 使用@InitBinder 标识的该方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
  2. @InitBinder 方法不能有返回值,它必须声明为 void。
  3. @InitBinder 方法的参数通常是是 WebDataBinder
    ● 案例-不希望接收怪物的名字属性
  4. 修改 MonsterHandler.java , 增加方法
//取消绑定 monster的 name表单提交的值给 monster.name属性
//方法上需要标注@InitBinder springmvc底层会初始化WebDataBinder

//1. setDisallowedFields("name")表示取消指定属性的绑定.是可变形参,可以指定多个字段
// 即:当表单提交字段为 namel时,就不在把接收到的name值,填充到model数据monster li的name属性
//2.当将一个字段/属性,设置机制: springmvc在底层通过反射调用目标方法时,
//接收到http请求的参数和值,使用反射+注解技术取消对指定属性的填充为 disallowed,就不在接收表单提交的值
//3.那么这个字段/属性的值,就是该对象默认的值 (具体看程序员定义时指定)
//4.一般来说,如果不接收表单字段提交数据,则该对象字段的验证也就没有意义了
// ,可以注销掉,比如 注销 //@NotEmpty
// //@NotEmpty
// private String name;
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
//测试完,记得注销了.
dataBinder.setDisallowedFields("name");
}
  1. 修改 Monster.java
    注销相应的验证绑定
  2. 完成测试(页面测试) 或 postman

10.5.2 注意事项和细节说明

  1. setDisallowedFields() 是可变形参,可以指定多个字段
  2. 当将一个字段/属性,设置为 disallowed,就不在接收表单提交的值,那么这个字段/属性的值,就是该对象默认的值 (具体看程序员定义时指定)
  3. 一般来说,如果不接收表单字段提交数据,则该对象字段的验证也就没有意义了可以注销掉,比如 注销 //@NotEmpty

11 中文乱码处理

11.1 自定义中文乱码过滤器
● 说明
当表单提交数据为中文时,会出现乱码,我们来解决一下(提示:先恢复 name 属性的绑定)

  1. 创 建 过 滤 器D:\lxc\javaProject\springMVC\src\com\codeSE\web\filter\MyCharacterFilter.java
package com.codeSE.web.filter;

import javax.servlet.*;
import java.io.IOException;

/*
* 编写过滤器,处理中文乱码
 * */
public class MyCharacterFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //这里加入对编码的处理
        servletRequest.setCharacterEncoding("utf-8");
        //放行请求
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

  1. 配置 web.xml , 将该过滤器配置在最前面
    <!--配置处理中文乱码的过滤器,全部拦截-->
    <filter>
        <filter-name>myCharacterFilter</filter-name>
        <filter-class>com.codeSE.web.filter.MyCharacterFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myCharacterFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  1. 完成测试

11.2 Spring 提供的过滤器处理中文

  1. 修改 web.xml , 换成 Spring 提供的过滤器,处理中文乱码
 <!--配置处理中文乱码的过滤器,全部拦截-->
<!--<filter>-->
    <!--    <filter-name>myCharacterFilter</filter-name>-->
    <!--    <filter-class>com.codeSE.web.filter.MyCharacterFilter</filter-class>-->
    <!--</filter>-->
    <!--<filter-mapping>-->
    <!--    <filter-name>myCharacterFilter</filter-name>-->
    <!--    <url-pattern>/*</url-pattern>-->
<!--</filter-mapping>-->
    
    <!--web.xml , 换成 Spring 提供的过滤器,处理中文乱码 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  1. 完成测试

12 处理 json 和 HttpMessageConverter

2.1 处理 JSON-@ResponseBody
● 说明:
项目开发中,我们往往需要服务器返回的数据格式是按照 json 来返回的
● 应用案例

  1. 引入处理 json 需要的 jar 包,注意 spring5.x 需要使用 jackson-2.9.x.jar 的包.
    在这里插入图片描述
  2. 新建D:\lxc\javaProject\springMVC\src\com\codeSE\web\json\entity\Dog.java和新建D:\lxc\javaProject\springMVC\src\com\codeSE\web\json\DogHandler.java
package com.codeSE.web.json.entity;

public class Dog {
    private String name;
    private String address;

    public Dog() {
    }

    public Dog(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

package com.codeSE.web.json;

import com.codeSE.web.json.entity.Dog;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class DogHandler {
    /*
    1.目标方法@ResponseBody,表返回的数据是json格式
    2.springmvc底层根据目标方法@ResponseBody,返回指定格式,根据的http 请求来进行处理
    4.底层原理我们在前面自定义GResponseBody讲过,这里原生的springmvc使用转换器
    5.
     * */
    @RequestMapping(value = "/json/dog")
    @ResponseBody
    public Dog getDog() {

        //返回对象
        // springmvc会根据你的设置,转成json格式数据返回
        Dog dog = new Dog();
        dog.setName("一号狗");
        dog.setAddress("小明家");
        return dog;
    }
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>json 提交</title>

</head>
<body>
<h1>请求一个 json 数据</h1>
<button  id="getJson" onclick="send()">点击获取 json 数据</button>

<script>
    function send() {
        var element = document.querySelector("#getJson");
        console.log(element)
        window.fetch(`/springMVC/json/dog?time=`+new Date()).then(res=>{
            console.log(res)

        })
        return false
    }

</script>
</body>
</html>
  1. 完成测试(页面方式), 浏览器 http://localhost:8080/springmvc/json.jsp
    在这里插入图片描述

12.2 处理 JSON-@RequestBody

● 应用案例

  • 前面是通过表单, 或者 url 请求携带 参数名=参数值 把数据提交给目标方法
  1. 给大家举例客户端发送 json 字符串数据,
  2. 使用 SpringMVC 的 @RequestBody 将客户端提交的 json 数据,封装成 JavaBean 对象
  3. 再把这个 javabean 以 json 对象形式返回
  1. 修改 json.jsp, 增加发送 json 数据代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>json 提交</title>

</head>
<body>
<h1>请求一个 json 数据</h1>
<button id="getJson" onclick="send()">点击获取 json 数据</button>
<hr/>
u:<input id="userName" type="text"><br/>
a:<input id="age" type="text"><br/>
<button id="postJson" onclick="postMsg()">点击发送 json 数据</button>

<script>
    function send() {
        window.fetch(`/springMVC/json/dog?time=` + new Date()).then(res => {
            console.log(res)
        })
        return false
    }

    function postMsg() {
        var userName = document.querySelector("#userName").value
        var age = document.querySelector("#age").value
        window.fetch(
            `/springMVC/json/save?time=` + new Date(), {
                headers: {
                    "Content-Type": "application/json;charset=utf-8"
                },
                method: 'post',
                body: JSON.stringify({
                    userName,
                    age
                })
            }).then(async res => {
            console.log(await res.json())
        })


    }
</script>
</body>
</html>
  1. 增 加 处 理 json 代 码 , 注 意 : 用 的 是 @PostMapping , 等
    价:@RequestMapping(method = RequestMethod.POST)
 /*
    *
    * 1.@RequestBody User user在形参指定了RequestBody
    * 2.springmvc就会将提交的json字符串数据填充给指定给javaBean
     * */
    @PostMapping(value = "/json/save")
    @ResponseBody
    public User save(@RequestBody  User user){
        System.out.println("---user---"+user);
        return user;
    }
  1. 增加 JavaBean : User.java

  1. 并完成测试(页面方式), 浏览器 http://localhost:8080/springmvc/json.jsp
  2. 并完成测试(Postman 方式)

12.3 处理 JSON-注意事项和细节

  1. 目标方法正常返回 JSON 需要的数据, 可以是一个对象, 也可以是一个集合
@GetMapping(value = "/json/dogs")
    @ResponseBody
    public List<Dog> dogs() {
        List<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog("大黄狗", "小新的家"));
        dogs.add(new Dog("大黄狗2", "小新2的家"));
        dogs.add(new Dog("大黄狗3", "小新3的家"));
        return dogs;
    }
  1. 前面讲解的是返回一个 Dog 对象->转成 Json 数据格式返回
  2. @ResponseBody 可以直接写在 controller 上,这样对所有方法生效
  3. @ResponseBody + @Controller 可以直接写成 @RestController , 我们看一下源码!
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
}

12.4 HttpMessageConverter<T>

● 基本说明
SpringMVC 处理 JSON-底层实现是依靠 HttpMessageConverter来进行转换的
● 工作机制简图
在这里插入图片描述
● 处理 JSON-底层实现(HttpMessageConverter<T>)

  1. 使用 HttpMessageConverter<T> 将请求信息转化并绑定到处理方法的入参中, 或将响应
    结果转为对应类型的响应信息,Spring 提供了两种途径:
    √ 使用 @RequestBody / @ResponseBody 对目标方法进行标注
    √ 使用 HttpEntity / ResponseEntity 作为目标方法的入参或返回值
  2. 当 控 制 器 处 理 方 法 使 用 到 @RequestBody/@ResponseBody 或
    HttpEntity/ResponseEntity 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹 配 的 HttpMessageConverter, 进 而 根 据 参 数 类 型 或 泛 型 类 型 的 过 滤 得 到 匹 配 的HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错

12.5 文件下载-ResponseEntity</T>

● 说明:
在 SpringMVC 中,通过返回 ResponseEntity<T>的类型,可以实现文件下载的功能

  1. 修改 json.jsp
<hr/>
<h1>下载文件测试</h1>
<a href="<%=request.getContextPath()%>/downFile">点击下载文件</a>
  1. 增加方法
    //响应用户下载文件的请求
    @RequestMapping("/downFile")
    public ResponseEntity<byte[]> downFile(HttpSession session) throws IOException {

        //1.钩建一个ResponseEntity对象1.的http响应头headers2. http响应状态3.下载的文件
        //1.先获取到下载文件的inputStream
        InputStream resourceAsStream = session.getServletContext().getResourceAsStream("/images/aa.webp");
        //2.开辟一个存放文件的byte数组,这里使用byte[]是可以支持二进制数据
        byte[] bytes = new byte[resourceAsStream.available()];
        //3.将下载文件的数据,读入到byte[]
        resourceAsStream.read(bytes);
        //4.创建返回的HttpStatus
        HttpStatus httpStatus = HttpStatus.OK;
        //5.创建返回的httpHeaders
        HttpHeaders httpHeaders = new HttpHeaders();
        //指定返回的数据,客户端应当以附件形式处理
        httpHeaders.add("content-Disposition" , "attachment;filename=2.jpg");

        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, httpHeaders, httpStatus);

        return  responseEntity;

    }

文件下载响应头的设置
content-type 指示响应内容的格式
content-disposition 指示如何处理响应内容。
一般有两种方式:
inline:直接在页面显示
attchment:以附件形式下载

  1. 完成测试
    w

13 SpringMVC 文件上传

13.1 基本介绍

● 基本介绍

  1. Spring MVC 为 文 件 上 传 提 供 了 直 接 的 支 持 , 这 种 支 持 是 通 过 即 插 即 用 的MultipartResolver 实 现 的 。 Spring 用 Jakarta Commons FileUpload 技 术 实 现 了 一 个MultipartResolver 实现类:CommonsMultipartResovler
  2. Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 MultipartResolver
    应用实例-代码实现
    1.引入 springmvc 文件上传需要的 jar 包
    在这里插入图片描述
    2.新增
<h1>文件上传的演示</h1>
<form action="fileUpload" method="post"
enctype="multipart/form-data">
文件介绍:<input type="text" name="introduce"><br>
选择文件:<input type="file" name="file"><br>
<input type="submit" value="上传文件">
</form>
//处理文件上传请求
    @PostMapping("/fileUpload")
    public String fileUpload(@RequestParam(value = "file") MultipartFile file, HttpServletRequest
            request,String introduce) throws IOException {

        //接收到提交的文件名
        String originalFilename = file.getOriginalFilename();
        System.out.println("上传的文件名=="+originalFilename);
        System.out.println("上传的文描述=="+introduce);
        //得到要把上传文件保存到哪个路径[全路径:包括文件名]
        String fileFullPath = request.getServletContext().getRealPath("/images/" + originalFilename);
        //创建文件
        File saveToFile = new File(fileFullPath);
        //上传的文件,转存到saveToFile
        file.transferTo((saveToFile));
        return "success";

    }

3.配 置 文 件 上 传 解 析 器 D:\lxc\javaProject\springMVC\src\applicationContext-mvc.xml

<!--配置文件上传需要的bean-->
    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">

4.完 成 测 试 ( 页 面 方 式 ), 看 文 件 是 否 成 功 上 传
在这里插入图片描述

14 自定义拦截器

14.1 什么是拦截器

● 说明

  1. Spring MVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定
    的功能.
  2. 自定义的拦截器必须实现 HandlerInterceptor 接口
    ● 自定义拦截器的三个方法
  3. preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求request 进行处理。
  4. postHandle():这个方法在目标方法处理完请求后执行
  5. afterCompletion():这个方法在完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

14.2 自定义拦截器执行流程分析图

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

14.3 自定义拦截器应用实例

14.3.1 快速入门
● 应用实例需求
完成一个自定义拦截器,学习一下如何配置拦截器和拦截器的运行流程
● 应用实例-代码实现

  1. 创 建D:\lxc\javaProject\springMVC\src\com\codeSE\web\interceptor\MyInterceptor01.java
package com.codeSE.web.interceptor;

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

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

//配置一个 @Component交给 springmvc管理
@Component
public class MyInterceptor01 implements HandlerInterceptor {
    /**
     *  1. preHandle()在目标方法执行前被执行
     *  2.如果preHandle()返回于false ,不再执行目标方法
     *  3.该方法可以获取到request, response , handler
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor01的preHandle()执行------");
        return true;
    }

    /**
     * 1.在目标方法执行后,会执行postHandle
     * 2. 方法可以获取到目标方法,返回的ModelAndView
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor01的postHandle()执行------");

    }

    /**
     * 1.afterCompletion()在视图渲染后被执行,这里可以进行资源清理工作
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor01的afterCompletion()执行------");

    }
}

  1. D:创 建\lxc\javaProject\springMVC\src\com\codeSE\web\interceptor\FurnHandler.java
package com.codeSE.web.interceptor;

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

@Controller
public class FurnHandler {
    @RequestMapping(value = "/hi")
    public String hi() {
        System.out.println("---FurnHandler--hi()---");
        return "success";

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

    }
}
  1. 在 D:\lxc\javaProject\springMVC\src\applicationContext-mvc.xml 配置拦截器
<!--配置自定义拦截器-->
    <mvc:interceptors>
        <!--第一种配置方式:直接引用对应的myInterceptor01,会拦截所有-->
        <!--<ref bean="myInterceptor01"/>-->
        
        <!--第二种配置方式:指定要拦截的路径-->
        <!--<mvc:interceptor>-->
        <!--    <mvc:mapping path="/hi"/>-->
        <!--    <ref bean="myInterceptor01"/>-->
        <!--</mvc:interceptor>-->
        
        <!--第三种配置方式:使用通配符指定要拦截的路径-->
        <mvc:interceptor>
            <mvc:mapping path="/h*"/>
            <mvc:exclude-mapping path="/hello"/><!--不拦截 "/hello"-->
            <ref bean="myInterceptor01"/>
        </mvc:interceptor>
    </mvc:interceptors>

注意:拦截器配置在spring的xml文件里面,过滤器配置在web.xml文件里

  1. 创interceptor01.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试自定义拦截器</title>
</head>
<body>
<h1>测试自定义拦截器</h1>
<a href="hi">测试自定义拦截器</a><br><br>
<a href="hello">登录</a>
</body>
</html>
  1. 完成测试(页面方式) , 浏览器http://localhost:8080/springmvc/interceptor.jsp
    在这里插入图片描述在这里插入图片描述

14.3.2 注意事项和细节
1、默认配置是都所有的目标方法都进行拦截, 也可以指定拦截目标方法, 比如只是拦截 hi
2、mvc:mapping 支持通配符, 同时指定不对哪些目标方法进行拦截
3、拦截器需要配置才生效,不配置是不生效的.
4、如果 preHandler() 方法返回了 false, 就不会执行目标方法(前提是你的目标方法被拦截了), 程序员可以在这里根据业务需要指定跳转页面.

14.3.3 Debug 执行流程

14.4 多个拦截器

14.4.1 多个拦截器执行流程示意图

在这里插入图片描述

14.4.2 应用实例 1

● 应用实例-代码实现

  1. 创 建D:\lxc\javaProject\springMVC\src\com\codeSE\web\interceptor\MyInterceptor02.java
package com.codeSE.web.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyInterceptor02 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor02的preHandle()执行------");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor02的postHandle()执行------");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor02的afterCompletion()执行------");

    }
}

  1. 在 D:\lxc\javaProject\springMVC\src\applicationContext-mvc.xml 配置拦截器
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor02"/>
</mvc:interceptor>
  1. 完成测试(页面方式), 浏览器 http://localhost:8080/springmvc/interceptor.jsp

14.4.2.2 注意事项和细节

  1. 如果第 1 个拦截器的 preHandle() 返回 false , 后面都不在执行
  2. 如 果 第 2 个 拦 截 器 的 preHandle() 返 回 false , 就 直 接 执 行 第 1 个 拦 截 器 的
    afterCompletion()方法, 如果拦截器更多,规则类似
  3. 说明: 前面说的规则,都是目标方法被拦截的前提

14.4.3 应用实例 2

  1. 需求: 如果用户提交的数据有禁用词(比如 病毒),则,在第 1 个拦截器就返回,不执行目标方法。
  2. 代码实现: 创建D:\lxc\javaProject\springMVC\web\WEB-INF\pages\warning.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>警告</title>
</head>
<body>
<h1>不要乱讲话~</h1>
</body>
</html>
  1. 代 码 实 现 : 修 改MyInterceptor01.java
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("-------第一个自定义拦截器MyInterceptor01的preHandle()执行------");
        String keyword = request.getParameter("keyword");
        if("病毒".equals(keyword)){
            request.getRequestDispatcher("/WEB-INF/pages/warning.jsp").forward(request,response);
            return false;//拒绝
        }
        System.out.println("得到到keyword=="+keyword);
        return true;//放行
    }
  1. 测试

15 异常处理

15.1 异常处理-基本介绍

● 基本介绍

  1. Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
  2. 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
  3. ExceptionHandlerMethodResolver 内 部 若 找 不 到 @ExceptionHandler 注 解 的 话 , 会 找@ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器

15.2 局部异常

15.2.1 应用实例
● 应用实例需求
演示局部异常处理机制
如果不处理异常, 非常的不友好

  1. 创建D:\lxc\javaProject\springMVC\src\com\codeSE\web\exception\MyExceptionHandler.java
package com.codeSE.web.exception;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class MyExceptionHandler {

    /*
    * 1.编写方法,模拟异常,算术异常
      2.如果我们不做异常处理,是由tomcat默认页面显示
    * */
    @RequestMapping("/testException01")
    public String test01(Integer num) {
        int i = 9 / num;

        return "success";

    }

    //局部异常就是直接在这个 Handler中编写即可
    //Exception ex:生成的异常对象,会传递给ex,通过ex可以得到相关的信息,可以写自己的业务逻辑
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String localException(Exception ex, HttpServletRequest request) {
        System.out.println("异常信息是~" + ex.getMessage());
        //如何将异常的信息带到下一个页面.
        request.setAttribute("reason", ex.getMessage());
        return "exception_mes";
    }
}
  1. 创建D:\lxc\javaProject\springMVC\web\exception.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>异常信息</title>
</head>
<body>
<h1>测试异常</h1>
<a href="<%=request.getContextPath()%>/testException01?num=0">点击测试异常</a><br><br>
</body>
</html>
  1. 创建显示异常信息页面 /WEB-INF/pages/exception_mes.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常提示信息</title>
</head>
<body>
<h1>朋友,发生异常,信息如下 :</h1>
${reason }
</body>
</html>
  1. 测试(页面方式) , 浏览器 http://localhost:8080/springmvc/exception.jsp
    在这里插入图片描述
    15.2.2 Debug 处理流程

15.3 全局异常

15.3.1 应用实例
● 应用实例需求
演 示 全 局 异 常 处 理 机 制 , ExceptionHandlerMethodResolver 内 部 若 找 不 到@ExceptionHandler 注解的话,会找 @ControllerAdvice 类的@ExceptionHandler 注解方法,这样就相当于一个全局异常处理器
● 应用实例-代码实现

  1. 创 建D:\lxc\javaProject\springMVC\src\com\codeSE\web\exception\MyGlobalException.java
package com.codeSE.web.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;

/*
 *@ControllerAdvice全局异常就不管是哪个Handler抛出的异常,都可以捕获,@ExceptionHandler({异常类型})
 * */
@ControllerAdvice
public class MyGlobalException {
    @ExceptionHandler({NumberFormatException.class, NullPointerException.class})
    public String globalException(Exception ex, HttpServletRequest request) {
        System.out.println("全局异常处理-" + ex.getMessage());
        request.setAttribute("reason", ex.getMessage());
        return "exception_mes";
    }

}
  1. 修改 MyExceptionHandler.java , 增加方法
@RequestMapping("/testGlobalException")
    public String test02() {
        int i = Integer.parseInt("hello");
        return "success";

    }
  1. 修改 exception.jsp
    在这里插入图片描述

  2. 完成测试(页面方式) 浏览器 http://localhost:8080/springmvc/exception.jsp
    在这里插入图片描述
    15.3.3 异常处理时:局部异常 优先级高于 全局异常

15.4 自定义异常

15.4.1 应用实例
● 应用实例需求
通过@ResponseStatus 注解, 可以自定义异常的说明
● 应用实例-代码实现

  1. 创 建D:\lxc\javaProject\springMVC\src\com\codeSE\web\exception\AgeException.java
package com.codeSE.web.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
//这个是给tomcat默认页面看的
@ResponseStatus(reason = "年龄在0-120之间",value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException{
    public AgeException() {
    }

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

  1. 修改 MyExceptionHandler.java, 增加方法
@RequestMapping("/testException02")
    public String test03() {
        int i = Integer.parseInt("hello");
        throw new AgeException("年龄必须再1-120之间~~~");

    }
  1. 修改 exception.jsp, 增加超链接
    在这里插入图片描述

  2. 并完成测试, 浏览器 http://localhost:8080/springmvc/exception.jsp
    在这里插入图片描述
    在这里插入图片描述

15.5 SimpleMappingExceptionResolver

15.5.1 基本说明
● 基本说明

  1. 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver
  2. 它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
  3. 需要在 ioc 容器中配置
    15.5.2 应用实例
    ● 应用实例-需求
    对数组越界异常进行统一处理,使用 SimpleMappingExceptionResolver
    应用实例-代码实现
  4. 修改 MyExceptionHandler.java , 增加方法test04
//统一的异常
    @RequestMapping(value="/testException03")
    public String test04(){
        int[] arr = new int[]{3,9,10,190};
        //抛出一个数组越界的异常ArrayIndexOut0fBoundsException
        System.out.println(arr[90]);
        return "success";
    }
  1. 配置 D:\lxc\javaProject\springMVC\src\applicationContext-mvc.xml
<!--配置统一处理异常Bean-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="excludedExceptions">
            <props>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
            </props>
        </property>
    </bean>
  1. 创建/WEB-INF/pages/arrEx.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>数组越界异常</title>
</head>
<body>
<h1>
   统一异常: 数组越界异常
</h1>
</body>
</html>
  1. 修改在这里插入图片描述
    exception.jsp

  2. 并完成测试(页面测试), 浏览器 http://localhost:8080/springmvc/exception.jsp

15.5.3 对未知异常进行统一处理
● 应用实例-需求
对未知异常进行统一处理,使用 SimpleMappingExceptionResolver

● 应用实例-代码实现

  1. 修改 MyExceptionHandler.java , 增加方法 test05
//如果发生了没有归类的异常, 可以给出统一提示页面
    @RequestMapping(value="/testException04")
    public String test05(){
        String str = "hello";
        //这里会抛出StringIndexOut0fBoundsException
        char c = str.charAt(10);
        return "success";
    }

  1. 配置 D:\lxc\javaProject\springMVC\src\applicationContext-mvc.xml
 <!--配置统一处理异常Bean-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="excludedExceptions">
            <props>
                <prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
                <prop key="java.lang.Exception">allEx></prop>
            </props>
        </property>
    </bean>
  1. 创建/WEB-INF/pages/otherEx.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>未知异常信息</title>
</head>
<body>
<h1>朋友,系统发生了未知异常~</h1>
</body>
</html>
  1. 修改 exception.jsp , 增加超链接
    在这里插入图片描述
  2. 并完成测试(页面测试), 浏览器 http://localhost:8080/springmvc/exception.jsp
    在这里插入图片描述
    15.5.4 异常处理的优先级梳理
    ● 异常处理的优先级
    局部异常 > 全局异常 > SimpleMappingExceptionResolver > tomcat 默认机制

16 SpingMVC 执行流程-源码剖析

  • Dubug SpringMVC 执行流程

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

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

新的甘特图功能,丰富你的文章

2014-01-07 2014-01-09 2014-01-11 2014-01-13 2014-01-15 2014-01-17 2014-01-19 2014-01-21 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 [这儿][2],

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 [这儿][3],

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦境之冢

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值