SSM-SpringMVC

SpringMVC

SSM:MyBatis + Spring + SpringMVC

Spring:IOC(控制反转)、AOP(面向切面编程)

SpringMVC:SpringMVC 的执行流程

SpringMVC:SSM 框架整合

1、简述

1.1 什么是 SpringMVC

Spring MVC 是 Spring Framework 的一部分,是基于 Java 实现 MVC 的轻量级 Web 框架

1.2 为什么学习 SpringMVC

因为 SpringMVC “简单好学”,便捷,能与 Spring 无缝集成(SpringIOC、AOP),使用约定大于配置,能够进行简单的 junit 测试,支持 Restful 风格,异常处理,本地化,国际化,数据验证,类型转换,拦截器,等等

最重要的是使用的公司多,用的人也多

1.3 SpringMVC 的特点

  • 简单好学
  • 高效(基于请求相应的 MVC 框架)
  • 与 Spring 无缝集成,兼容性好
  • 约定大于配置
  • 功能强大(Restful、数据验证、格式化、本地化、类型转换、等等)
  • 简洁灵活

2、中心控制器

  • Spring 的 web 框架围绕 DispatcherServlet 设计。DispatcherServlet 的作用是将请求分发到不同的处理器。从 Spring 2.5 开始,使用 Java 5 或者以上版本的用户可以采用基于注解的 controller 声明方式。

  • Spring MVC 框架像许多其他 MVC 框架一样, 以请求为驱动 , 围绕一个中心 Servlet 分派请求及提供其他功能DispatcherServlet 是一个实际的 Servlet (它继承自 HttpServlet 基类)

在这里插入图片描述

SpringMVC的原理如下图所示:

  • 当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

3、SpringMVC 原理

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

在这里插入图片描述

SpringMVC 执行原理

在这里插入图片描述

实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现

  1. DispatcherServlet 表示前置控制器,是整个 SpringMVC 的控制中心。用户发出请求,DispatcherServlet 接收请求并拦截请求。

    • 我们假设请求的url为 : http://localhost:8080/SpringMVC/hello

    • 如上 url 拆分成三部分:

      • http://localhost:8080 服务器域名
      • SpringMVC 部署在服务器上的 web 站点
      • hello 表示控制器
    • 通过分析,如上 url 表示为:请求位于服务器 localhost:8080上的Spring MVC站点的 hello 控制器。

  2. HandlerMapping 为处理器映射。DispatcherServlet 调用 HandlerMapping,HandlerMapping 根据请求 url 查找 Handler。

  3. HandlerExecution 表示具体的Handler,其主要作用是根据 url 查找控制器,如上 url 被查找控制器为:hello。

  4. HandlerExecution 将解析后的信息传递给 DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter 表示处理器适配器,其按照特定的规则去执行 Handler。

  6. Handler 让具体的 Controller 执行。

  7. Controller 将具体的执行信息返回给 HandlerAdapter,如 ModelAndView。

  8. HandlerAdapter 将视图逻辑名或模型传递给 DispatcherServlet。

  9. DispatcherServlet 调用视图解析器(ViewResolver)来解析 HandlerAdapter 传递的逻辑视图名。

  10. 视图解析器将解析的逻辑视图名传给 DispatcherServlet。

  11. DispatcherServlet 根据视图解析器解析的视图结果,调用具体的视图。

  12. 最终视图呈现给用户。

4、HelloSpringMVC

4.1 配置版

