java--ajax技术和json的介绍及应用(自动补全、省级联动)

Ajax的定义

ajax是多种技术的统称,它向服务器发送请求的一种手段,但发送请求时不会导致页面刷新或跳转。

 a - asynchronous - 可以通过 xhr 对象发送异步请求
 j - javascript 
 a - and
 x - xml - 指响应格式 ,后来应用主流是json,但是名字已经定下,这里的x'也可以说是xml和json

XMLHttpRequest 对象的基本使用

  • 创建 xhr 对象 

         var xhr = new XMLHttpRequest();

  •  发送请求

         xhr.open("get|post", 请求地址, true|false); // 发请求前准备
         xhr.send(); // 真正发送请求

         open的第一个参数

          post 请求 

          1、get 请求只有请求行和请求头
          2、post 请求有行、请求头、请求体,下面是post发送请求实例

          var xhr = new XMLHttpRequest();
          xhr.onload = function(){};

          // 发请求
          xhr.open("post", url, true);
          xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded"); // 告诉服务器,请求体的格式是:表单格式
          xhr.send("请求体内容"); // 地址?参数名=参数值&参数名=参数值...

         open的第三个参数

         true 默认值 -- 异步请求, send 方法不会阻塞,页面其他代码,视频都不会等待响应,继续执行
         false -- 同步请求 响应没有返回之前,页面代码、视频都会暂停,直到响应返回为止,send方法在此期间一直处于阻塞状态

        异步请求下需要使用事件的机制来接收响应
        // (新)响应返回会触发 onload事件,执行事件对应的函数
            xhr.onload = function () {

            };

           // (旧)发送请求时会触发, 响应返回时会触发,  响应完全返回时会触发
              xhr.onreadystatechange = function() {
                    //xhr.readyState // 状态 
                   //以4秒触发为例, xhr 创建时 0
                  // xhr.send 0-->1
                 // xhr. 1--> 2  2-->3
                // 3--> 4 表示响应完全返回
                  if(xhr.readyState == 4) {

                  }
              }  

  •  接收响应

         xhr.responseText       需要的响应通常不是一个完整的html, 而是一个html片段,或是一个字符串

代码演示

模拟弹幕,发文字 ,视频不会暂停(也就是不会刷新视频)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script>
        function sendRequest() {
            // 1.创建 xhr 对象 
            var xhr = new XMLHttpRequest();

            // 2. 定义 onload 事件
            xhr.onload = function() {
                // 5. 处理服务器端的响应
                document.getElementById("result").innerText = xhr.responseText;
            };
          
            //得到id="c" 这个文本框的值
            var v = document.getElementById("c").value;
            // 3. 发送请求,把文本框的值当做参数传给页面,以便页面可以拿到该值,并显示出来
            xhr.open("get", "/commentServlet?comment=" + v, true);
            xhr.send();
            
        }
            //以上的全部可以写成以下
            /*function sendRequest() {
            // url 请求地址 例如:/commentServlet
            // param 请求参数 例如: "comment=" + document.getElementById("c").value
            // callback 回调函数 在响应返回时被调用

            post("/commentServlet", "comment=" + document.getElementById("c").value, function (x) {
                document.getElementById("result").innerText = x.responseText;
            });
        }*/
    </script>
</head>
<body>
    <video src="1.mp4" controls width="500"></video>

    <hr>
        请输入评论:<input type="text" name="comment" id="c">
        <<%--input type="submit" value="提交评论">--%>
        <input type="button" value="用xhr发送请求" onclick="sendRequest()">
    <hr>

    <span id="result"></span>
</body>
</html>
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/commentServlet")
public class CommentServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 4.
        req.setCharacterEncoding("utf-8");
        //在服务器输出输入的文字内容
        String comment = req.getParameter("comment");
        System.out.println("comment:" + comment);


        // 返回响应
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print(comment); // 在浏览器上输出输入的内容
    }
}

结果

响应格式

