• 学习笔记_SpringMVC


    SpringMvc

    学习视频,B站狂神说Java——SpringMVC

    参考文章

    ssm: mybatis + spring + springmvc

    1、MVC

    1.1、什么是MVC

    • MVC是模型(Model)、视图(View)、控制器(controller)的简写。是一种软件设计的规范。
    • 是将业务逻辑、数据、显示分离的方法来组织代码
    • MVC主要是减低了视图与业务逻辑之间的双向耦合
    • MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。

    Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao)和服务层(行为Service),也就是模型提供了模型数据查询和模型数据的转台更新等功能,包括数据和业务。

    View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户向看到的东西。

    controller(控制器):接受用户请求,委托给模型进行处理(状态改变),如果完毕后把返回的模型数据返回给视图,有视图负责展示,也就是所控制器做了个调度员的工作。

    最典型的MVC是JSP + Servlet + JavaBean的模式

    image-20210304172159268

    MVC架构图

    image-20210118160241088

    1.2、Model1

    • 在web早期开发中,常采用的都是Model1
    • 在Model1中,主要分为两层,视图层和模型层

    image-20210118161733711

    1.3、Model2

    image-20210118161908980

    image-20210118162012891

    1.4、三层架构各层职责分析

    Controller:控制器

    1. 取得表单数据
    2. 调用业务逻辑
    3. 指向指定的页面

    Model:模型

    1. 业务逻辑
    2. 保存数据的状态

    View:视图

    1. 显示页面

    2、什么是SpringMVC

    2.1、概述

    img

    springMVC 官方文档

    老版本官方文档(学习使用)

    为什么要学习SpringMVC呢?

    SpringMVC的特点:

    1. 轻量级,简单易学
    2. 高效,基于请求响应的MVC框架
    3. 与Spring兼容性好,无缝结合
    4. 约定优于配置
    5. 功能强大:RESTFUL、数据验证、格式化、本地化、主题等
    6. 简介灵活

    Spring的Web框架围绕DispatcherServlet[调度Servlet]设计

    DispatcherServlet的作用是将请求分发到不同的处理器。从Spring2.5开始,使用Java5或者以上版本的用户可以采用基于注解形式进行开发。十分简洁;

    MVC框架要做哪些事情?

    1. 将url映射到Java类或Java类的方法
    2. 封装用户提交的数据
    3. 处理请求,调用相关的业务处理,封装响应数据
    4. 将响应数据进行渲染,jsp/html等视图层的数据

    2.2、中心控制器

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

      img

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

    • SpringMVC的原理如下:

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

      在这里插入图片描述

      在这里插入图片描述

    2.3、SpringMVC执行原理

    在这里插入图片描述

    上图为SpringMVC的一个比较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

    简要分析执行流程

    1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接受请求并拦截请求。
      1. 我们假设请求的url为:http://localhost:8080/SpringMVC/hello
      2. 将url拆分为三部分:
      3. http://localhost:8080---------->服务器域名
      4. SpringMvc---------->部署在服务器上的Web站点
      5. hello------------------>表示控制器
      6. 通过分析,上述的url表示为:请求位于服务器localhost:8080上的SpringMVC 站点的hello控制器。
    2. HandleMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler.
    3. HandlerExecution表示具体的Handler,其作用是根据url查找控制器,如上述url被查找控制器为:hello.
    4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
    5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
    6. HandlerAdapter让具体的Controller执行。
    7. Controller将具体执行后的信息返回给HandlerAdapter, 如ModeAndView。
    8. HandlerAdapter将试图逻辑名或模型传递给DispatcherServlet
    9. DispatcherServlet根据视图解析器解析(ViewResolver)的视图结果,调用具体的视图。
    10. 最终将试图呈现给用户。

    3、第一个MVC程序

    3.1、配置版

    1. 新建一个Moudle,添加Web支持

    2. 确定导入了SpringMVC的依赖。

    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">
      
          <!--1.注册DispatcherServlet:这是SpringMVC的核心;请求分发器,前端控制器-->
          <servlet>
              <servlet-name>springmvc</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <!--DiapatcherServlet要绑定一个spring配置文件
              关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
              <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>classpath:springmvc-servlet.xml</param-value>
              </init-param>
              <!--启动级别-1-->
              <load-on-startup>1</load-on-startup>
          </servlet>
      
          <!--/ 匹配所有的请求;(不包括.jsp)-->
          <!--/* 匹配所有的请求;(包括.jsp)-->
          <servlet-mapping>
              <servlet-name>springmvc</servlet-name>
              <url-pattern>/</url-pattern>
          </servlet-mapping>
      
      </web-app>
      
    4. 编写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">
      
          <!--处理器映射器-->
          <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
          <!--处理器适配器-->
          <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
          <!--视图解析器:DispatcherServlet给他的ModelAndView
          1. 获取ModelAndView的数据
          2. 解析ModelAndView的视图名字
          3. 拼接视图名字,找到对应视图,比如找到的视图名字为:hello,拼接后为:/WEB-INF/jsp/hello.jsp
          4. 将数据渲染到视图中
          -->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
              <!--前缀-->
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <!--后缀-->
              <property name="suffix" value=".jsp"/>
          </bean>
      <!--    Handler-->
          <bean id="/hello" class="com.liuxingwu.controller.HelloController"/>
      
      </beans>
      
    5. 编写要操作业务的控制器Controller

      package com.liuxingwu.controller;
      
      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      /**
       * @author saodidheng
       * @Date 2021/3/4
       */
      // 注意:这里我们要导入controller接口
      public class HelloController implements Controller {
          public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
              // ModelAndView 模型和视图
              ModelAndView mv = new ModelAndView();
      
              // 封装对象,放在ModelAndView中,
              mv.addObject("msg", "HelloSpringMVC");
              // 封装要跳转的对象的视图,放在ModelAndView中
              mv.setViewName("hello");        // :/WEB-OMF/jsp/hello.jsp
              return mv;
          }
      }
      
    6. 在springmvc-servlet.xml配置文件中将控制器交给Spring容器管理

      <!--Handler-->
      <bean id="/hello" class="com.liuxingwu.controller.HelloController"/>
      
    7. 编写要跳转的页面

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

    3.2、注解版

    1. 新建一个普通maven Moudle,添加web支持

    2. 为防止Maven出现资源过滤问题,在pom.xml中添加资源路径

      <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. 配置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.注册DispatcherServlet:这是SpringMVC的核心;请求分发器,前端控制器-->
          <servlet>
              <servlet-name>springmvc</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <!--DiapatcherServlet要绑定一个spring配置文件
              关联一个springmvc的配置文件:【servlet-name】-servlet.xml-->
              <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>classpath:springmvc-servlet.xml</param-value>
              </init-param>
              <!--启动级别-1-->
              <load-on-startup>1</load-on-startup>
          </servlet>
      
          <!--/ 匹配所有的请求;(不包括.jsp)-->
          <!--/* 匹配所有的请求;(包括.jsp)-->
          <servlet-mapping>
              <servlet-name>springmvc</servlet-name>
              <url-pattern>/</url-pattern>
          </servlet-mapping>
      </web-app>
      
    4. 添加SpringMVC配置文件

      在resource目录下添加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"
             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.liuxingwu.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>
      

    在视图解析器中,我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。

    • 让IOC的注解生效
    • 静态资源过滤:HTML, JS, CSS, 图片,视图...
    • MVC的注解驱动
    • 配置视图解析器
    1. 创建Controller

      编写一个Java控制类

      package com.liuxingwu.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;/** * @author saodisheng * @Date 2021/3/4 */@Controller@RequestMapping("/HelloController")public class HelloController {     //真实访问地址 : 项目名/HelloController/hello    @RequestMapping("/hello")    public String hello(Model model) {        // 封装数据        model.addAttribute("msg", "hello, SpringMVCAnnotation!");        return "hello";     // 会被视图解析器处理视图    }}
      
      1. @Controller是为了让Spring IOC容器初始化时自动扫描到
      2. @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射,所以访问的应该是/HelloController/hello;
      3. 方法中声明Model类型的参数是为了把Action中的数据带到视图中
      4. 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成/WEB-INF/jsp/hello.jsp
    2. 创建视图

      在WEB-INF/jsp目录中创建hello.jsp,视图可以直接取出并展示从Controller带回的信息。可以通过EL 表是取出Model中存放的值或对象。

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

    小结

    实现步骤:

    1. 新建一个web项目
    2. 导入相关jar包
    3. 编写web.xml,注册到DispatcherServlet
    4. 编写springmvc配置文件
    5. 创建对应的控制类,Controller
    6. 完善前端视图和Controller之间的对应
    7. 测试

    使用springMVC 必须配置的三大件

    1. 处理器映射器
    2. 处理器适配器
    3. 视图解析器

    通常,我们只需要手动配饰视图解析器,而处理器和处理器适配器只需要开启注解驱动即可。

    4、注解的使用

    4.1、控制器Controller

    • 控制器负责提供访问应用程序的行为,通常通过接口定义或注解定义两种方式实现。
    • 控制器负责解析用户请求并将其转换成一个模型。
    • 在SpringMVC中一个控制器类可以包含多个方法。
    • 在SpringMVC中,对于Controller的配置方式有很多种。

    4.2、实现Controller接口

    Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法:

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

    2. 在MVC配置文件中只留下视图解析器

    3. 编写一个controller类

      //定义控制器
      //注意点:不要导错包,实现Controller接口,重写方法;
      public class ControllerTest1 implements Controller {
         public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
             //返回一个模型视图对象
             ModelAndView mv = new ModelAndView();
             mv.addObject("msg","Test1Controller");
             mv.setViewName("test");
             return mv;
        }
      }
      
      
      
    4. 在Spring配置文件中注册请求的bean,name对应的是请求路径,class对应的是处理请求的类

      <bean name="/t1" class="com.liuxingwu.controller.ControllerTest1"/>
      
    5. 编写前端的test.jsp,将该视图放入WEB-INF/jsp目录下,和视图解析器的前缀想匹配。

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
         <title>Kuangshen</title>
      </head>
      <body>
      ${msg}
      </body>
      </html>
      
    6. 部署到服务器Tomcat上运行

    说明:

    1. 实现接口Controller定义控制器是传统的方式
    2. 该方式的缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller控制器,相对而言比较麻烦。

    4.3、使用注解@Controller

    1. @Controller注解来让用户声明对应的类是Spring容器中的一个控制器。
    2. Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到指定的控制器,需要在配置文件中声明组件扫描:
    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.liuxingwu.controller"/>
    
    1. 增加一个Controller类,使用注解实现:
    //@Controller注解的类会自动添加到Spring上下文中
    @Controller
    public class ControllerTest2{
    
       //映射访问路径
       @RequestMapping("/t2")
       public String index(Model model){
           //Spring MVC会自动实例化一个Model对象用于向视图中传值
           model.addAttribute("msg", "ControllerTest2");
           //返回视图位置
           return "test";
      }
    }
    
    1. 部署到服务器Tomcat运行

    可以看出,两个控制器转发到的是同一个前端视图,但页面结果却有所出入,从而可以知道视图是被复用的,而控制器与视图之间是弱耦合关系。

    注解方式是平时使用最多的方式

    RequestMapping

    @RequestMapping

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

    • 注解在方法上

      @Controller
      public class TestController {
         @RequestMapping("/h1")
         public String test(){
             return "test";
        }
      }
      

      访问路径为:http://localhost:8080/项目名/h1

    • 同时注解类与方法

      @Controller
      @RequestMapping("/admin")
      public class TestController {
         @RequestMapping("/h1")
         public String test(){
             return "test";
        }
      }
      

      访问路径为:http://localhost:8080/项目名/admin/h1,即需要先指定类的路径再指定方法的路径。

    5、RestFul风格

    5.1、概念

    RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件更加简洁,更有层次,更易实现缓存等机制。

    5.2、功能

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

    资源操作:使用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

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

    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

    1. 新建一个测试类

      @Controller
      public class RestFulController {}
      
    2. 在SpringMVC中可以使用@PathVariable注解,让方法参数的值对应绑定到一个URL模板的变量上:

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

    思考:使用路径变量的好处?

    1. 使路径变得更加简洁
    2. 获取参数更加方便,框架会自动进行类型转换。
    3. 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法。

    使用method属性指定请求类型

    用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

    如下:

    //映射访问路径,必须是POST请求
    @RequestMapping(value = "/hello",method = {RequestMethod.POST})
    public String index2(Model model){
       model.addAttribute("msg", "hello!");
       return "test";
    }
    

    小结:

    SpringMVC的@RequestMapping注解能够处理HTTP请求的方法,如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等

    所有的地址栏请求默认都会是HTTP GET类型

    方法级别的注解变体有如下几个:组合注解

    @GetMapping

    @PostMapping

    @PutMapping

    @DeleteMapping

    @PatchMapping

    6、结果跳转方式

    6.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;
      }
    }
    

    6.2、ServletAPI

    ServletAPI

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

    1. 通过HttpServletResponse进行输出
    2. 通过HttpServletResponse实现重定向
    3. 通过HttpServletRequest实现转发
    @Controller
    public class ResultGo {
    
       @RequestMapping("/result/t1")
       public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
           rsp.getWriter().println("Hello,Spring BY servlet API");
      }
    
       @RequestMapping("/result/t2")
       public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
           rsp.sendRedirect("/index.jsp");
      }
    
       @RequestMapping("/result/t3")
       public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
           //转发
           req.setAttribute("msg","/result/t3");
           req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
      }
    }
    

    6.3、SpringMVC

    通过SpringMVC来实现转发和重定向,无需视图解析器

    先将原来配置文件中的视图解析器注释掉

    1. 默认通过forward转发
    2. redirect重定向需要注明
    @Controller
    public class ResultSpringMVC {
       @RequestMapping("/rsm/t1")
       public String test1(){
           //转发
           return "/index.jsp";
      }
    	// 等价于test1
       @RequestMapping("/rsm/t2")
       public String test2(){
           //转发二
           return "forward:/index.jsp";
      }
    
       @RequestMapping("/rsm/t3")
       public String test3(){
           //重定向
           return "redirect:/index.jsp";
      }
    }
    

    通过SpringMVC来实现转发和重定向,有视图解析器

    重定向,不需要视图解析器,本质就是重新请求一个新地方,只需注意路径问题。可以重定向到另外一个请求实现

    • 默认为forward转发(可以不加上)
    • redirect重定向需要特别加上
    @Controller
    public class ResultSpringMVC2 {
       @RequestMapping("/rsm2/t1")
       public String test1(){
           //转发
           return "test";
      }
       @RequestMapping("/rsm2/t2")
       public String test2(){
           //重定向
           return "redirect:/index.jsp";
           //return "redirect:hello.do"; //hello.do为另一个请求/
      }
    }
    

    7、数据处理

    7.1、处理提交数据

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

      提交数据: http://localhost:8080/hello?name=saodisheng

      代码:

      @RequestMapping("/hello")
      public String hello(String name){
         System.out.println(name);
         return "hello";
      }
      
    2. 提交的域名称和处理的参数名不一致

      提交数据 : http://localhost:8080/hello?username=saodisheng

      需要使用注解@RequestParam("设定参数名")

      代码:

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

      要求提交的表单域和对象的属性名一一对应,如果不一致,则对象的属性名会为默认值。控制器中可以直接传递一个对象类型

      1. 实体类

        public class User {
           private int id;
           private String name;
           private int age;
        }
        
        
      2. 提交数据 : http://localhost:8080/mvc04/user?name=saodisheng&id=1&age=15

      3. 控制器代码:

        @RequestMapping("/user")
        public String user(User user){
           System.out.println(user);
           return "hello";
        }
        

    7.2、数据显示到前端

    1、通过ModelAndView

    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;
      }
    }
    

    2、通过ModelMap

    @RequestMapping("/hello")
    public String hello(@RequestParam("username") String name, ModelMap modelMap){
       //封装要显示到视图中的数据
       //相当于req.setAttribute("name",name);
       modelMap.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";
    }
    

    8、处理乱码问题

    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>不能漏写一个*,写成
    	<url-pattern>/</url-pattern>,这样的话过滤器无法达到我们的目的
    	-->
       <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    网上提供了一个较为全面的过滤器代码:

    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;
      }
    }
    

    9、Json交互处理

    8.1、什么是JSON?

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

    在JS中,一切都是对象。因此,任何JS支持的类型都可以通过JSON来表示,

    例如字符串、数字、对象、数组等。其要求和语法格式如下:

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

    JSON键值对是用来保存JS对象的一种方式,和JS 对象的写法也很相似。

    {"name": "SaoDiSheng"}
    {"age": "1"}
    {"sex": "男"}
    

    注意JSON和JS对象表示的区别:

    var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的
    var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串
    

    9.2、JSON和JS对象的转换

    1、使用JSON.parse()方法将JSON字符串转换为JS对象:

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

    2、使用JSON.stringify()方法将JS对象转为JSON字符串:

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

    9.3、Controller返回JSON数据

    • JackSon应该是目前比较好的Json解析工具了

    • 还有阿里巴巴的fastjson等

    • 例题中使用的是Jackson,先导入相应的jar包:

      <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
      <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.9.8</version>
      </dependency>
      
    • 在SpringMVC配置文件中配置好相应的内容:

      • 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>
             <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-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"
            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.liuxingwu.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>
      
    • 编写实体类

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private int id;
          private String name;
        private int age;
      }
      
    • 使用注解开发

      @Controller
      public class UserController {
      	@RequestMapping("/j1")
          @ResponseBody//他就不会走视图解析器,会直接返回一个字符串
          public String json1() throws JsonProcessingException {
              //jackson,ObjectMapper
              ObjectMapper mapper = new ObjectMapper();
      
              //创建一个对象
              User user = new User(1, "扫地生1", 12);
              //System.out.println(user);
      
              String str = mapper.writeValueAsString(user);
              return str;
          }
      }
      

    9.4、代码块优化

    乱码统一处理

    可以在springmvc的配置文件上添加StringHttpMessageConverter转换配置(SpringMVC自带的过滤器):

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

    返回JSON字符串统一处理

    • @ResponseBody解决(不建议使用该方法)

      @Controller
      public class UserController {
         // produces:指定响应体返回类型和编码
         @RequestMapping(value = "/json1", produces="指定响应体返回类型和编码")
         @ResponseBody
         public String json1() throws JsonProcessingException {
             // 创建一个jackson的对象映射器,用来解析数据
             ObjectMapper mapper = new ObjectMapper();
             // 创建一个对象
             User user = new User(1, "扫地生1", 12);
             // 将我们的对象解析成为json格式
             String str = mapper.writeValueAsString(user);
             // 由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
             return str;
        }
      }
      
    • @RestController(直接在类上使用)

      @RestController
      public class UserController {
         @RequestMapping(value = "/j1")
         public String json1() throws JsonProcessingException {
             // 创建一个jackson的对象映射器,用来解析数据
             ObjectMapper mapper = new ObjectMapper();
             // 创建一个对象
             User user = new User(1, "扫地生1", 12);
             // 将我们的对象解析成为json格式
             String str = mapper.writeValueAsString(user);
             return str;
        }
      
      }
      

    9.5、集合测试输出

    @RequestMapping("/j2")
    public String json2() throws JsonProcessingException {
        // 创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        // 创建一个对象
       	User user1 = new User(1, "扫地生1号", 12);
        User user2 = new User(2, "扫地生2号", 12);
        User user3 = new User(3, "扫地生3号", 12);
        User user4 = new User(4, "扫地生4号", 12);
        User user5 = new User(5, "扫地生5号", 12);
        List<User> list = new ArrayList<User>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        list.add(user5);
        // 将我们的对象解析成为json格式
        String str = mapper.writeValueAsString(list);
        return str;
    }
    

    9.6、输出时间对象

    @RequestMapping("/j3")
    public String json3() throws JsonProcessingException {
       ObjectMapper mapper = new ObjectMapper();
       // 创建时间一个对象,java.util.Date
       Date date = new Date();
       // 将我们的对象解析成为json格式
       String str = mapper.writeValueAsString(date);
       return str;
    }
    

    可以定义时间格式:

    @RequestMapping("/j3")
    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();
       String str = mapper.writeValueAsString(date);
       return str;
    }
    

    9.7、可根据需要抽取出工具类

    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();
            // java自定义日期格式
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            // sdf.format(date)
            // 使用ObjectMapper 来格式化输出
     	 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
            mapper.setDateFormat(sdf);
            try {
                // ObjectMapper,时间解析后的默认格式为:TImestamp.时间戳
                return mapper.writeValueAsString(object);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    9.8、FastJson

    • fastJson.jar是阿里开发的一款专用与Java的jar包
    • 实现json独享与javaBean对象之间的转换
    • 实现JavaBean对象与Json字符串之间的转换
    • 实现js对象与json字符串之间的转换
    • 实现json的转换方法很多,目的都是一致的。

    fastjson的pom依赖:

    <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>fastjson</artifactId>
       <version>1.2.60</version>
    </dependency>
    

    fastjson中三个主要的类:

    1. JSONObject代表的json对象
      1. JSONObject实现了Map接口,所以JSONObject底层操作可能是由Map实现的。
      2. JSONObject对应的json对象,通过各种形式的get()方法可以获取json对象中的数据,也可以利用诸如size(),isEmpty()等方法获取“键:值”对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。
    2. JSONArray代表Json对象数组
      1. 内部是有List接口中的方法来完成操作的。
    3. JSON代表JSONObject和JSONArray的转化
      1. JSON类源码分析与使用
      2. 仔细观察这些方法,主要是实现json对象,json对象数组,Javabean对象, json字符串之间的相互转换。
    @RequestMapping("/j4")
        //@ResponseBody//他就不会走视图解析器,会直接返回一个 字符串
        public String json4(){
    
            User user1 = new User(1, "扫地生1号", 12);
            User user2 = new User(2, "扫地生2号", 12);
            User user3 = new User(3, "扫地生3号", 12);
            User user4 = new User(4, "扫地生4号", 12);
            User user5 = new User(5, "扫地生5号", 12);
    
            List<User> list = new ArrayList<User>();
            list.add(user1);
            list.add(user2);
            list.add(user3);
            list.add(user4);
            list.add(user5);
    
            System.out.println("*******Java对象 转 JSON字符串*******");
            String str1 = JSON.toJSONString(list);
            System.out.println("JSON.toJSONString(list)==>"+str1);
            String str2 = JSON.toJSONString(user1);
            System.out.println("JSON.toJSONString(user1)==>"+str2);
    
            System.out.println("
    ****** JSON字符串 转 Java对象*******");
            User jp_user1=JSON.parseObject(str2,User.class);
            System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
    
            System.out.println("
    ****** Java对象 转 JSON对象 ******");
            JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
            System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
    
            System.out.println("
    ****** 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(list);
        }
    }
    

    10、Ajax

    10.1、简介

    • 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用户界面。

    10.2、伪造Ajax

    我们可以使用前端的一个标签来伪造一个ajax的样子。iframe标签

    1、新建一个module :sspringmvc-06-ajax , 导入web支持!

    2、编写一个 ajax-frame.html 使用 iframe 测试,感受下效果

    <!DOCTYPE html>
    <html>
    <head lang="en">
       <meta charset="UTF-8">
       <title>kuangshen</title>
    </head>
    <body>
    
    <script type="text/javascript">
       window.onload = function(){
           var myDate = new Date();
           document.getElementById('currentTime').innerText = myDate.getTime();
      };
    
       function LoadPage(){
           var targetUrl =  document.getElementById('url').value;
           console.log(targetUrl);
           document.getElementById("iframePosition").src = targetUrl;
      }
    
    </script>
    
    <div>
       <p>请输入要加载的地址:<span id="currentTime"></span></p>
       <p>
           <input id="url" type="text" value="https://www.baidu.com/"/>
           <input type="button" value="提交" onclick="LoadPage()">
       </p>
    </div>
    
    <div>
       <h3>加载页面位置:</h3>
       <iframe id="iframePosition" style=" 100%;height: 500px;"></iframe>
    </div>
    
    </body>
    </html>
    

    3、使用IDEA开浏览器测试一下!

    利用AJAX可以做:

    • 注册时,输入用户名自动检测用户是否已经存在。
    • 登陆时,提示用户名密码错误
    • 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。
    • ....等等

    10.3、jQuery.ajax

    纯JS原生实现Ajax我们不去讲解这里,直接使用jquery提供的,方便学习和使用,避免重复造轮子。

    Ajax的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。

    jQuery 提供多个与 AJAX 有关的方法

    通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中。

    jQuery 不是生产者,而是大自然搬运工。

    jQuery Ajax本质就是 XMLHttpRequest,对他进行了封装,方便调用!

    jQuery.ajax(...)
          部分参数:
                url:请求地址
                type:请求方式,GET、POST(1.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 将自动替换 ? 为正确的函数名,以执行回调函数
    

    简单的测试:使用最原始的HttpServletResponse处理 , 最简单 最通用

    1、配置web.xml 和 springmvc的配置文件,复制上面案例的即可 【记得静态资源过滤和注解驱动配置上】

    <?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.kuang.controller"/>
        <!-- 静态资源过滤 -->
       <mvc:default-servlet-handler />
        <!-- 注解驱动 -->
       <mvc:annotation-driven />
    
       <!-- 视图解析器 -->
       <beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"
             id="internalResourceViewResolver">
           <!-- 前缀 -->
           <property name="prefix" value="/WEB-INF/jsp/" />
           <!-- 后缀 -->
           <property name="suffix" value=".jsp" />
       </bean>
    
    </beans>
    

    2、编写一个AjaxController

    @Controller
    public class AjaxController {
    
       @RequestMapping("/a1")
       public void ajax1(String name , HttpServletResponse response) throws IOException{
           if ("admin".equals(name)){
               response.getWriter().print("true");
          }else{
               response.getWriter().print("false");
          }
      }
    
    }
    

    3、导入jquery , 可以使用在线的CDN , 也可以下载导入

    
    4、编写index.jsp测试
    
    ```jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
     <head>
       <title>扫地生</title>
      <%--<script src="https://code.jquery.com/jquery-3.6.0.js"></script>--%>
       <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" onblur="a1()"/>
    
     </body>
    </html>
    

    5、启动tomcat测试!打开浏览器的控制台,当我们鼠标离开输入框的时候,可以看到发出了一个ajax的请求!是后台返回给我们的结果!测试成功!

    Springmvc实现

    实体类user

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
       private String name;
       private int age;
       private String sex;
    
    }
    

    我们来获取一个集合对象,展示到前端页面

    @RequestMapping("/a2")
    public List<User> ajax2(){
       List<User> list = new ArrayList<User>();
       list.add(new User("扫地生1",1,"男"));
       list.add(new User("扫地生2",2,"男"));
       list.add(new User("扫地生3",3,"男"));
       return list; //由于@RestController注解,将list转成json格式返回
    }
    

    前端页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
       <title>Title</title>
    </head>
    <body>
    <input type="button" id="btn" value="获取数据"/>
    <table width="80%" align="center">
       <tr>
           <td>姓名</td>
           <td>年龄</td>
           <td>性别</td>
       </tr>
       <tbody id="content">
       </tbody>
    </table>
    
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
    <script>
    
       $(function () {
           $("#btn").click(function () {
               $.post("${pageContext.request.contextPath}/a2",function (data) {
                   console.log(data)
                   var html="";
                   for (var 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);
              });
          })
      })
    </script>
    </body>
    </html>
    

    成功实现了数据回显!可以体会一下Ajax的好处!

    10.4、注册提示效果

    再测试一个小Demo,思考一下我们平时注册时候,输入框后面的实时提示怎么做到的;如何优化

    我们写一个Controller

    @RequestMapping("/a3")
    public String ajax3(String name,String pwd){
       String msg = "";
       //模拟数据库中存在数据
       if (name!=null){
           if ("admin".equals(name)){
               msg = "OK";
          }else {
               msg = "用户名输入错误";
          }
      }
       if (pwd!=null){
           if ("123456".equals(pwd)){
               msg = "OK";
          }else {
               msg = "密码输入有误";
          }
      }
       return msg; //由于@RestController注解,将msg转成json格式返回
    }
    

    前端页面 login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
       <title>ajax</title>
       <script src="${pageContext.request.contextPath}/statics/js/jquery-3.1.1.min.js"></script>
       <script>
           function a1(){
               $.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 a2(){
               $.post({
                   url:"${pageContext.request.contextPath}/a3",
                   data:{'pwd':$("#pwd").val()},
                   success:function (data) {
                       if (data.toString()=='OK'){
                           $("#pwdInfo").css("color","green");
                      }else {
                           $("#pwdInfo").css("color","red");
                      }
                       $("#pwdInfo").html(data);
                  }
              });
          }
    
       </script>
    </head>
    <body>
    <p>
      用户名:<input type="text" id="name" onblur="a1()"/>
       <span id="userInfo"></span>
    </p>
    <p>
      密码:<input type="text" id="pwd" onblur="a2()"/>
       <span id="pwdInfo"></span>
    </p>
    </body>
    </html>
    

    【记得处理json乱码问题】

    测试一下效果,动态请求响应,局部刷新,就是如此!

    img

    10.5、获取baidu接口Demo

    <!DOCTYPE HTML>
    <html>
    <head>
       <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
       <title>JSONP百度搜索</title>
       <style>
           #q{
                500px;
               height: 30px;
               border:1px solid #ddd;
               line-height: 30px;
               display: block;
               margin: 0 auto;
               padding: 0 10px;
               font-size: 14px;
          }
           #ul{
                520px;
               list-style: none;
               margin: 0 auto;
               padding: 0;
               border:1px solid #ddd;
               margin-top: -1px;
               display: none;
          }
           #ul li{
               line-height: 30px;
               padding: 0 10px;
          }
           #ul li:hover{
               background-color: #f60;
               color: #fff;
          }
       </style>
       <script>
    
           // 2.步骤二
           // 定义demo函数 (分析接口、数据)
           function demo(data){
               var Ul = document.getElementById('ul');
               var html = '';
               // 如果搜索数据存在 把内容添加进去
               if (data.s.length) {
                   // 隐藏掉的ul显示出来
                   Ul.style.display = 'block';
                   // 搜索到的数据循环追加到li里
                   for(var i = 0; i < data.s.length; i++){
                       html += '<li>'+data.s[i]+'</li>';
                  }
                   // 循环的li写入ul
                   Ul.innerHTML = html;
              }
          }
    
           // 1.步骤一
           window.onload = function(){
               // 获取输入框和ul
               var Q = document.getElementById('q');
               var Ul = document.getElementById('ul');
    
               // 事件鼠标抬起时候
               Q.onkeyup = function(){
                   // 如果输入框不等于空
                   if (this.value != '') {
                       // JSONPz重点
                       // 创建标签
                       var script = document.createElement('script');
                       //给定要跨域的地址 赋值给src
                       //这里是要请求的跨域的地址 我写的是百度搜索的跨域地址
                       script.src ='https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+this.value+'&cb=demo';
                       // 将组合好的带src的script标签追加到body里
                       document.body.appendChild(script);
                  }
              }
          }
       </script>
    </head>
    
    <body>
    <input type="text" id="q" />
    <ul id="ul">
    
    </ul>
    </body>
    </html>
    

    Ajax在我们开发中十分重要,一定要学会使用!

    11、拦截器+文件上传下载

    拦截器

    11.1、概述

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

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

    过滤器

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

    拦截器

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

    11.2、自定义拦截器

    那如何实现拦截器呢?

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

    1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持

    2、配置web.xml 和 springmvc-servlet.xml 文件

    3、编写一个拦截器

    package top.saodisheng.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    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="top.saodisheng.interceptor.MyInterceptor"/>
       </mvc:interceptor>
    </mvc:interceptors>
    

    5、编写一个Controller,接收请求

    package top.saodisheng.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    //测试拦截器的控制器
    @Controller
    public class InterceptorController {
    
       @RequestMapping("/interceptor")
       @ResponseBody
       public String testFunction() {
           System.out.println("控制器中的方法执行了");
           return "hello";
      }
    }
    

    6、前端 index.jsp

    <a href="${pageContext.request.contextPath}/interceptor">拦截器测试</a>
    

    7、启动tomcat 测试一下!

    img

    11.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">
      用户名:<input type="text" name="username"> <br>
      密码:<input type="password" name="pwd"> <br>
       <input type="submit" value="提交">
    </form>
    </body>
    </html>
    

    2、编写一个Controller处理请求

    package top.saodisheng.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) throwsException {
           // 向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 页面上测试跳转!启动Tomcat 测试,未登录也可以进入主页!

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
     <head>
       <title>$Title$</title>
     </head>
     <body>
     <h1>首页</h1>
     <hr>
    <%--登录--%>
     <a href="${pageContext.request.contextPath}/user/jumplogin">登录</a>
     <a href="${pageContext.request.contextPath}/user/jumpSuccess">成功页面</a>
     </body>
    </html>
    

    5、编写用户登录拦截器

    package top.saodisheng.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    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, HttpServletResponseresponse, Object handler) throws ServletException, IOException {
           // 如果是登陆页面则放行
           // System.out.println("uri: " + 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;
      }
    
       public void postHandle(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView)throws Exception {
    
      }
       
       public void afterCompletion(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    
      }
    }
    

    6、在Springmvc的配置文件中注册拦截器

    <!--关于拦截器的配置-->
    <mvc:interceptors>
       <mvc:interceptor>
           <mvc:mapping path="/**"/>
           <bean id="loginInterceptor" class="top.saodisheng.interceptor.LoginInterceptor"/>
       </mvc:interceptor>
    </mvc:interceptors>
    

    7、再次重启Tomcat测试!

    OK,测试登录拦截功能无误.

    11.4、文件上传和下载

    准备工作

    文件上传是项目开发中最常见的功能之一 ,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的组件。

    文件上传

    1、导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;

    <!--文件上传-->
    <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>
    

    2、配置bean:multipartResolver

    注意!!!这个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>
    

    CommonsMultipartFile 的 常用方法:

    • String getOriginalFilename():获取上传文件的原名
    • InputStream getInputStream():获取文件流
    • void transferTo(File dest):将上传文件保存到一个目录文件中

    3、编写前端页面

    <form action="/upload" enctype="multipart/form-data" method="post">
     <input type="file" name="file"/>
     <input type="submit" value="upload">
    </form>
    

    4、Controller

    package top.saodisheng.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.commons.CommonsMultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.*;
    
    @Controller
    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 "redirect:/index.jsp";
      }
    }
    

    5、测试上传文件,OK!

    采用file.Transto 来保存上传的文件

    1、编写Controller

    /*
    * 采用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 "redirect:/index.jsp";
    }
    

    2、前端表单提交地址修改

    3、访问提交测试,OK!

    文件下载

    文件下载步骤:

    1、设置 response 响应头

    2、读取文件 -- InputStream

    3、写出文件 -- OutputStream

    4、执行操作

    5、关闭流 (先开后关)

    代码实现:

    @RequestMapping(value="/download")
    public String downloads(HttpServletResponse response ,HttpServletRequest request)throws Exception{
       //要下载的图片地址
       String  path = request.getServletContext().getRealPath("/upload");
       String  fileName = "基础语法.jpg";
    
       //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;
    }
    

    前端

    <a href="/download">点击下载</a>
    
    向大神看齐
  • 相关阅读:
    作业8: 软件工程学习总结
    用户体验——南通大学教务学生管理系统
    “构建之法互动游戏”感想
    第二次作业
    音乐播放器的发展演变
    C++用法的学习心得
    一、最后一次作业:软件工程学习总结
    设计一款给爸爸妈妈用的手机
    附加题1—— 我想搞懂的软工问题
    计算机病毒软件的发展演变
  • 原文地址:https://www.cnblogs.com/Liu-xing-wu/p/14641410.html
Copyright © 2020-2023  润新知