SpringMVC

Spring MVC

0.为什么需要使用框架

框架类似于Java代码的模板,它完成了一部分功能(项目中的通用规范,接口,通用功能),开发者就可以根据我们的需求完成另外一部分功能,这样就可以快速的完成一个开发标准的项目,主要是为了提高开发效率

注:所有框架想精通,初学者可以根据课程先入门,以后开发了几年项目再去研究官方网站

spring.io spring官网

https://docs.spring.io/spring-framework/reference/web/webmvc.html Spring MVC官网

1.什么是Spring MVC --- 面试题

Spring MVC是spring框架的一个子项目(导入依赖一定要和Spring版本一致),底层实现是DispacherServlet,可以在项目中实现web层功能(替换之前的Servlet),通过把模型-视图-控制器分离,将web层进行解耦合,相比Servlet效率和性能会更好,并且Spring MVC和Spring框架可以无缝连接

1.1 Spring MVC相比Servlet的优点
  • 底层是依赖于Servlet实现的,参数的获取,只需要通过方法的形参来获取
  • 不需要实现接口和父类,只需要通过@Controller和@RequestMapping两个注解,就可以实现请求获取和请求处理
  • 数据的返回也可以通过方法的形参来定义
  • Spring MVC提供了统一的编码过滤器
  • Spring MVC提供了视图解析器,可以统一解析视图
  • Spring MVC可以很方便的处理同一个模块的不同请求
  • Spring MVC提供了拦截器,可以指定哪些请求可以拦截,哪些可以放行

2.Spring MVC的搭建步骤

  • 创建新的Maven项目

  • 导入相关的依赖(jar) pom.xml

  • 配置Spring MVC配置文件

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:content="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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!--1.配置ioc扫描包:springmvc只会在提供的包去扫描@Controller注解(IOC扫描注解)
                        spring扫描到了之后才会找到控制层对象
        -->
        <content:component-scan base-package="com.sc.controller"/>
        <!--2.开启注解驱动:目的是让@RequestMapping @GetMapping...生效-->
        <mvc:annotation-driven/>
    </beans>
  • 配置web.xml(配置核心Servlet,编码过滤器)

    xml 复制代码
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <!--springmvc提供的编码过滤器-->
      <filter>
        <filter-name>EncodingFilter</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>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      <!--springmvc的核心:
          1.所有请求的入口
          2.读取它的配置文件:默认读取web-inf下的资源 默认名称servlet名称(springmvc)-servlet.xml
            如果想自定义配置文件和读取位置 需要添加初始化参数
          3.
      -->
      <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*:springmvc.xml</param-value>
        </init-param>
        <!--让其服务器启动 初始化servlet 同时读取配置文件-->
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--因为DispatcherServlet是springmvc的入口-->
        <!--/ 表示所有请求(不带后缀的请求)都要进入springmvc-->
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    </web-app>
  • 随便写个类和方法通过几个简单的注解,就可以让它接收请求处理请求

    java 复制代码
    @Controller
    public class XXX{
    	@RequestMapping()
    	public void test(){
    		
    	}
    }

3.Spring MVC输入和输出

  • 前后端不分离:前端代码和后端代码在同一个项目,不存在跨域的问题

    • 输入:前端如何将数据提交给后端

      java 复制代码
      //1.如果前端提交的数据是一个简单的数据(String,int...)
      //springmvc直接在方法的形参上定义指定类型的参数
      //传递的名称必须和springmvc方法的形参名一样
      <a href="/url?name=张三&id=10"></a>
      @RequestMapping("/url")
      public String test(String name,Integer id){}
      
      //2.如果提交多个数据,比如:批量删除
      <a href="/url?ids=1&ids=3&ids=5"></a>
      @RequestMapping("/url")
      public String test(Integer[] ids){}
      
      //3.如果提交多种数据 比如:注册 新增
      //springmvc只需要在形参定义对象类型 它会自动把传递的数据自动给对象的属性赋值(set)
      //要求:传递的名称必须和对象的属性名一致
      @RequestMapping("/url")
      public String test(User u){}
      
      //4.如果提交的数据是日期格式,springmvc默认不负责(因为它不知道格式),也可以通过
      //@Datetimeformat(日期格式)
      
      //5.springmvc针对于提交数据是中文,乱码的问题,提供了一个编码过滤器(但是你需要配置)
    • 输出:如何将后端的数据返回给前端,同时响应前端

      • 存储作用域

        java 复制代码
        //1.存储request作用域 --- 最常用
        a.形参添加HttpServletRequest
        b.形参定义Model类型
        c.形参定义Map类型 --- 比较推荐
        d.形参定义ModelMap类型
        e.通过ModelAndView(重要)
            
        //2.存储session作用域
        形参定义HttpSession
        
        //3.如果想使用respones application...
        形参定义HttpServletResponse ServletContext springmvc帮你自动赋值
      • 跳转

        java 复制代码
        //1.想转发跳转 --- 比较常用
        a.方法返回值定义String  return"/xx.jsp"
        b.方法返回值ModelAndView  往View里面存储前端页面 也是转发
        
        //2.想重定向跳转 --- 看需求
        方法返回值定义String
        return "forward:/xxx.jsp" //默认(转发)
        return "redirect:/xxx.jsp" //重定向
  • 前后端分离:前端代码是一个项目,后端代码也是一个项目,存在跨域问题