主流格式有两种:

  •  xml (可扩展标记语言)

       <response>
          <time>2019-1-19 11:43:33</time>
          <author>张三</author>
          <content>还不错</content>
          <score>11</score>
       </response>
       var xml = xhr.responseXML;
       xml.getElementsByTagName("time")[0].innerText;//time表示response里的所有标签,拿到0位置的数据,也就是time标签里的值

  •  json (javascript object 对象 notation 标记)

        {
             "time":"2019-1-19 11:43:33",
             "author":"张三",
             "content": "还不错",
             "score": 11
         }
          var obj = xhr.responseXML;
          obj.time   //表示拿到time的值

          json中除了解析方便外,还支持更多类型  字符串、数字、布尔值、数组、对象

json(jackson)

JSON 是存储和交换文本信息的语法,类似 XML,但JSON 比 XML 更小、更快,更易解析。

1. 把 java 对象转换为 json 字符串, 或反之则转换为java,转换json的第三方 api, gson(谷歌),jackson(spring),fastjson(阿里)
2. js 对象和 json 字符串相互转换
    var js对象 = JSON.parse(json字符串);
    var json字符串 = JSON.stringify(js对象);

3、如果要对日期进行控制,在日期属性上添加注解: @JsonFormat(pattern="日期格式", timezone="GMT+08") 

timezone表示时区,GMT加上8刚好是中国时间

4、如果要忽略某个属性:在属性上添加一个 @JsonIgnore 注解

5、如果要转换后改变属性名:@JsonProperty("新属性名")

json重要的类

    ObjectMapper om = new ObjectMapper();
    String json = om.writeValueAsString(java对象);//把java对象转变为json

还原操作

ObjectMapper om = new ObjectMapper();
类型 java对象 = om.readValue(json字符串, 类.class);//把json转变为java对象

代码演示

1、json操作集合,无需遍历,代码简洁

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Date;

public class Student {

    public Student(int id, String password, String name, String sex, Date birthday) {
        this.id = id;
        this.password = password;
        this.name = name;
        this.sex = sex;
        this.birthday = birthday;
    }

    public Student() {
    }

    private int id;
    //用该注解可以忽略大小写
    @JsonIgnore
    private String password;
    //用该注解可以把声明的属性,序列化为括号里的名字,也就是序列化
    @JsonProperty("username")
    private String name;

    private String sex;
    //用该注解可以指定日期格式,
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08")
    private Date birthday;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", password='" + password + '\'' +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@WebServlet("/students")
public class StudentServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //指定响应的编码方式
        resp.setCharacterEncoding("utf-8");
        //指定响应的内容类型
        resp.setContentType("application/json;charset=utf-8");
        List<Student> list = new ArrayList<>();
        list.add(new Student(1, "123", "张三", "男", new Date()));
        list.add(new Student(2, "456", "李四", "男", new Date()));
        list.add(new Student(3, "456", "王五", "男", new Date()));
        list.add(new Student(4, "678", "赵六", "男", new Date()));

        ObjectMapper om = new ObjectMapper();


        //以下是浏览器输出的两种方式,这样方便了遍历
        // 方法1:
//        String json = om.writeValueAsString(list);
//        resp.getWriter().print(json);
        // 方法2:
        om.writerWithDefaultPrettyPrinter().writeValue(resp.getWriter(), list);
    }
}

结果

这里的集合是用【】括起来的,里面包含花括号的集合内容 

2、将json字符串转换为js字符串,并输出到console上

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

    <script>
        function getJsonData(){
            var xhr = new XMLHttpRequest();
            xhr.onload = function() {
                var json = xhr.responseText;
                // 使用 JSON.parse(json字符串) ==> 转换为 js 的对象或数组
                var array = JSON.parse(json);
                for(var i = 0; i < array.length; i++) {
                    //输出系列化注解的那个username
                    console.log(array[i].username);
                }
            };
            xhr.open("get", "/students", true);
            xhr.send();
        }
    </script>