步骤:

  1. 新建一个 项目,添加 web 支持

  2. 导入 SpringMVC 的依赖

  3. 创建 SpringMVC 的配置文件(springmvc-service.xml)

  4. 配置 web.xml,注册 DispatcherServlet

    <!--配置DispatchServlet:这个是SpringMVC的核心:请求分发器,前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatchServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-service.xml</param-value>
        </init-param>
        <!--启动级别-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--在Spring中,   /   /*
    /:只匹配所有的请求,不会去匹配jsp
    /*:匹配所有请求,包括jsp页面
    -->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
  5. 继续配置 SpringMVC 的配置文件(springmvc-servlet.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    </beans>
    
    • 添加 处理映射器

      <!--处理器映射器-->
      <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
      
    • 添加 处理器适配器

      <!--处理器适配器-->
      <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
      
    • 添加 视图解析器

      <!--视图解析器-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
          <!--前缀-->
          <property name="prefix" value="/WEB-INF/jsp/"/>
          <!--后缀-->
          <property name="suffix" value=".jsp"/>
      </bean>
      
  6. 编写操作业务的 Controller

    • 要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图
    public class HelloController implements Controller {
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //业务代码
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","HelloSpringMVC!");
            //视图解释
            mv.setViewName("hello");//需要拼接的视图名称,参考视图解析器的代码实际地址就是(/WEB-INF/jsp/hello.jsp)
            return mv;
        }
    }
    
  7. 注册 bean(将 HelloController 这个类交给 Spring 容器【IOC】)

    <!--BeanNameUrlHandlerMapping:匹配的id(Handler)-->
    <!--Handler-->
    <bean id="/hello" class="com.aze.controller.HelloController"/>
    
  8. 编写跳转页面,显示 ModelandView 存放的数据,以及我们编写的页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    

大多数都会遇到的问题!

即使你的代码正确,但是却一直报出 404 错误!

  • 问题原因可能就在于 IDEA 项目发布中,缺少了 springMVC 相关的 jar 包

  • 解决办法:

    1. 在这里插入图片描述

    2. 在这里插入图片描述

    3. 在 WEB-INF 下创建一个 lib 目录,在这个目录下存放 相关的 jar 包

      在这里插入图片描述

      在这里插入图片描述

    4. 将所有的 包 导入,点击 ok

      在这里插入图片描述

4.2 注解版(*)

步骤:

  1. 新建一个项目,添加 web 支持

  2. 解决 Maven 可能存在资源过滤的问题

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    
  3. 在 pom.xml 文件引入相关的依赖:主要有 Spring 框架核心库、Spring MVC、servlet , JSTL 等

  4. 配置 web.xml

    <!--这是使用注解开发时固定的配置-->
    <!--注册servlet-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--初始化Spring配置文件的位置-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动顺序,数字越小,启动越早-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--所有的请求都会被SpringMVC拦截-->
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
  5. 编写 springmvc-servlet.xml 配置文件

    • 配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能
    • 在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问
      • 让IOC的注解生效(context:component-scan base-package="")
      • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …(mvc:default-servlet-handler)
      • MVC的注解驱动(mvc:annotation-driven)
      • 配置视图解析器(开发中,程序员只需要配置这一步,其他的都是死的,CV 就可以)
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           https://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!--这也是固定配置,需要修改的只有需要注入IOC的包-->
        <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
        <context:component-scan base-package="com.aze.controller"/>
        <!-- 让Spring MVC不处理静态资源 -->
        <mvc:default-servlet-handler />
        <!--
        支持mvc注解驱动
            在spring中一般采用@RequestMapping注解来完成映射关系
            要想使@RequestMapping注解生效
            必须向上下文中注册DefaultAnnotationHandlerMapping
            和一个AnnotationMethodHandlerAdapter实例
            这两个实例分别在类级别和方法级别处理。
            而annotation-driven配置帮助我们自动完成上述两个实例的注入。
         -->
        <mvc:annotation-driven />
    
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 前缀 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 后缀 -->
            <property name="suffix" value=".jsp" />
        </bean>
    </beans>
    
  6. 编写一个 Java 控制类 HelloController.java

    • @Controller 是为了让 Spring IOC 容器初始化时自动扫描到;
    • @RequestMapping 是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是 /hello/hi;
    • 方法中声明 Model 类型的参数是为了把 Action 中的数据带到视图中;
    • 方法返回的结果是视图的名称 hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
    @Controller
    @RequestMapping("/hello")
    public class HelloController {
    
        //类上注册了@RequestMapping方法就自动下移,实际地址:项目名/hello/hi
        @RequestMapping("/hi")
        public String sayHello(Model model){
            //向模型中添加属性msg与值,可以在JSP页面中取出并渲染
            model.addAttribute("msg","hello,SpringMVCAnnotatiom!");
            //web-inf/jsp/hello.jsp
            return "hello";
        }
    }
    
  7. 编写视图层 hello.jsp

    • 视图可以直接取出并展示从 Controller 带回的信息;

    • 可以通过 EL 表示取出 Model 中存放的值,或者对象

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    

4.3 结论

实现步骤:

  1. 新建一个 web 项目
  2. 导入相关 jar 包
  3. 编写 web.xml , 注册 DispatcherServlet
  4. 编写 springmvc 配置文件
  5. 接下来就是去创建对应的控制类 , controller
  6. 最后完善前端视图和 controller 之间的对应
  7. 测试运行调试.

使用springMVC必须配置的三大件:

  • 处理器映射器
  • 处理器适配器
  • 视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置。

5、注解

5.1 Controller

Controller 控制器

  • 负责提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。

  • 负责解析用户的请求并将其转换为一个模型。

在 Spring MVC 中一个控制器类可以包含多个方法

在 Spring MVC 中,对于 Controller 的配置方式有很多种

5.1.1 实现 Controller 接口

Controller是一个接口

//实现该接口的类获得控制器功能
public interface Controller {
    //处理请求且返回一个模型与视图对象
    ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}

步骤:(上面 HelloSpringMVC 的配置版就是 实现 Controller 接口)

  1. 新建一个 web 项目
  2. 导入 SpringMVC 的依赖
  3. 创建 SpringMVC 的配置文件(springmvc-service.xml)
    • 删掉 HelloController
    • 只留下 视图解析器!
  4. 配置 web.xml,注册 DispatcherServlet
  5. 编写一个 Controller 类
  6. 编写完毕后,去 Spring 配置文件中注册请求的 bean
    • name 对应请求路径
    • class 对应处理请求的类
  7. 编写前端 hello.jsp
    • 注意在 WEB-INF/jsp 目录下编写,对应我们的视图解析器
  8. 配置 Tomcat 运行测试

注意:

  • 实现接口 Controller 定义控制器是较老的办法
  • 缺点
    • 一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller
    • 定义的方式比较麻烦;
5.1.2 注解 @Controller
  • @Controller 注解类型用于声明 Spring 类的实例是一个控制器(
    • @Controller:用于controller
    • @Component:用于组件
    • @Repository:用于 dao
    • @Service:用于 service
  • Spring 可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证 Spring 能找到你的控制器,需要在配置文件中声明组件扫描。

步骤:(4.2 注解版就是用注解 @Controller)

  1. 新建一个 web 项目
  2. 导入 SpringMVC 的依赖
  3. 创建 SpringMVC 的配置文件
  4. 配置 web.xml,注册 DispatcherServlet
  5. 编写一个 Controller 类
    • 使用注解()
  6. 编写前端 hello.jsp
  7. 配置 Tomcat 运行测试

5.2 RequestMapping

@RequestMapping

  • @RequestMapping 注解用于映射 url 到控制器类或一个特定的处理程序方法。
  • 可用于方法
    • 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
    • 用于方法上,方法的响应请求是该地址

建议:类上不要使用此注解

6、RestFul 风格

6.1 什么是 RestFul

Restful 就是一个资源定位及资源操作的风格。

  • 不是标准也不是协议,只是一种风格
  • 基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

6.2 RestFul 功能

资源:互联网所有的事物都可以被抽象为资源

  • 资源操作:使用 POST、DELETE、PUT、GET,使用不同方法对资源进行操作。

  • 分别对应 添加、 删除、修改、查询。

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

​ http://127.0.0.1/item/queryItem.action?id=1 查询,GET

​ http://127.0.0.1/item/saveItem.action 新增,POST

​ http://127.0.0.1/item/updateItem.action 更新,POST

​ http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

​ http://127.0.0.1/item/1 查询,GET

​ http://127.0.0.1/item 新增,POST

​ http://127.0.0.1/item 更新,PUT

​ http://127.0.0.1/item/1 删除,DELETE

6.3 测试 RestFul 风格

6.3.1 使用 @RequestMapping

步骤:

  1. 新建一个 Controller 类,用于测试 RestFul 风格

  2. 在 Spring MVC 中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

    @Controller
    public class RestFulTest {
    
        //映射访问路径
        @RequestMapping("/commit/{a}/{b}")
        public String index(@PathVariable int a, @PathVariable int b, Model model){
            int result = a + b;
            //Spring MVC会自动实例化一个Model对象用于向视图中传值
            model.addAttribute("msg", "结果:"+result);
            //返回视图位置
            return "hello";
        }
    
    }
    
  3. 测试

    在这里插入图片描述

  4. 修改下对应的参数类型,再次测试

    // 将 int b,改为 String b
    @PathVariable int a, @PathVariable String b
        
    // 对应的 result 也需要改为 String
    String result = a+b;
    

    在这里插入图片描述

6.3.2 使用注解变体

使用method属性指定请求类型:

  • 用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如 GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE 等
@RequestMapping("/commit/{a}/{b}",method = {RequestMethod.GET})

问题?

  • 这样写,代码量就会变多,这时候,注解变体他来了!

注解变体:(方法级别)

  • @GetMapping:GET 类型
  • @PostMapping:POST 类型
  • @PutMapping:PUT 类型
  • @DeleteMapping:DELETE 类型
  • @PatchMapping:PATCH 类型
// 使用注解变体
@GetMapping("/commit/{a}/{b}")
// 等价于 @RequestMapping(value = "/commit/{a}/{b}",method = {RequestMethod.GET})

6.4 小结

Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。

所有的地址栏请求默认都会是 HTTP GET 类型的。(注意!!!)

@GetMapping 是一个组合注解,平时使用的会比较多!

使用浏览器地址栏进行访问默认是 Get 请求,会报错 405

  • 解决办法:将 POST 修改为 GET 就好了

7、转发与重定向

7.1 ModelAndView

设置 ModelAndView 对象 , 根据 view 的名称 , 和视图解析器跳到指定的页面 .

  • 页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
    
  • controller 类

    public class ControllerTest1 implements Controller {
    
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            // 返回一个模型视图对象
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","ControllerTest1");
            mv.setViewName("test");
            return mv;
        }
    }
    

7.2 ServletAPI

这是 JavaWeb 中学习到的用法:

  • 通过设置 ServletAPI,不需要视图解析器 .

    1、通过 HttpServletResponse 进行输出

    @RequestMapping("/test1")
    public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
        rsp.getWriter().println("Hello,Spring BY servlet API");
    }
    

    2、通过 HttpServletResponse 实现重定向(重定向)

    @RequestMapping("test2")
    public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
        rsp.sendRedirect("/index.jsp");
    }
    

    3、通过 HttpServletResponse 实现转发(请求转发)

    @RequestMapping("/test3")
    public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
        // 转发
        req.setAttribute("msg","/result/t3");
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
    }
    

7.3 SpringMVC

7.3.1 无视图解析器
@RequestMapping("/rsm/t1")
public String test1(){
    // 默认就是转发
    return "/index.jsp";
}

@RequestMapping("/rsm/t2")
public String test2(){
    // 也可以显示定义转发
    return "forward:/index.jsp";
}

@RequestMapping("/rsm/t3")
public String test3(){
    // 重定向:需要显示定义
    return "redirect:/index.jsp";
}
7.3.2 有视图解析器

重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方 , 所以注意路径问题.

@RequestMapping("/rsm2/t1")
public String test1(){
    // 转发:默认就是转发
    return "test";
}

@RequestMapping("/rsm2/t2")
public String test2(){
    // 重定向:需要显示定义
    return "redirect:/index.jsp";
    // return "redirect:hello.do"; //hello.do为另一个请求/
}

8、数据处理

8.1 处理提交数据

  1. 提交的域名称和处理方法的参数名一致

    @RequestMapping("/hello")
    public String hello(String name){
        System.out.println(name);
        return "hello";
    }
    
  2. 提交的域名称和处理方法的参数名不一致,就需要使用注解 @RequestParam 来解决

    // @RequestParam("username") : username提交的域的名称 .
    @RequestMapping("/hello")
    public String hello(@RequestParam("username") String name){
        System.out.println(name);
        return "hello";
    }
    
  3. 提交的是一个对象

    • 实体类
    // 这里使用的是 Lombok
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private int age;
    }
    
    • Controller 类
    @RequestMapping("/user")
    public String user(User user){
        System.out.println(user);
        return "hello";
    }
    

8.2 数据显示到前端

  1. 通过 ModelAndView

    public class ControllerTest implements Controller {
    
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            // 返回一个模型视图对象
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg","ControllerTest1");
            mv.setViewName("test");
            return mv;
        }
    }
    
  2. 通过ModelMap

    @RequestMapping("/hello")
    public String hello(@RequestParam("username") String name, ModelMap model){
        //封装要显示到视图中的数据
        //相当于req.setAttribute("name",name);
        model.addAttribute("name",name);
        System.out.println(name);
        return "hello";
    }
    
  3. 通过 Model

    @RequestMapping("/ct2/hello")
    public String hello(@RequestParam("username") String name, Model model){
        //封装要显示到视图中的数据
        //相当于req.setAttribute("name",name);
        model.addAttribute("msg",name);
        System.out.println(name);
        return "test";
    }
    

区别:

  • Model:只有寥寥几个方法只适合用于储存数据,简化了新手对于 Model 对象的操作和理解

  • ModelMap:继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

  • ModelAndView:可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

9、中文乱码问题

解决办法:

  1. 自我编写一个过滤器,再注册到 web.xml 文件中

    public class CharacterEncodingFilter implements Filter {
    
        // 初始化:web 服务器启动,就已经初始化了,随时等待过滤对象的出现
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            servletRequest.setCharacterEncoding("utf-8");
            servletResponse.setCharacterEncoding("utf-8");
            servletResponse.setContentType("text/html;charset=utf-8");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        // 销毁:web 服务器关闭时,过滤器会销毁
        public void destroy() {
        }
    }
    
    
  2. 使用 SpringMVC 自带的过滤器

    • 导入相关依赖后,直接配置 web.xml 即可
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  3. 使用大佬配置的过滤器

    package com.kuang.filter;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.Map;
    
    /**
    * 解决get和post请求 全部乱码的过滤器
    */
    public class GenericEncodingFilter implements Filter {
    
        @Override
        public void destroy() {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //处理response的字符编码
            HttpServletResponse myResponse=(HttpServletResponse) response;
            myResponse.setContentType("text/html;charset=UTF-8");
    
            // 转型为与协议相关对象
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            // 对request包装增强
            HttpServletRequest myrequest = new MyRequest(httpServletRequest);
            chain.doFilter(myrequest, response);
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
    }
    
    //自定义request对象,HttpServletRequest的包装类
    class MyRequest extends HttpServletRequestWrapper {
    
        private HttpServletRequest request;
        //是否编码的标记
        private boolean hasEncode;
        //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
        public MyRequest(HttpServletRequest request) {
            super(request);// super必须写
            this.request = request;
        }
    
        // 对需要增强方法 进行覆盖
        @Override
        public Map getParameterMap() {
            // 先获得请求方式
            String method = request.getMethod();
            if (method.equalsIgnoreCase("post")) {
                // post请求
                try {
                    // 处理post乱码
                    request.setCharacterEncoding("utf-8");
                    return request.getParameterMap();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            } else if (method.equalsIgnoreCase("get")) {
                // get请求
                Map<String, String[]> parameterMap = request.getParameterMap();
                if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                    for (String parameterName : parameterMap.keySet()) {
                        String[] values = parameterMap.get(parameterName);
                        if (values != null) {
                            for (int i = 0; i < values.length; i++) {
                                try {
                                    // 处理get乱码
                                    values[i] = new String(values[i]
                                                           .getBytes("ISO-8859-1"), "utf-8");
                                } catch (UnsupportedEncodingException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                    hasEncode = true;
                }
                return parameterMap;
            }
            return super.getParameterMap();
        }
    
        //取一个值
        @Override
        public String getParameter(String name) {
            Map<String, String[]> parameterMap = getParameterMap();
            String[] values = parameterMap.get(name);
            if (values == null) {
                return null;
            }
            return values[0]; // 取回参数的第一个值
        }
    
        //取所有值
        @Override
        public String[] getParameterValues(String name) {
            Map<String, String[]> parameterMap = getParameterMap();
            String[] values = parameterMap.get(name);
            return values;
        }
    }
    

以上都不行可能是你的 Tomcat 没有配置好!去修改 tomcat 配置文件 :设置编码

<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

10、Json

  • JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛。
  • 采用完全独立于编程语言的文本格式来存储和表示数据。
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。(前后端分离)
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

10.1 JSON 与 JavaScript

在 JavaScript 语言中,一切都是对象。因此,任何 JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:

  • 对象表示为键值对,数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

JSON 键值对是用来保存 JavaScript 对象的一种方式,和 JavaScript 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 “” 包裹,使用冒号 : 分隔,然后紧接着值( {“键” : “值”} )

JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。

JSON 和 JavaScript 对象互转:

  • 要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法

    var obj = JSON.parse('{"a": "Hello", "b": "World"}');
    
  • 要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法

    var json = JSON.stringify({a: 'Hello', b: 'World'});
    //结果是 '{"a": "Hello", "b": "World"}'
    

10.2 Controller 返回 JSON 数据

  • Jackson 应该是目前比较好的 json 解析工具了

  • 当然工具不止这一个,比如还有阿里巴巴的 fastjson 等等。

  • 这里使用的是 Jackson,使用它需要导入它的 jar 包;

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.0</version>
    </dependency>
    
  • web.xml 配置 SpringMVC 需要的配置

    • servlet 注册 SpringMVC
    • filter 注册字符过滤器
    <?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>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!-- 启动顺序,数字越小,启动越早 -->
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!--所有请求都会被springmvc拦截 -->
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>encoding</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>encoding</filter-name>
            <url-pattern>/</url-pattern>
        </filter-mapping>
    
    </web-app>
    
  • 编写 springmvc-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               https://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/mvc
                               https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
        <context:component-scan base-package="com.aze.controller"/>
    
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 前缀 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 后缀 -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>
    
  • 编写一个实体类 User,去测试

    • 需要导入 lombok 的 jar 包
    //需要导入lombok
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        private String name;
        private int age;
        private String sex;
    
    }
    
  • 编写 UserController

    • @ResponseBody:将 java 对象转化为 json
    • ObjectMapper:创建一个 jackson 的对象映射器,用来解析数据
    @Controller
    public class UserController {
    
        @RequestMapping("/json1")
        @ResponseBody
        public String json1() throws JsonProcessingException {
            //创建一个jackson的对象映射器,用来解析数据
            ObjectMapper mapper = new ObjectMapper();
            //创建一个对象
            User user = new User("秦疆1号", 3, "男");
            //将我们的对象解析成为json格式
            String str = mapper.writeValueAsString(user);
            //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
            return str;
        }
    
    }
    
  • 配置 Tomcat

  • 配置 Artifacts


问题:乱码问题

在这里插入图片描述

解决办法:通过 @RequestMaping 的 produces 属性来实现

// produces:指定响应体返回类型和编码
@RequestMapping(value = "/json1",produces = "application/json;charset=utf-8")

10.3 Controller 返回 JSON 数据代码优化

10.3.1 将乱码问题统一解决

上面解决乱码的方法在项目请求多的情况下就不适用了

就可以使用 Spring 配置统一指定返回类型和编码,等等

  • 在 spring-mvc 的配置文件上添加一段消息

    • StringHttpMessageConverter 转换配置
    <!--统一的解决乱码问题-->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    
10.3.2 统一解决返回为 json 字符串问题

如果类中有所有的方法都需要返回 json 字符串,那么所有的方法都需要标识注释 @ResponseBody,这将会特别麻烦

  • 所以直接在类上使用 @RestController 来标注这个类的所有方法返回的都是 json 字符串
    • 这样就不用再每个方法上写 @ResponseBody
    • 前后端分离开发中,一般都会使用 @RestController ,十分便捷

10.4 测试集合的输出

在 UserController 中增加一个方法来进行测试

@RequestMapping("/json2")
public String json2() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    List<User> userList = new ArrayList<User>();
    userList.add(new User("大牛逼1",18,"男"));
    userList.add(new User("大牛逼2",18,"男"));
    userList.add(new User("大牛逼3",18,"男"));
    userList.add(new User("大牛逼4",18,"男"));
    userList.add(new User("大牛逼5",18,"男"));

    return mapper.writeValueAsString(userList);
}

10.5 测试时间对象输出

在 UserController 中增加一个方法来进行测试

@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    Date date = new Date();
    // 默认的是日期格式,但是 Jackson 默认是会把时间转成 timestamps(时间戳)形式
    return mapper.writeValueAsString(date);
}

默认的是日期格式,但是 Jackson 默认是会把时间转成 timestamps(时间戳)形式:

  • 取消 timestamps(时间戳)形式,自定义时间格式

    @RequestMapping("/json4")
    public String json4() throws JsonProcessingException {
    
        ObjectMapper mapper = new ObjectMapper();
    
        //不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //自定义日期格式对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //指定日期格式
        mapper.setDateFormat(sdf);
    
        Date date = new Date();
    
        return mapper.writeValueAsString(date);
    }
    

10.6 将重复代码抽取为工具类

如果要经常使用的话,这样是比较麻烦的,我们可以将这些代码封装到一个工具类中

public class JsonUtils {
    public static String getJson(Object object){
        return getJson(object,"yyyy-MM-dd HH:mm:ss");
    }

    public static String getJson(Object object,String dateFormat){
        ObjectMapper mapper = new ObjectMapper();
        //不使用时间差的方式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //自定义日期格式对象
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        //指定日期格式
        mapper.setDateFormat(sdf);
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

}

使用工具类后的代码量

@RequestMapping("/json5")
public String json5() throws JsonProcessingException {
    return JsonUtils.getJson(new Date());
}

10.7 FastJson

fastjson.jar 是阿里开发的一款专门用于 Java 开发的包,可以方便的实现多种转换,最后的实现结果都是一样的。

  • 实现 json 对象与 JavaBean 对象的转换
  • 实现 JavaBean 对象与 json 字符串的转换
  • 实现 json 对象与 json 字符串的转换。实现 son的 转换方法很多

fastjson 的 pom 依赖:

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.78</version>
</dependency>

JSONObject 代表 json 对象

  • JSONObject 实现了 Map 接口, 猜想 JSONObject 底层操作是由 Map 实现的。
  • JSONObject 对应 json对 象,通过各种形式的 get() 方法可以获取 json 对象中的数据,也可利用诸如 size(),isEmpty() 等方法获取"键:值"对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。

JSONArray 代表 json 对象数组

  • 内部是有 List 接口中的方法来完成操作的。

JSON 代表 JSONObject 和 JSONArray 的转化

  • JSON 类源码分析与使用
  • 仔细观察这些方法,主要是实现 json 对象,json 对象数组,javabean 对象,json 字符串之间的相互转化。

代码测试:

@RequestMapping("/json6")
public String json6(){
    //创建一个对象
    User user1 = new User("1号", 3, "男");
    User user2 = new User("2号", 3, "男");
    User user3 = new User("3号", 3, "男");
    User user4 = new User("4号", 3, "男");
    List<User> userList = new ArrayList<User>();
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    userList.add(user4);

    System.out.println("*******Java对象 转 JSON字符串*******");
    String str1 = JSON.toJSONString(userList);
    System.out.println("JSON.toJSONString(list)==>"+str1);
    String str2 = JSON.toJSONString(user1);
    System.out.println("JSON.toJSONString(user1)==>"+str2);

    System.out.println("\n****** JSON字符串 转 Java对象*******");
    User jp_user1=JSON.parseObject(str2,User.class);
    System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);

    System.out.println("\n****** Java对象 转 JSON对象 ******");
    JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
    System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));

    System.out.println("\n****** JSON对象 转 Java对象 ******");
    User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
    System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
    
    return JSON.toJSONString(userList);
}