4.SpringMVC文件上传和下载

  • 文件上传:需要满足很多前提

    • 前端

      • 请求方式只能是post<form method="post></form>"
      • 表单数据的传递方式(只能是附件形式提交)<form enctype=""></form> (application/x-www-form-urlencoded 默认值,按照字符串提交;multipart/form-data,表示附件形式提交,文件是附件,其他数据还是字符串提交)
    • 后端

      • SpringMVC配置文件,配置上传组件对象(允许接收文件)

      • 方法的形参MultipartFile用于接收文件,形参名和提交name值一致

        bug:上传组件name唯独不能和对象的属性名一致(字符串属性(存储路径)是不能接收文件的)否则400错误

  • 上传和下载工具类

    java 复制代码
    //文件上传和下载的工具类
    //只适用于SSM不适用于SpringBoot(底层是内置Tomcat)
    public class UpDowns {
        //上传
        public static String upload(HttpServletRequest req, MultipartFile myHead){
            //文件上传:文件不会存储数据库,一般会存在文件服务器或者云服务器
            //也可以存储到本地磁盘(本地磁盘的项目的服务器位置)
            //数据库只需要存储地址,这样前端就可以通过地址访问服务器存储的哪个图片
    
            //1.先获取项目在服务器的真实路径+/upload
            //D:\IdeaProjects\sc250601\sc250601\springmvc\target\springmvc\ upload
            String path = req.getServletContext().getRealPath("/upload/");
            File file = new File(path);
            if (!file.exists()) {
                file.mkdirs();
            }
    
            //2.保存的文件名一定不是你上传的文件名(可能会有重名) 文件名需要随机处理 后缀名需要保留
            String fileName = myHead.getOriginalFilename();
            //如果前端不传文件
            if ("".equals(fileName)) return null;
            String suffix = fileName.substring(fileName.lastIndexOf("."));
            //随机文件名 通过UUID 它可以帮你生成32位永不重复的字符串
            String name = UUID.randomUUID().toString();
            String newFileName = name + suffix;
            File newFile = new File(path+newFileName);
            //开始上传文件
            try {
                myHead.transferTo(newFile);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return newFileName;
        }
    }

5.视图解析器viewResolver

如果将页面放入WEB-INF,最终只能通过服务器转发访问,就会出现很多冗余代码(相同的地址),视图解析器可以将这些地址的相同的前缀和后缀统一处理,这样以后跳转的每个地址,都会自动添加前缀和后缀

java 复制代码
//web-inf不能对外共享 只能通过服务器转发访问
@RequestMapping("/toLogin")
public String toLogin() {
    //前缀:/WEB-INF/jsp   后缀:.jsp
    return "/WEB-INF/jsp/index/login.jsp";
}
@RequestMapping("/toReg")
public String toReg() {
    return "/WEB-INF/jsp/index/reg.jsp";
}
  • 配置方式:只需要在SpringMVC配置文件添加视图解析器组件

    xml 复制代码
    <!--5.配置视图解析器组件-->
        <!--class是可以变的  不同的前端资源要使用不同的视图解析器 目前InternalResourceViewResolver只能解析jsp-->
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--添加前缀-->
            <property name="prefix" value="/WEB-INF/jsp"/>
            <!--添加后缀-->
            <property name="suffix" value=".jsp"/>
        </bean>
  • bug:如果添加了视图解析器,返回的所有地址都默认添加了前缀和后缀,肯定是有地址是不需要添加的

    java 复制代码
    //只要添加forward或者redirect 不会走视图解析器 也不会添加前缀和后缀
    return "forward:/地址";
    return "redirect:/地址";

6.分页技术

分页是一种将所有数据分段展示给用户的一种技术,用户看到的并不是全部的数据,而是其中一部分,用户就可以通过指定的页码数来切换可见的内容,类似于阅读书籍

6.1 分页的步骤
  • 会编写MySQL分页的语句

    sql 复制代码
    -- 每页五条数据 查询第三页
    select * from 表 limit 10,5;
    -- 查询第n页的数据
    select * from 表 limit (n-1)*每页条数,每页条数;
  • 封装分页需要的信息(工具类/分页插件PageHelper)

    java 复制代码
    当前页数
    每页条数
    总条数
    总页数
    每页数据集合
    ... 可选的
    是否是上一页 下一页
    导航页码数 1 2 3 4 5
  • 封装好的信息传递(作用域)给前端

    • service

      java 复制代码
      @Override
      public PageInfo<OAdmin> select(Integer pageNum,Integer pageSize) {
      	PageInfo<OAdmin> p=new PageInfo<>();
      	//存储5个值
      	p.setPageNum(pageNum);
      	p.setPageSize(pageSize);
      	p.setCount(ad.selectCount());//dao + 内部计算总页数
      	p.setList(ad.select(pageNum,pageSize));//dao
      	return p;
      }
    • controller

      java 复制代码
      @RequestMapping("/select")
      public String select(HttpSession session, HttpServletRequest req, Integer pageNum, Integer pageSize) {
      	if (pageNum == null) pageNum = 1;
      	//如果传递了pageSize 存储session
      	//如果没有传递pageSize 获取session的值,如果有 使用
      	//如果没有传递pageSize 获取session的值,如果没有 默认值
      	if (pageSize == null) {
      		pageSize = (Integer) session.getAttribute("pageSize");
      	if (pageSize == null) pageSize = 3;
      	}else {
      		session.setAttribute("pageSize", pageSize);
      	}
      	PageInfo<OAdmin> pi = as.select(pageNum,pageSize);
      	req.setAttribute("p", pi);
      	return "/user/list";
      }
    • list.jsp

      jsp 复制代码
      <body>
      <form name="myForm" method="post">
      <button type="button" onclick="add()">新增</button>
      <button type="button" onclick="batchDelete()">批量删除</button>
      <table border="1" cellspacing="0" cellpadding="10">
        <tr>
          <th><input type="checkbox" id="all" onclick="checkAll()">全选</th>
          <th>编号</th><th>账号</th><th>密码</th><th>姓名</th><th>手机</th><th>邮件</th>
          <th>状态</th><th>时间</th><th>性别</th><th>角色</th><th>头像</th><th>操作</th>
        </tr>
        <c:forEach var="a" items="${p.list}">
          <tr>
            <td><input type="checkbox" name="choose" value="${a.id}"></td>
            <td>${a.id}</td>
            <td>${a.account}</td>
            <td>${a.password}</td>
            <td>${a.name}</td>
            <td>${a.phone}</td>
            <td>${a.email}</td>
            <%--<td>${a.status == "0" ? "未验证":a.status == "1" ? "正常":"禁用"}</td>--%>
            <td>
              <c:if test="${a.status==0}"><span class="warn">未验证</span></c:if>
              <c:if test="${a.status==1}"><span class="success">正常</span></c:if>
              <c:if test="${a.status==2}"><span class="error">禁用</span></c:if>
            </td>
            <td>${a.createtime}</td>
            <td>${a.sex == "1" ? "男":"女"}</td>
            <td>${a.roleid}</td>
            <td><img class="img" src="/upload/${a.headPic}"></td>
            <td>
              <a href="#">修改</a>
              <a href="/delete?id=${a.id}">删除</a>
              <a href="/download?filename=${a.headPic}">下载</a>
            </td>
          </tr>
        </c:forEach>
        </table>
        <br>
        <span>
          [当前页数:${p.pageNum}/总页数:${p.pages}]
        </span>
        <a href="/select?pageNum=1">首页</a>
        <c:if test="${p.pageNum > 1}">
          <a href="/select?pageNum=${p.pageNum-1}">上一页</a>
        </c:if>
        <span class="nums">
          <c:forEach var="i" begin="1" end="${p.pages}" step="1">
            <a <c:if test="${p.pageNum==i}">class="bg"</c:if> href="/select?pageNum=${i}">${i}</a>
          </c:forEach>
        </span>
        <c:if test="${p.pageNum < p.pages}">
          <a href="/select?pageNum=${p.pageNum+1}">下一页</a>
        </c:if>
        <a href="/select?pageNum=${p.pages}">尾页</a>
        <input size="1" id="page"><button type="button" onclick="clickPage()">跳转</button>
        <select onchange="changes(this)">
         <%-- <option <c:if test="${p.pageSize==3}">selected</c:if> >3</option>
          <option <c:if test="${p.pageSize==6}">selected</c:if> >3</option>
          <option <c:if test="${p.pageSize==9}">selected</c:if> >3</option>--%>
          <c:forEach var="i" begin="3" end="15" step="3">
            <option <c:if test="${p.pageSize==i}">selected</c:if> >${i}</option>
          </c:forEach>
        </select>
      </form>
      </body>
      </html>
      <script>
        function add(){
          document.myForm.action = "/toAdd";
          document.myForm.submit();
        }
        function batchDelete(){
          document.myForm.action = "/batchDelete"
          document.myForm.submit();
        }
        function checkAll(){
          var all = document.getElementById("all");
          var chooses = document.getElementsByName("choose");
          for (var i = 0; i < chooses.length; i++){
            chooses[i].checked = all.checked;
          }
        }
        function clickPage(){
          var num = document.getElementById("page").value;
          if (isNaN(num) || num < 1) num = 1;
          if (num > ${p.pages}) num = ${p.pages};
          location.href="/select?pageNum="+num;
        }
        function changes(a){
          location.href="/select?pageSize="+a.value;
        }
      </script>

7.Spring MVC拦截器Interceptor --- 面试题

springmvc拦截器依赖于springmvc框架,类似于Servlet中的过滤器,底层都是通过反射来实例化对象,但是功能实现是通过JDK动态代理实现的,属于面向切面编程的重要的应用(AOP),拦截器主要用于拦截进入控制层的请求,而且在一次控制层的生命周期过程中会拦截多次

面试题:什么是拦截器?

  • 是一种动态拦截方法调用的机制,类似于过滤器。
  • 拦截器依赖于spring MVC框架,用来动态拦截进入控制层的请求,并且在一次控制层的生命周期过程中会拦截多次。
  • 拦截器的作用:拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码。
7.1使用方式
  • 实现一个HandlerInterceptor接口

  • 重写三个方法

  • 配置springmvc配置文件,添加拦截器组件(1.让拦截器生效;2.配置哪些请求拦截哪些请求不拦截)

    xml 复制代码
    <!--6.springmvc拦截器-->
        <mvc:interceptors>
            <!--一个拦截器-->
            <mvc:interceptor>
                <!--哪些需要拦截 先配置拦截的再配置放行的才有效-->
                <mvc:mapping path="/**"/>
                <!--哪些需要放行-->
                <mvc:exclude-mapping path="/toReg"/>
                <!--<mvc:exclude-mapping path="/to**"/>-->
                <mvc:exclude-mapping path="/toLogin"/>
                <mvc:exclude-mapping path="/reg"/>
                <mvc:exclude-mapping path="/login"/>
                <!--拦截器的实现类-->
                <bean id="login" class="com.sc.interceptor.LoginInterceptor">
    
                </bean>
            </mvc:interceptor>
        </mvc:interceptors>

8.springmvc工作流程 --- 面试题

  • 前后端不分离:

  • 前后端分离:

8.1 工作流程的几个重要的组件
  • DispatcherServlet:核心控制器,最核心的组件,所有请求都必须经过它才能到达springmvc,而且其他组件也需要它进行流程控制
  • HanderMapping:请求映射器,保存了曾经在@RequestMapping,@GetMapping里面配置过的请求地址,用于和用户发送的请求进行一一对应,如果有对应的请求进行下一步,如果没有返回404
  • HanderAdapter:一个代理对象,用于动态的调用上面的请求地址对应的哪个Controller的哪个Method(方法)
  • Controller:控制层,执行控制层里面的方法,返回的是ModelAndView对象,如果是前后端分离的则返回JSON
  • ViewResover:视图解析器,用于解析ModelAndView对象,是为了解析成哪个Model(数据)对应哪个View(视图)
8.2 工作流程 --- 面试题
  • 前后端不分离

    1.用户发送请求:客户端向服务器端发送HTTP请求

    2.请求先到核心控制器(DispatcherServlet)

    3.处理器映射(HandlerMapping) : DispatcherServlet 调用 HandlerMapping ,根据请求 URL 查找对应的处理器和拦截器链

    4.处理器适配(HandlerAdapter) : DispatcherServlet 根据找到的处理器类型,选择合适的 HandlerAdapter 来执行处理器

    【5.执行拦截器的 preHandle 方法 :在处理器执行前,依次执行拦截器链中所有拦截器的 preHandle 方法(若返回 false,则中断后续流程)】

    6.执行控制器(Controller):HandlerAdapter 调用具体的控制器方法,处理业务逻辑并返回ModelAndView对象

    【7.执行拦截器的 postHandle 方法 :在控制器执行后、视图渲染前,依次执行拦截器链中所有拦截器的 postHandle 方法】

    8.视图解析(ViewResolver) : DispatcherServlet 调用 ViewResolver ,将逻辑视图名解析为物理视图对象

    9.视图渲染 :视图对象渲染模型数据,生成最终的 HTML 响应

    【10.执行拦截器的 afterCompletion 方法 :在视图渲染完成后,依次执行拦截器链中所有拦截器的 afterCompletion 方法(无论请求处理是否成功都会执行)】

    11.响应客户端 : DispatcherServlet 将渲染后的响应返回给客户端

  • 前后端分离

    1.请求先到核心控制器(DispatcherServlet)

    2.核心控制器查询请求映射器(HandlerMapping)来看请求是否存在,如果不存在,返回404,如果存在返回执行链

    3.核心控制器在通过代理对象(HandlerAdapter)动态调用Controller中对应的Method(方法)

    4.控制层(Controller)开始执行方法,处理请求,返回的是JSON对象给核心控制器

    5.通过核心控制器直接将JSON数据返回给前端

9.Spring MVC常见面试题

9.1谈谈你对Spring MVC的理解?

Spring MVC是Spring的一个子项目,是实现了MVC模式的请求驱动类的轻量级Web框架,最核心的组件是DispacherServlet,通过把Model-View-Controller分离,将Web层进行解耦合,可以简化开发,减少出错,相比于Servlet效率和性能更好

9.2拦截器interceptor和过滤器filter的区别
  • 依赖的框架:filter过滤器依赖于Servlet;拦截器依赖于Spring MVC框架
  • 拦截的资源:过滤器几乎可以拦截所有的资源(包括发送的控制层的请求,前端访问页面,静态资源...);拦截器只能拦截进入控制层的请求
  • 拦截次数:过滤器在一次请求之内之内拦截一次;拦截器可以在一次请求之内拦截多次(执行方法前,执行方法途中,执行方法结束返回)
  • 拦截规则:过滤器不能控制拦截规则(哪些拦截,哪些不能拦截);拦截器可以控制拦截规则
  • 功能不同:过滤器主要用于对请求进行预处理和过滤,例如设置字符集、登录验证等;拦截器则主要用于对请求进行流程控制,例如权限验证
  • 触发时机也不同:请求的执行顺序是:请求进入容器 > 进入过滤器 > 进入 Servlet > 进入拦截器 > 执行控制器,所以过滤器会先执行,然后才会执行拦截器
9.3 Spring MVC有哪些常用的注解?
java 复制代码
@Conntroller @RestController
@RequestMapping  @GetMapping @PostMapping
@RequestBody
@ResponseBody
@DateTimeFormat
@JsonFormat
@RequestParam
  • @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
  • @RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
  • @ResponseBody:注解实现表示返回值是响应结果不是地址,如果返回的是对象,Spring MVC会自动将其转换成JSON返回
  • @Conntroller:控制器的注解,表示是控制层,负责处理由DispatcherServlet 分发的请求
  • @JsonFormat:后端的日期对象转换成json格式时,如果不添加此注解,默认会转换成毫秒
  • @DateTimeFormat:将前端提交的字符串按指定格式转换成后端的日期,否则报400
    5.Spring和Spring MVC的关系?

10.js验证

通过JavaScript在用户提交数据时(form),进行一些数据校验,如果数据合法才可以提交数据,反之无法提交,这样到达服务器的数据都是合法的,服务器也无需再对数据做二次验证,起到了减轻服务器的压力

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册</title>
</head>
<body>
<%--onsubmit: 表单提交事件,只要提交表单就会触发该事件,onsubmit就会根据里面结果的返回值
    return true   提交
    return false  不提交
--%>
<div id="error" style="color: red"></div>
<form onsubmit="return check()" action="/reg" method="post" enctype="multipart/form-data">
    <p>账号:<input name="account" placeholder="请输入账号"></p>
    <p>密码:<input name="password" placeholder="请输入密码" type="password"></p>
    <p>确认密码:<input name="password2" placeholder="请再次输入密码" type="password"></p>
    <p>姓名:<input name="name" placeholder="请输入姓名"></p>
    <p>手机:<input name="phone" placeholder="请输入手机号"></p>
    <p>邮件:<input name="email" placeholder="请输入邮箱" type="email"></p>
    <p>状态:
        <input name="status" type="radio" value="0">未验证
        <input name="status" type="radio" value="1" checked>正常
        <input name="status" type="radio" value="2">禁用
    </p>
    <p>性别:
        <input type="radio" name="sex" value="1" checked>男
        <input type="radio" name="sex" value="0">女
    </p>
    <p>爱好:
        <input type="checkbox" name="like" value="学习">学习
        <input type="checkbox" name="like" value="看书">看书
        <input type="checkbox" name="like" value="玩游戏">玩游戏
        <input type="checkbox" name="like" value="游泳">游泳
    </p>
    <%--onchange:域改变事件 通常用于表单元素中--%>
    <p>头像:<input type="file" name="myHead" onchange="showImg(this)"></p>
    <p><img id="img" src="" style="width: 100px;border-radius: 100px;height: 100px"></p>
    <button>注册</button>
</form>
</body>
</html>
<script>
    /*选择图片后立即展示*/
    function showImg(o){
        /*获取上传组件的文件对象*/
        var imgFile = o.files[0];
        /*通过文件对象获取本地的虚拟路径*/
        var src = window.URL.createObjectURL(imgFile);
        /*把地址给img标签 src属性赋值*/
        document.getElementById("img").setAttribute("src",src);
    }

    // 验证表单所有数据的合法性,不合法返回false
    function check(){
        //1.获取需要验证的表单组件对象对应的数据
        var unValue = document.getElementsByName("account")[0].value;
        var psValue = document.getElementsByName("password")[0].value;
        var psValue2 = document.getElementsByName("password2")[0].value;
        var nValue = document.getElementsByName("name")[0].value;
        var pValue = document.getElementsByName("phone")[0].value;
        var eValue = document.getElementsByName("email")[0].value;
        var likes = document.getElementsByName("like");

        //2.获取显示错误信息的组件对象
        var div = document.getElementById("error");

        //3.验证数据合法性
        if (unValue === ""){
            div.innerHTML="账号不能为空";
            return false;
        }
        //账号只能由汉字 字母 数字 _ 构成... (正则表达式)
        var unReg = /^[\u4e00-\u9fa5_a-zA-Z0-9]+$/;
        if (!unReg.test(unValue)){
            div.innerHTML="账号只能由汉字,字母,数字,_构成";
            return false;
        }
        //账号肯定是唯一的 (ajax异步请求)
		if (isSameName){
            return false;
        }
        if(psValue === "" || psValue2 === ""){
            div.innerHTML="密码不能为空";
            return false;
        }
        if (psValue !== psValue2){
            div.innerHTML="两次密码输入不一致!";
            return false;
        }
        if (nValue === ""){
            div.innerHTML="姓名不能为空";
            return false;
        }
        //姓名一般是由汉字或者字母构成(正则表达式)
        var nameReg = /^[\u4e00-\u9fa5]+$/;
        if (!nameReg.test(nValue)){
            div.innerHTML="姓名一般是由汉字构成";
            return false;
        }

        if(pValue === ""){
            div.innerHTML="手机号不能为空";
            return false;
        }
        //手机号11为构成 为 1 开头的11位(正则表达式)
        var phoneReg = /^1[3-9]\d{9}$/;
        if (!phoneReg.test(pValue)){
            div.innerHTML="手机号只能是由11位构成,为1开头的11位数字";
            return false;
        }

        if (eValue === ""){
            div.innerHTML="邮件不能为空";
            return false;
        }
        //邮件必须包含@ .cn .com...(正则表达式)
        var emailReg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
        if (!emailReg.test(eValue)){
            div.innerHTML="邮件必须包含@ .cn .com...";
            return false;
        }

        //爱好复选框 不能为空
        if (!checkLike(likes)){
            div.innerHTML="爱好不能为空!"
            return false;
        }
        return true
    }
    //专门用于验证复选框是否选中的 参数是复选框对象
    //如果选中了,返回true 一个没有选中 返回false;
    function checkLike(likes) {
        for (var i = 0; i < likes.length; i++){
            if (likes[i].checked) return true
        }
        return false;
    }
</script>

11.Ajax --- 重点

Ajax: async(异步的)JavaScript and xml,是一种在不加载整个浏览器的情况下,就可以跟服务器进行交互,并更新部分页面内容的技术,也称之为局部刷新技术,它就可以使页面异步的加载数据,不会影响用户操作体验,提供更好的流畅的效果

注:ajax必须依赖于JS才可以发送异步请求

11.1 Ajax异步请求优势
  • 提高页面性能:Ajax可以让页面并行加载数据和样式,提高页面加载的速度和性能

  • 减少带宽消耗:由于只更新局部内容,不是更新整个浏览器,Ajax可以减少传输的数据量,从而降低了带宽的消耗

  • 提高用户体验:Ajax实现不刷新浏览器进行服务器的交互,这样对于用户正在操作的功能可以不受到影响(一边看直播一边发评论,验证用户名可用)

    目前:Ajax异步请求经常用于实现前后端分离的项目

    vscode(vue)-->发送异步请求-->idea(springboot)

11.2 Ajax异步请求的实现方式
  • 原生Ajax实现:步骤繁琐,不推荐使用

    js 复制代码
    1.创建Ajax对象  XMLHttpRequest
    2.为Ajax对象绑定监听(绑定什么时候发送成功,什么时候失败..做回调函数(发送请求成功后的函数))
    3.为Ajax对象绑定地址
    4.发送异步请求
    5.接收响应的数据(在绑定监听里面的不同的请求状态里面编写的)
    
    -----代码实现
    //原生js实现Ajax:验证用户名是否可用
    function ajax1(name){
        //1.创建Ajax对象
        var ajax = new XMLHttpRequest();
        //2.绑定监听
        //function (){} 没有函数名的函数叫匿名函数,等价于()==>{}
        //ajax.onreadystatechange = function (){}
        ajax.onreadystatechange = ()=>{
            //readyState用于表示连接状态
            //0 未连接  1 打开连接  2 发送请求  3 交互  4 交互完成可以接收响应
            //status表示状态码  200请求成功
            if (ajax.readyState === 4 && ajax.status === 200){
                //5.请求执行成功,可以接收响应
                var error = ajax.responseText; //用于获取后端的返回数据
                document.getElementById("error").innerHTML=error;
            }
        }
        //3.绑定地址
        //ajax.open("请求方式","请求地址",是否是异步请求(默认是true是异步请求))
        ajax.open("get","/checkName?name="+name,true);
        //4.发送请求
        ajax.send();
    }
  • jQuery封装好的方法来发送异步请求:数据传递时,默认格式不是JSON,不适合前后端分离的项目

    js 复制代码
    1.前提:先导入jQuery文件
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    2.通过$.方法(),发送异步请求
    $("#id") === jQuery("#id"),例如:$.ajax()  $.get()  $.post()...
    
    ----代码实现
    //利用jQuery实现异步
    function ajax2(name){
        //请求地址, 传递的json参数, 回调函数(请求成功之后调用的函数)
        $.post('/checkName?name='+name,(res)=>{
            //res就是后端返回的结果
            $("#error").html(res);
        });
    }
  • axios发送异步请求:目前最主流的方式,非常适合前后端分离的技术,默认交互格式就是JSON

    js 复制代码
    1.前提:导入axios环境
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    后期创建vue项目可以安装axios依赖
    2.通过axios.post()  axios.get()
    
    ----代码实现
      //利用axios实现异步请求
      function ajax3(name){
          //axios.post("请求地址","传递的JSON数据").then((res)=>{
          //res.data 后端返回的结果
          //})
          axios.post('/checkName?name='+name).then((res)=>{
              var result = res.data;
              //$("#error").html(res.data);
              if (result === "账号已存在") {
                  isSameName = true;
                  $("#error").html(result);
              }
              else {
                  isSameName = false;
                  $("#error").css("color","green").html(result);
              }
          });
      }
      //js使用var定义变量属于全局变量,每个函数都可以使用
      var isSameName;
11.3 同步请求和异步请求区别 --- 面试题
  • 同步请求:发送请求后需要等待服务器响应,这个期间会被阻塞,只有等待响应之后,上面的代码全部执行完了,才可以执行后续的内容(类似于单线程执行)

    • 应用场景:适用于需要立即响应结果的场景,并且后续的操作需要依赖于这个结果,比如:登录后才知道访问哪个页面,前后不分离,存储作用域,如果没有存储完,不能够展示数据。
  • 异步请求:发送请求后不需要等待服务器响应,就可以执行后续代码,最后什么时候响应,再通过回调函数去更新对应的数据(类似于多线程)

    • 应用场景:适用于不需要立即响应的场景,比如:订单支付(不支付也不影响去浏览其他商品,支付成功了才会发货),视频播放,点赞,评论。前后端分离的项目全是异步请求,RabbitMQ异步请求

12.JSON --- 重点

JSON是一种轻量级的数据交互格式,本质上其实就是一个满足特定格式的字符串,可以描述任意数据,具有良好的扩展性,体积小,传输效率高,易于解析,目前在前后端分离的情况下被广泛使用

12.1 JSON语法

JSON也是类似于Map集合,基于key和value来组装数据

  • key和value之间通过 ":" 隔开,多组key和value通过 "," 隔开

  • key类似于一个唯一标识,只要不重名可以任意写,一般用于表示属性名比较多(key双引号可以加也可以不加)

  • value类似于对象中的属性值,可以描述任意数据

    • value存储整型:key:10
    • value存储字符串:key:"内容"
    • value存储布尔类型:key:true
    • value存储控制:key:null
    • value存储对象:key:{key1:value1,key2:value2}
    • value存储集合或者数组(字符串):key:["1","2","3"]
    • value存储集合或者数组(对象):key:[{key:value,key2,value2},{key:value,key2,value2}]
  • 代码案例

    jsp 复制代码
    <script>
        //通过JSON表示一个用户对象(id,name,sex)
        var user = {
          id : 10,
          name : "张三",
          sex : "1"
        };
        //浏览器控制台打印数据 --- 比较多
        console.log(user.id+" "+user.name+" "+user.sex);
        console.log(user.id,user.name,user.sex);
        //在浏览器打印数据 缺点不能换行
        document.write(user.id+" "+user.name+" "+user.sex+"<br>");
      
        //通过json描述一个班级对象(id,classname,用户集合)
        var classes = {
          id : 250601,
          classname : "sc250601",
          users : [
            {id : 10, name : "张三", sex : "1"},
            {id : 20, name : "李四", sex : "1"},
            {id : 30, name : "王五", sex : "0"}
          ]
        };
        console.log(classes.id,classes.classname);
        for (var i=0; i < classes.users.length; i++){
          //classes.users[i]
          var u = classes.users[i];
          console.log(u.id,u.name,u.sex);
        }
      
        //通过json描述一个员工对象(id,name,员工信息对象)
        //info信息对象(id,age,sex,time)
        var emp = {
          id : 1,
          name : "张三",
          info : {
            id : 1,
            age : 18,
            sex : "1",
            time : new Date()
          }
        };
        console.log(emp.id,emp.name,emp.info.id,emp.info.age,emp.info.sex,emp.info.time);
      </script>
        //通过json描述一个班级对象(id,classname,用户集合)
        var classes = {
          id : 250601,
          classname : "sc250601",
          users : [
            {id : 10, name : "张三", sex : "1"},
            {id : 20, name : "李四", sex : "1"},
            {id : 30, name : "王五", sex : "0"}
          ]
        };
        console.log(classes.id,classes.classname);
        for (var i=0; i < classes.users.length; i++){
          //classes.users[i]
          var u = classes.users[i];
          console.log(u.id,u.name,u.sex);
        }
      
        //通过json描述一个员工对象(id,name,员工信息对象)
        //info信息对象(id,age,sex,time)
        var emp = {
          id : 1,
          name : "张三",
          info : {
            id : 1,
            age : 18,
            sex : "1",
            time : new Date()
          }
        };
        console.log(emp.id,emp.name,emp.info.id,emp.info.age,emp.info.sex,emp.info.time);
      </script>
相关推荐
2021_fc3 小时前
StarRocks技术分享
数据仓库
杂家18 小时前
Hadoop完全分布式部署(超详细)
大数据·hadoop·分布式
BD_Marathon18 小时前
【Hadoop】hadoop3.3.1完全分布式配置
大数据·hadoop·分布式
Q26433650231 天前
【有源码】基于Hadoop+Spark的起点小说网大数据可视化分析系统-基于Python大数据生态的网络文学数据挖掘与可视化系统
大数据·hadoop·python·信息可视化·数据分析·spark·毕业设计
yumgpkpm1 天前
CMP(类Cloudera CDP 7.3 404版华为泰山Kunpeng)和Apache Doris的对比
大数据·hive·hadoop·spark·apache·hbase·cloudera
呆呆小金人2 天前
SQL字段对齐:性能优化与数据准确的关键
大数据·数据仓库·sql·数据库开发·etl·etl工程师
口_天_光健2 天前
制造企业的数据目录编写
大数据·数据库·数据仓库·数据分析
梦里不知身是客113 天前
spark读取table中的数据【hive】
大数据·hive·spark
DashVector3 天前
向量检索服务 DashVector产品计费
数据库·数据仓库·人工智能·算法·向量检索