</head>
<body>
    <input type="button" value="获取json结果" onclick="getJsonData()">
</body>
</html>

结果

servlet 中结合 jackson

1、自动补全

存储信息的代码

public class Region {
    private int regionId;

    private int regionCode;

    private String regionName;

    private int parentId;

    private String regionNameEn;

    public int getRegionId() {
        return regionId;
    }

    public void setRegionId(int regionId) {
        this.regionId = regionId;
    }

    public int getRegionCode() {
        return regionCode;
    }

    public void setRegionCode(int regionCode) {
        this.regionCode = regionCode;
    }

    public String getRegionName() {
        return regionName;
    }

    public void setRegionName(String regionName) {
        this.regionName = regionName;
    }

    public int getParentId() {
        return parentId;
    }

    public void setParentId(int parentId) {
        this.parentId = parentId;
    }

    public String getRegionNameEn() {
        return regionNameEn;
    }

    public void setRegionNameEn(String regionNameEn) {
        this.regionNameEn = regionNameEn;
    }
}

连接数据库

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * jdbc工具类
 * @author yihang
 */
public class JdbcUtil {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("加载驱动失败");
        }
    }

    /**
     * 获取数据库连接
     * @return 连接对象
     */
    public static Connection getConnection() {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/rbac", "root", "root");
            return conn;
        } catch (SQLException e) {
            System.out.println("获取连接失败" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭资源(包括ResultSet, Statement等)
     * @param resources 资源对象
     */
    public static void close(AutoCloseable... resources) {
        for (AutoCloseable resource : resources) {
            if(resource !=null) {
                try {
                    resource.close();
                } catch (Exception e) {
                    System.out.println("关闭资源失败" + e.getMessage());
                    throw new RuntimeException(e);
                }
            }
        }
    }

}

搜索信息的代码

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RegionDao {

    // 模糊查询地名
    public List<Region> findByName(String name) {
        String sql = "select * from region where region_name like ?";
        try(Connection conn = JdbcUtil.getConnection()) {
            try(PreparedStatement stmt = conn.prepareStatement(sql)){
                //                  name = 西
                stmt.setString(1, name + "%");
                ResultSet rs = stmt.executeQuery();
                List<Region> list = new ArrayList<>();
                while(rs.next()) {
                    Region region = new Region();
                    region.setRegionId(rs.getInt("region_id"));
                    region.setRegionCode(rs.getInt("region_code"));
                    region.setRegionName(rs.getString("region_name"));
                    region.setParentId(rs.getInt("parent_id"));
                    region.setRegionNameEn(rs.getString("region_name_en"));
                    list.add(region);
                }
                return list;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }
}

jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <style>
        li {
            list-style: none;

        }
        li:hover {
            background: blue;
            color:white;
        }
        ul {
            display: none;
            border: 1px solid rgb(238,238,238);
            margin: 0;
            padding: 0;
            width: 160px;
            box-shadow: 0px 1px 6px rgba(0,0,0,0.2);
            border-radius: 4px ;
            font-size: 13px;
        }
    </style>
</head>
<body>
    <%--根据输入的第一个字扩张出有关地名,失去焦点时隐藏菜单,单击扩展出来的地名,显示在文本框里,并且菜单隐藏,再点击文本框时,菜单有显示出来--%>
    <input type="text" id="name" onkeyup="findByName()" onblur="hideResult()" onclick="toggleResult()">
    <ul id="result"></ul>

    <script>
        //触发该函数单击时如果没有菜单则显示,否者则不显示
        function toggleResult() {
            if(document.getElementById("result").style.display == "none") {
                document.getElementById("result").style.display = "block";
            } else {
                document.getElementById("result").style.display = "none";
            }
        }
        //触发该函数隐藏菜单
        function hideResult() { // 14:19:00
            setTimeout(function(){
                document.getElementById("result").style.display = "none"; // 14:19:00:003
            }, 300);
        }
        //触发该函数找到扩展地名
        function findByName() {
            var xhr = new XMLHttpRequest();
            xhr.onload = function() {
                var json = xhr.responseText;
                var list = JSON.parse(json);
                document.getElementById("result").innerHTML="";
                for(let i = 0; i < list.length; i++) {
                    let li = document.createElement("li");
                    li.innerText = list[i].regionName;
                    // li 随着列表隐藏了起来
                    li.onclick = function(){
                        document.getElementById("name").value = li.innerText;
                        document.getElementById("result").style.display = "none";
                    };
                    document.getElementById("result").appendChild(li);
                }
                document.getElementById("result").style.display = "block";
            };
            var v = document.getElementById("name").value;
            if( v != "" ) {
                xhr.open("get", "http://localhost:8080/regionByName?name=" + v);
                xhr.send();
            }

        }
    </script>
</body>
</html>

servlet代码

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet(urlPatterns = "/regionByName")
public class RegionNameServlet extends HttpServlet {

    private RegionDao regionDao = new RegionDao();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获得参数name的值,也就是想要模糊查询的字
        String name = req.getParameter("name");
        //把上面拿到的字进行模糊查询
        List<Region> list = regionDao.findByName(name);
        //指定响应编码方式和内容类型
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("application/json;charset=utf-8");
        
        ObjectMapper om = new ObjectMapper();
        //把值的集合输出到浏览器上
        om.writeValue(resp.getWriter(), list);

    }
}

结果

2、省级联动(根据选出的省,市只能选省包含的市,县只能选市包含的县)
存储地名的信息的代码

public class Region {
    private int regionId;

    private int regionCode;

    private String regionName;

    private int parentId;

    private String regionNameEn;

    public int getRegionId() {
        return regionId;
    }

    public void setRegionId(int regionId) {
        this.regionId = regionId;
    }

    public int getRegionCode() {
        return regionCode;
    }

    public void setRegionCode(int regionCode) {
        this.regionCode = regionCode;
    }

    public String getRegionName() {
        return regionName;
    }

    public void setRegionName(String regionName) {
        this.regionName = regionName;
    }

    public int getParentId() {
        return parentId;
    }

    public void setParentId(int parentId) {
        this.parentId = parentId;
    }

    public String getRegionNameEn() {
        return regionNameEn;
    }

    public void setRegionNameEn(String regionNameEn) {
        this.regionNameEn = regionNameEn;
    }
}

连接数据库

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * jdbc工具类
 * @author yihang
 */
public class JdbcUtil {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("加载驱动失败");
        }
    }

    /**
     * 获取数据库连接
     * @return 连接对象
     */
    public static Connection getConnection() {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/rbac", "root", "root");
            return conn;
        } catch (SQLException e) {
            System.out.println("获取连接失败" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭资源(包括ResultSet, Statement等)
     * @param resources 资源对象
     */
    public static void close(AutoCloseable... resources) {
        for (AutoCloseable resource : resources) {
            if(resource !=null) {
                try {
                    resource.close();
                } catch (Exception e) {
                    System.out.println("关闭资源失败" + e.getMessage());
                    throw new RuntimeException(e);
                }
            }
        }
    }

}

根据查询id查询地名

import domain.Region;
import util.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RegionDao {

    public List<Region> findByParentId(int parentId) {
        String sql = "select * from region where parent_id = ?";
        try (Connection conn = JdbcUtil.getConnection()) {
            try(PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setInt(1, parentId);
                ResultSet rs = stmt.executeQuery();
                List<Region> list = new ArrayList<>();
                while(rs.next()) {
                    Region region = new Region();
                    region.setRegionId(rs.getInt("region_id"));
                    region.setRegionCode(rs.getInt("region_code"));
                    region.setRegionName(rs.getString("region_name"));
                    region.setParentId(rs.getInt("parent_id"));
                    region.setRegionNameEn(rs.getString("region_name_en"));
                    list.add(region);
                }
                return list;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }
}

jsp页面及联动代码

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

</head>
<body>
    省 <select id="sheng" onchange="findShi()"></select>
    市 <select id="shi" onchange="findxian()"></select>
    县 <select id="xian"></select>



    <script>
        //console.log(document.getElementById("sheng"));
        function findxian() {
            // 获取省当前选中的 option 的 value
            var parentId = document.getElementById("shi").value;
            var xhr = new XMLHttpRequest();
            xhr.onload = function () {
                var json = xhr.responseText;
                var list = JSON.parse(json);
                // 添加之前要清除下拉列表的内容
                document.getElementById("xian").innerHTML = "";
                // 添加新内容
                for(var i = 0 ; i < list.length; i++) {
                    var region = list[i];
                    // js 生成标签
                    var option = document.createElement("option"); // 创建<option></option>标签
                    option.value = region.regionId; // 给option的value赋值
                    option.innerText = region.regionName; // 给option的赋予内容
                    // 把它添加给 市 这个父元素
                    document.getElementById("xian").appendChild(option);
                }
            };
            xhr.open("get", "/region?parentId=" + parentId, true);
            xhr.send();
        }

        function findShi () {
            // 获取省当前选中的 option 的 value
            var parentId = document.getElementById("sheng").value;
            var xhr = new XMLHttpRequest();
            xhr.onload = function () {
                var json = xhr.responseText;
                var list = JSON.parse(json);
                // 添加之前要清除下拉列表的内容
                document.getElementById("shi").innerHTML = "";
                // 添加新内容
                for(var i = 0 ; i < list.length; i++) {
                    var region = list[i];
                    // js 生成标签
                    var option = document.createElement("option"); // 创建<option></option>标签
                    option.value = region.regionId; // 给option的value赋值
                    option.innerText = region.regionName; // 给option的赋予内容
                    // 把它添加给 市 这个父元素
                    document.getElementById("shi").appendChild(option);
                }
                // 联动
                findxian ();
            };
            xhr.open("get", "/region?parentId=" + parentId, true);
            xhr.send();
        }

        function findSheng() {
            var xhr = new XMLHttpRequest();
            xhr.onload = function() {
                var json = xhr.responseText;
                var list = JSON.parse(json);
                for(var i = 0 ; i < list.length; i++) {
                    var region = list[i];
                    // js 生成标签
                    var option = document.createElement("option"); // 创建<option></option>标签
                    option.value = region.regionId; // 给option的value赋值
                    option.innerText = region.regionName; // 给option的赋予内容
                    // 把它添加给 省 这个父元素
                    document.getElementById("sheng").appendChild(option);
                }
                // 联动
                findShi ();
            };
            xhr.open("get", "/region?parentId=1", true);
            xhr.send();
        }
        findSheng();
    </script>
</body>
</html>

servlet代码

@WebServlet("/region")
public class RegionServlet extends HttpServlet {
    private RegionDao regionDao = new RegionDao();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //得到发送过来的参数parentId的值
        String parentId = req.getParameter("parentId");
        //根据parentId查找子地名集合
        List<Region> list = regionDao.findByParentId(Integer.valueOf(parentId));


        resp.setCharacterEncoding("utf-8");
        resp.setContentType("application/json;charset=utf-8");

        ObjectMapper om = new ObjectMapper();
        //输出到浏览器上
        om.writeValue(resp.getWriter(), list);
    }
}

结果

总结

ajax可以做评论弹幕、图形验证码的验证、级联动(省级联动)等,这样不需要刷新便可以显示信息,更加人性化,体验感更好,因此做有关页面的项目时,不要忘记它,还有就是发送时有同步和异步两个模式,异步请求就是请求发送后,不必等待响应返回,接下来的代码可以继续执行,而同步需要等待结束,才能继续执行,把open里的true就是异步,false就同步,一般都是些异步。

猜你喜欢

转载自blog.csdn.net/grey_mouse/article/details/86573366