11、Ajax

11.1 什么是 Ajax

  • AJAX(Asynchronous JavaScript And XML):异步的 JavaScript 和 XML。
  • AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
  • Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。
  • 在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest 能够自动帮你完成搜索单词。
  • Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
  • 就和国内百度的搜索框一样!
  • 传统的网页(即不用 ajax 技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。
  • 使用 ajax 技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。
  • 使用 Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的 Web 用户界面。

11.2 Ajax 原理

在这里插入图片描述

11.3 伪造 Ajax

  1. 新建一个项目,导入 web 支持

  2. 编写一个 前端页面,使用 iframe 进行测试

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script type="text/javascript">
            function x() {
                var url =  document.getElementById('url').value;
                document.getElementById('iframePosition').src = url;
            }
        </script>
    </head>
    <body>
    
    <div>
        <p>请输入地址:</p>
        <p>
            <input id="url" type="text" placeholder="网页地址"/>
            <input type="button" value="前往" onclick="x()">
        </p>
    </div>
    
    <div>
        <iframe id="iframePosition" style="width: 100%;height: 666px"></iframe>
    </div>
    
    </body>
    </html>
    
  3. 测试

11.4 jQuery 操作 ajax

  • 使用jquery提供的,方便,有兴趣的可以自己去了解下 JS 原生 XMLHttpRequest !
    • Ajax 的核心是 XMLHttpRequest 对象(XHR)。XHR 为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。
  • jQuery 提供多个与 AJAX 有关的方法。
  • 通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中。
  • jQuery 不是生产者,而是大自然搬运工。
  • jQuery Ajax本质就是 XMLHttpRequest,对他进行了封装,方便调用!
jQuery.ajax(...)
      部分参数:
            url:请求地址
            type:请求方式,GETPOST1.9.0之后用method)
        headers:请求头
            data:要发送的数据
    contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
          async:是否异步
        timeout:设置请求超时时间(毫秒)
      beforeSend:发送请求前执行的函数(全局)
        complete:完成之后执行的回调函数(全局)
        success:成功之后执行的回调函数(全局)
          error:失败之后执行的回调函数(全局)
        accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
        dataType:将服务器端返回的数据转换成指定类型
          "xml": 将服务器端返回的内容转换成xml格式
          "text": 将服务器端返回的内容转换成普通文本格式
          "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
        "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
          "json": 将服务器端返回的内容转换成相应的JavaScript对象
        "jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数

11.5 原始的 HttpServletResponse 实现

  1. 配置 web.xml

    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  2. springmvc 配置文件(applicationContext.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
        <context:component-scan base-package="com.aze.controller"/>
        <!--过滤静态资源-->
        <mvc:default-servlet-handler />
        <mvc:annotation-driven />
    
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 前缀 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 后缀 -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>
    
  3. 静态资源过滤和注解驱动配置(我的父工程已经配置了,所以这就不配了)

  4. 编写 Controller 类

    @RestController
    public class UserController {
    
        @RequestMapping("/a")
        public void ajax(String name, HttpServletResponse response) throws IOException {
            if ("admin".equals(name)){
                response.getWriter().write("true");
            }else {
                response.getWriter().write("false");
            }
        }
    
    }
    
  5. 导入 jQuery

  6. 编写前端页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>ajaxTest</title>
        <script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
        <script>
          function a1(){
            $.post({
              url:"${pageContext.request.contextPath}/a1",
              data:{'name':$("#txtName").val()},
              success:function (data,status) {
                alert(data);
                alert(status);
              }
            });
          }
        </script>
      </head>
      <body>
    
      <%--onblur:失去焦点触发事件--%>
      用户名:<input type="text" id="txtName" οnblur="a1()"/>
    
    
      </body>
    </html>
    
  7. 测试

11.6 SpringMVC 实现异步加载数据

  1. 编写 Controller 类

    @RequestMapping("/a2")
    public List<User> ajax2(){
        List<User> userList = new ArrayList<User>();
        userList.add(new User("大牛",10,"男"));
        userList.add(new User("中牛",11,"女"));
        userList.add(new User("小牛",12,"男"));
        return userList;
    }
    
  2. 在 index 中增加内容

    <div>
      <input id="btn" type="button" value="取得信息">
      <table width="100%" align="center">
        <tr>
          <td>姓名</td>
          <td>年龄</td>
          <td>性别</td>
        </tr>
        <tbody id="content">
      </table>
    </div>
    
    $(function () {
        $("#btn").click(function () {
            $.post("${pageContext.request.contextPath}/a2",function (data) {
                console.log(data)
                var html="";
                for (let i = 0; i < data.length ; i++) {
                    html+= "<tr>" +
                        "<td>" + data[i].name + "</td>" +
                        "<td>" + data[i].age + "</td>" +
                        "<td>" + data[i].sex + "</td>" +
                        "</tr>"
                }
                $("#content").html(html);
            });
        })
    })
    
  3. 测试

11.7 注册提示效果

  1. 编写 Controller 类

    @RequestMapping("/a3")
    public String ajax3(String name,String oldPwd,String newPwd, String newPwd2){
        String msg = "";
        if (name != null){
            if ("admin".equals(name)){
                msg = "ok";
            }else {
                msg = "用户名不存在";
            }
        }
        if (oldPwd != null){
            if ("123456".equals(oldPwd)){
                msg = "ok";
            }else {
                msg = "旧密码有误";
            }
        }
        return msg;
    }
    
  2. 编写前端

    <div>
      <p>
        用户名:<input type="text" id="name" οnblur="a3_1()">
        <span id="userInfo"></span>
      </p>
      <p>
        旧密码:<input type="password" id="oldPwd" οnblur="a3_2()">
        <span id="oldPwdInfo"></span>
      </p>
    </div>
    
    function a3_1() {
      $.post({
        url: "${pageContext.request.contextPath}/a3",
        data: {"name":$('#name').val()},
        success:function (data) {
          if (data.toString() === 'ok'){
            $('#userInfo').css("color","green");
          }else {
            $('#userInfo').css("color","red");
          }
          $('#userInfo').html(data)
        }
      })
    }
    function a3_2() {
      $.post({
        url: "${pageContext.request.contextPath}/a3",
        data: {"oldPwd":$('#oldPwd').val()},
        success:function (data) {
          if (data.toString() === 'ok'){
            $('#oldPwdInfo').css("color","green");
          }else {
            $('#oldPwdInfo').css("color","red");
          }
          $('#oldPwdInfo').html(data)
        }
      })
    }
    
  3. 解决乱码问题(applicationContext.xml)

    <!--统一的解决乱码问题-->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    
  4. 测试

12、拦截器

12.1 什么是拦截器

SpringMVC 的处理器、拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

**过滤器与拦截器的区别:**拦截器是AOP思想的具体应用。

  • 过滤器

    • servlet 规范中的一部分,任何 java web工程都可以使用
    • 在 url-pattern 中配置了/*之后,可以对所有要访问的资源进行拦截
  • 拦截器

    • 拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能使用
    • 拦截器只会拦截访问的控制器方法, 如果访问的是 jsp、html、css、image、js 是不会进行拦截的

12.2 自定义拦截器

想要自定义拦截器,必须实现 HandlerInterceptor 接口。

  1. 新建一个项目,添加 web 支持

  2. 配置 web.xml 和 applicationContext.xml

  3. 编写一个拦截器

    public class MyInterceptor implements HandlerInterceptor {
        //在请求处理的方法之前执行
        //如果返回true执行下一个拦截器
        //如果返回false就不执行下一个拦截器
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
            System.out.println("------------处理前------------");
            return true;
        }
    
        //在请求处理方法执行之后执行
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
            System.out.println("------------处理后------------");
        }
    
        //在dispatcherServlet处理后执行,做清理工作.
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            System.out.println("------------清理------------");
        }
    }
    
  4. 在 springmvc 配置文件中配置拦截器

    <!--关于拦截器的配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--/** 包括路径及其子路径-->
            <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
            <!--/admin/** 拦截的是/admin/下的所有-->
            <mvc:mapping path="/**"/>
            <!--bean配置的就是拦截器-->
            <bean class="com.aze.config.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  5. 编写 Controller

    @RestController
    public class TestController {
    
        @RequestMapping("/t1")
        public String test(){
            System.out.println("控制器方法执行!");
            return "index";
        }
    
    }
    
  6. 编写前端页面

    <a href="${pageContext.request.contextPath}/t1">测试</a>
    
  7. 测试

12.3 验证用户是否登录(认证用户)

测试思路

  1. 有一个登陆页面,需要写一个 controller 访问页面。
  2. 登陆页面有一提交表单的动作。需要在 controller 中处理。判断用户名密码是否正确。如果正确,向 session 中写入用户信息。返回登陆成功。
  3. 拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面

测试步骤:

  1. 编写登录页面(login.jsp)

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
        <head>
            <title>Title</title>
        </head>
    
        <h1>登录页面</h1>
        <hr>
    
        <body>
            <form action="${pageContext.request.contextPath}/user/login" method="post">
                用户名:<input type="text" name="username" required> <br>
                密码:<input type="password" name="pwd" required> <br>
                <input type="submit" value="登录">
            </form>
        </body>
    </html>
    
  2. 编写 Controller 类处理请求

    package com.aze.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpSession;
    
    @Controller
    @RequestMapping("/user")
    public class UserController {
    
        //跳转到登陆页面
        @RequestMapping("/jumplogin")
        public String jumpLogin() throws Exception {
            return "login";
        }
    
        //跳转到成功页面
        @RequestMapping("/jumpSuccess")
        public String jumpSuccess() throws Exception {
            return "success";
        }
    
        //登陆提交
        @RequestMapping("/login")
        public String login(HttpSession session, String username, String pwd) throws Exception {
            // 向session记录用户身份信息
            System.out.println("接收前端==="+username);
            session.setAttribute("user", username);
            return "success";
        }
    
        //退出登陆
        @RequestMapping("logout")
        public String logout(HttpSession session) throws Exception {
            // session 过期
            session.invalidate();
            return "login";
        }
    }
    
  3. 编写登录成功页面(success.jsp)

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
        <head>
            <title>Title</title>
        </head>
        <body>
    
            <h1>登录成功页面</h1>
            <hr>
    
            ${user}
            <a href="${pageContext.request.contextPath}/user/logout">注销</a>
        </body>
    </html>
    
  4. 编写首页,来测试页面跳转(index.jsp)

    <%--
      Created by IntelliJ IDEA.
      User: Hasaki
      Date: 2021/10/3
      Time: 16:29
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    
      <div>
        <a href="${pageContext.request.contextPath}/t1">测试</a>
      </div>
    
      <div>
        <h1>首页</h1>
        <hr>
        <%--登录--%>
        <a href="${pageContext.request.contextPath}/user/jumplogin">登录</a>
        <a href="${pageContext.request.contextPath}/user/jumpSuccess">成功页面</a>
      </div>
    
      </body>
    </html>
    
  5. 编写用户登录拦截器

    package com.aze.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    public class LoginInterceptor implements HandlerInterceptor {
    
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
            // 如果是登陆页面则放行
            System.out.println("url: " + request.getRequestURI());
            if (request.getRequestURI().contains("login")) {
                return true;
            }
    
            HttpSession session = request.getSession();
    
            // 如果用户已登陆也放行
            if(session.getAttribute("user") != null) {
                return true;
            }
    
            // 用户没有登陆跳转到登陆页面
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
            return false;
        }
    
    }
    
  6. 在 springmvc 配置文件中注册拦截器

    <!--关于拦截器的配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--/** 包括路径及其子路径-->
            <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
            <!--/admin/** 拦截的是/admin/下的所有-->
            <mvc:mapping path="/**"/>
            <!--bean配置的就是拦截器-->
            <bean class="com.aze.config.MyInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/user/**"/>
            <bean class="com.aze.config.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
  7. 配置 Tomcat

  8. 测试

13、文件上传和下载

13.1 准备测试

文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是 SpringMVC 上下文中默认没有装配 MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用 Spring 的文件上传功能,则需要在上下文中配置 MultipartResolver。

前端表单要求:为了能上传文件,必须将表单的 method 设置为 POST,并将 enctype 设置为 multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;

对表单中的 enctype 属性做个详细的说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
    <input type="file" name="file"/>
    <input type="submit">
</form>

一旦设置了 enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的 HTTP 响应。在2003年,Apache Software Foundation 发布了开源的 Commons FileUpload 组件,其很快成为 Servlet/JSP 程序员上传文件的最佳选择。

  • Servlet3.0 规范已经提供方法来处理文件上传,但这种上传需要在 Servlet中完成。
  • 而 Spring MVC 则提供了更简单的封装。
  • Spring MVC 为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver 实现的。
  • Spring MVC 使用 Apache Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResolver。
  • 因此,SpringMVC 的文件上传还需要依赖 Apache Commons FileUpload 的组件。

13.2 文件上传

  1. 新建一个项目,添加 web 支持

  2. 导入相关 jar 包

    <dependencies>
        <!--文件上传-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
        <!--servlet-api导入高版本的-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
    </dependencies>
    
  3. 配置

    • 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">
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>encoding</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>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>
    
    • applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/mvc
           https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
        <context:component-scan base-package="com.aze.controller"/>
        <!--过滤静态资源-->
        <mvc:default-servlet-handler />
        <mvc:annotation-driven />
    
        <!--统一的解决乱码问题-->
        <mvc:annotation-driven>
            <mvc:message-converters register-defaults="true">
                <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <constructor-arg value="UTF-8"/>
                </bean>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper">
                        <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                            <property name="failOnEmptyBeans" value="false"/>
                        </bean>
                    </property>
                </bean>
            </mvc:message-converters>
        </mvc:annotation-driven>
    
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!-- 前缀 -->
            <property name="prefix" value="/WEB-INF/jsp/" />
            <!-- 后缀 -->
            <property name="suffix" value=".jsp" />
        </bean>
    
    </beans>
    
  4. 在 applicationContext.xml 中添加 bean 配置

    • 注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!
    <!--文件上传配置-->
    <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>
    
  5. 编写前端页面

    <%--
      Created by IntelliJ IDEA.
      User: Hasaki
      Date: 2021/10/3
      Time: 17:54
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    
      <div>
        <form action="/upload2" enctype="multipart/form-data" method="post">
          <input type="file" name="file"/>
          <input type="submit" value="upload">
        </form>
      </div>
    
      </body>
    </html>
    
  6. 编写 Controller

    @RestController
    public class FileController {
        
    }
    
    • 方式一:

      //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
      //批量上传CommonsMultipartFile则为数组即可
      @RequestMapping("/upload")
      public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
      
          //获取文件名 : file.getOriginalFilename();
          String uploadFileName = file.getOriginalFilename();
      
          //如果文件名为空,直接回到首页!
          if ("".equals(uploadFileName)){
              return "redirect:/index.jsp";
          }
          System.out.println("上传文件名 : "+uploadFileName);
      
          //上传路径保存设置
          String path = request.getServletContext().getRealPath("/upload");
          //如果路径不存在,创建一个
          File realPath = new File(path);
          if (!realPath.exists()){
              realPath.mkdir();
          }
          System.out.println("上传文件保存地址:"+realPath);
      
          InputStream is = file.getInputStream(); //文件输入流
          OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流
      
          //读取写出
          int len=0;
          byte[] buffer = new byte[1024];
          while ((len=is.read(buffer))!=-1){
              os.write(buffer,0,len);
              os.flush();
          }
          os.close();
          is.close();
          return "success";
      }
      
    • 方式二:采用 file.Transto 来保存上传的文件

      /*
           * 采用file.Transto 来保存上传的文件
           */
      @RequestMapping("/upload2")
      public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
      
          //上传路径保存设置
          String path = request.getServletContext().getRealPath("/upload");
          File realPath = new File(path);
          if (!realPath.exists()){
              realPath.mkdir();
          }
          //上传文件地址
          System.out.println("上传文件保存地址:"+realPath);
      
          //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
          file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
      
          return "success";
      }
      
  7. 配置 Tomcat

  8. 测试

13.3 文件下载

步骤:

  1. 设置 response 响应头
  2. 读取文件 – InputStream
  3. 写出文件 – OutputStream
  4. 执行操作
  5. 关闭流 (先开后关)

Controller 类添加

@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
    //要下载的图片地址
    String  path = request.getServletContext().getRealPath("/upload");
    String  fileName = "baidu.png";

    //1、设置response 响应头
    response.reset(); //设置页面不缓存,清空buffer
    response.setCharacterEncoding("UTF-8"); //字符编码
    response.setContentType("multipart/form-data"); //二进制传输数据
    //设置响应头
    response.setHeader("Content-Disposition",
            "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

    File file = new File(path,fileName);
    //2、 读取文件--输入流
    InputStream input=new FileInputStream(file);
    //3、 写出文件--输出流
    OutputStream out = response.getOutputStream();

    byte[] buff =new byte[1024];
    int index=0;
    //4、执行 写出操作
    while((index= input.read(buff))!= -1){
        out.write(buff, 0, index);
        out.flush();
    }
    out.close();
    input.close();
    return null;
}

前端添加

<div>
  <a href="/download">点击下载</a>
</div>

测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值