LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

JSON,JQuery,Ajax,Freemarker,百度Echarts,正则表达,过滤器,监听与实践

2023/9/20 后端

JSON (JavaScript Object Notation) [JavaScript对象表示法]

  • 掌握JSON语法的书写规则
  • 掌握JSON与JavaScript的交互技巧
  • 掌握JSON与Java之间的序列化与反序列化

JSON是轻量级的文本数据交换格式,独立于语言,具有自我描述性,更易理解,已经逐渐替代了XML

{
    "sites":[
        {"name":"慕课网","url":"www.imooc.com"},
        {"name":"百度","url":"www.baidu.com"}
    ]
}

JSON语法规则

  • 数据由键(key)/值(value)描述,由逗号分隔
  • 大括号代表一个完整的对象,拥有多个键/值对
  • 中括号保存数组,多个对象之间使用逗号分割

所有的key和value都要用双引号进行标注

JSON存储员工信息

[
  {
    "empno": 7369,
    "ename": "李宁",
    "job": "软件工程师",
    "hiredate": "2017-05-12",
    "salary": 13000,
    "dname": "研发部"
  },
  {
    "empno": 8848,
    "ename": "小明",
    "job": "销售总监",
    "hiredate": "2022-4-23",
    "salary": 8000,
    "dname": "人事部",
    "customers": [
      {
        "cname": "李东"
      },
      {
        "cname": "刘楠"
      }
    ]
  }
]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
        var json = [
            {
                "empno": 7369,
                "ename": "李宁",
                "job": "软件工程师",
                "hiredate": "2017-05-12",
                "salary": 13000,
                "dname": "研发部"
            },
            {
                "empno": 8848,
                "ename": "小明",
                "job": "销售总监",
                "hiredate": "2022-4-23",
                "salary": 8000,
                "dname": "人事部",
                "customers": [
                    {
                        "cname": "李东"
                    },
                    {
                        "cname": "刘楠"
                    }
                ]
            }
        ];
        //在浏览器控制台中对json内容进行输出
        console.log(json);
        for (var i = 0; i < json.length; i++){
            var emp = json[i];
            document.write("<h1>");
            document.write(emp.empno);
            document.write("," + emp.ename);
            document.write("," + emp.job);
            document.write("," + emp.hiredate);
            document.write("," + emp.salary);
            document.write("," + emp.dname);
            document.write("</h1>");
            if (emp.customers != null){
                document.write("<h2>"+emp.ename+"的顾客是")
                for (var j = 0; j < emp.customers.length; j++){
                    var customer = emp.customers[j];
                    document.write(customer.cname + ",");
                }
                document.write("</h2>");
            }
        }
    </script>
</head>
<body>

</body>
</html>

JSON与字符串相互转换

  • JSON.parse()方法字符串转换为JSON对象
  • JSON.stringify()方法JSON对象转换为字符串
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
       var str = "{\"class_name\" : \"五年fdhgfs级三班\"}"; <!--字符串 斜杠进行原意的输出-->
       var json = JSON.parse(str);
       console.log(str);
       console.log(json);
       document.write("班级:" + json.class_name);
    </script>
</head>
<body>

</body>
</html>

JS中JSON转为字符串

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
       var json1 = "{\"class_name\" : \"五年级三班\"}"; <!--字符串 斜杠进行原意的输出-->
       var str1 = JSON.stringify(json1);
       console.log(json1);
       console.log(str1);
       var json2 = {};
       json2.class_name = "五年级五班";
       json2.floor = "团委楼四层";
       json2.teacher = "王义夫"
       console.info(json2);
    </script>
</head>
<body>

</body>
</html>

JSON与Java交互

JSON工具包:json与java之间的互相转换

  • FastJson是阿里巴巴著名的JSON序列化与反序列化工具包
  • FastJson国内拥有大量使用者,拥有API简单,效率高等优点
https://repo1.maven.org/maven2/com/alibaba/fastjson/ 下载fastjson
或者配置maven依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>x.x.x</version>
</dependency>

FastJson序列化与JSON注解

public class Employee{
    private Integer empno;
    private String ename;
    private String job;
    //日期格式化
    @JSONField(name="hiredate", format="yyyy-MM-dd HH:mm:ss")
    private Date hdate;
    private Float salary;
    @JSONField(serialize = false) //不对dname进行序列化
    private String dname;
    ......
}
public class FastJsonSample1{
    public static void main(String[] args){
        Employee employee = new Employee();
        employee.setEmpno(4488);
        employee.setEname("王晓东");
        employee.setJob("客户经理");
        employee.setSalary(1000f);
        employee.setDname("市场部");
        //用日期来对时间进行初始化
        --------------//对应上方日期格式化@JSONField-------------------
        Calendar c = Calendar.getInstance();
        c.set(2019,0,30,0,0,0);
        employee.setHdate(c.getTime());//获取所需要的日期对象
        -------------------------------------------------------------
        //FastJSON中提供了JSON对象,完成对象对JSON字符串的转换
        String json = JSON.toJSONString(employee);
        System.out.println(json);
        //                            ↓↓ 原始字符串 反序列化类 ↓↓
        Employee emp = JSON.parseObject(json, Employee.class); //转换为何种对象
        //JSNO.parse()方法将字符串转换为JSON对象
        System.out.println(emp.getEname());
    }
}

FastJSON对象数组序列化与反序列化

JSON序列化与反序列化的用途:数据传输和数据保存 [A电脑中有100个员工信息,通过JSON把数据变成字符串发送給另一个系统,再进行转换]

public class FastJsonSample2{
    public static void main(String[] args){
        List emplist = new ArrayList();
        for(int i = 1; i <= 100; i++){
            Employee employee = new Employee();
            employee.setEmpno(4488 + i);
            employee.setEname("员工" + i);
            emplist.add(employee);
        }
        String json = JSON.toJSONString(emplist);
        System.out.println(json);
        List<Employee> emps = JSON.parseArray(json, Employee.class);
        for(Employee e : emps){
            System.out.println(e.getEmpno() + ":" + e.getEname());
        }
    }
}

JSON教程 https://www.cainiaojc.com/json/json_objects.html
JSON用于与Web服务器交换数据。数据发送到Web服务器时,数据必须是字符串

JSON格式几乎与JavaScript对象相同 
在JSON中,键必须是字符串,并用双引号引起来 {"name":"Seagull"}
在JavaScript中,键可以是字符串数字或标识符 {name : "Seagull"} 
JSON字符串类型 {"name":"Seagull"}
JSON数字类型 {"age":22}
JSON布尔类型 {"isAdult":true}
JSON对象类型 {"user":{"name:""Seagull","age":22,"city":"New Delhi"}} 
JSON数组类型 {"user":["Seagull","Cynthia","Tarush"]} 
//来自服务器的JSON
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
   if (this.readyState = 4 && this.status = 200) {
      var myObj = JSON.parse(this.responseText);
      document.getElementById("output").innerHTML = myObj.name;
   }
};
httpRequest.open("GET", "demo.json", true);
httpRequest.send();

**JSON.parse()**方法解析JSON字符串来构造JavaScript值或该字符串描述
**JSON.stringify()**方法将JavaScript对象或值转换为JSON字符串

解析日期parse()
<!DOCTYPE html>
<html>
<title>JSON.parse() 日期与json转换示例 - 基础教程(cainiaojc.com)</title>
<body>
<p>如果需要包含日期,则将其写为字符串,然后稍后将其转换回日期对象:</p>
<p id="output"></p>
<script>
var myJSON = '{"name":"Seagull", "birth":"1997-11-10", "city":"New Delhi"}';
var myObj = JSON.parse(myJSON);
myObj.birth = new Date(myObj.birth);
document.getElementById("output").innerHTML = myObj.name + " DOB is, " + myObj.birth;
</script>
</body>
</html> 
/*如果需要包含日期,则将其写为字符串,然后稍后将其转换回日期对象:
Seagull DOB is, Mon Nov 10 1997 08:00:00 GMT+0800 (中国标准时间)*/ 
解析日期stringify()
<!DOCTYPE html>
<html>
<title>JSON.stringify() 将日期对象转换为字符串示例 - 基础教程(cainiaojc.com)</title>
<body>
<p> JSON.stringify()方法会将任何日期对象转换为字符串:</p>
<p id="output"></p>
<script>
var myObj = { name: "Seagull", today: new Date(), city : "New Delhi" };
var myJSON = JSON.stringify(myObj);
document.getElementById("output").innerHTML = myJSON;
</script>
</body>
</html>
/*JSON.stringify()方法会将任何日期对象转换为字符串:
{"name":"Seagull","today":"2023-10-19T09:21:17.353Z","city":"New Delhi"*/

Java中的this.对象不会被函数内部修改 去寻找定义的值
this可以去调到外面的东西 解决了变量名一样的冲突
this区分成员变量和局部变量
this调用成员方法

Java中的static是公共的变量池


jQuery[主流的JavaScript库]与Ajax

  • 了解jQuery3的基本使用方法
  • 掌握Ajax处理流程与实现流程
  • 掌握jQuery中Ajax方法的使用

jQuery是一个轻量级JS库,使用十分简单;jQuery的核心是选择器用于获取页面元素

jQuery选择器 [先引用]

//引用jQuery
<script type="text/javascript">
    $("span").css("color","red");
    //简化形式 中间加JSON表达式
    $("a").css({"color" : "red", "font-size" : "30px", "font-weight" : "bold", "font-style" : "italic"})
    //为选中的选择器增添类效果 为li增加两个css类
    $("li").addClass("highlight myclass");
    $("p").removeClass("myclass") //移除类
    var color = $("a").css("color","orange");
    // alert(color)



### 设置元素内容

- **val()** 获取或设置输入项的值
- **text()** 获取或设置元素的纯文本
- **html()** 获取或设置元素内部的HTML

```html
.. <input type = "text" name = "uname" value = "admin"/> ..//将初始uname值变admin..
.. <span class = "myclass">我是myclass类的span标签</span> ..
<script type="text/javascript" src="js/jquery-3.1.1.js" ></script>
<script type = "text/javascript">
    //获取或设置文本输入框的内容
    $("input[name='uname']").val("administrator");
    
    //text与html方法最大的区别在于对文本中的html标签是否进行转义
    var vspan = $("span.myclass").text("<b>锄禾日当午</b>");
    alert(vspan);//锄禾日当午 纯文本中带了<b>标签
    
    var vspan = $("span.myclass").html("<b>锄禾日当午</b>");  
    alert(vspan);//<b>锄禾日当午</b>
</script>

jquery事件处理方法

  • on(“click”, function) 为选中的页面元素绑定单机事件
  • click(function) 是绑定事件的简写形式
  • 处理方法中提供了event参数包含了事件的相关信息
鼠标事件 键盘事件 表单事件 文档/窗口事件
click keypress
(键盘完整按下弹起)
submit
(表单提交)
load
(文档加载)
dblclick(双击) keydown
(键盘按下)
change
(表单发生变化)
resize
(文档窗口产生变化)
mouseenter
(滑鼠移动)
keyup
(键盘弹起)
focus
(表单输入项获得焦点)
scroll
(文档窗口滚动变化)
mouseleave
(滑鼠移出)
blur
(表单输入项失去焦点)
unload
(文档窗口关闭/卸载)
mouseover
(滑鼠移动过程)
.. <p class = "myclass">我是拥有myclass的p标签</p> ..
.. <input type = "text" name = "uname" value = "admin"/> ..
<script type = "text/javascript" src = "js/jquery-3.3.1.js"></script>
<script type = "text/javascript">
    $("p.myclass").on("click", function(){
    //$(this)是指当前事件产生的对象$("p.myclass")
    $(this).css("background-color", "yellow");
})
    $("span.myclass").click(function(){
    $(this).css("background-color", "lightgreen");                      
}) 
    
    $("input[name='uname']").keypress(function(event){
        console.log(event); //打印事件 F12 每次输入按键的Console
        $(this).css("color", "red");
    })
    
</script>
----------------------------------------------------------------
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn,staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
 $("p").click(function()){
    <!--动作触发后执行的代码-->
    #(this).hide();
});
});
</script>
    </head>
    <body>
        <p>
            如果你点我,我就会消失。
        </p>
        <p>
            点我消失!
        </p>
    </body>

jQuery hide()和show()

使用hide()和show()方法来隐藏和显示HTML元素

$("#hide").click(function(){
    $("p").hide();
});
$("#show").click(function(){
    $("p").show();
});
隐藏速度

$(selector).hide(speed,callback);

$(selector).show(speed,callback);

<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn,staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
$("button").click(function(){
    $("p").hide(1000);
 });
});
</script>
    </head>
    <body>
      <button>隐藏</button>
      <p> 这是个段落</p>
      <p> 小段落</p>
    </body>

jQuery选择器实验室

sample2.html
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery实验室</title>

<style>
.myclass {
    font-style: italic;
    color: darkblue;
}
/* 高亮css类 */
.highlight {
    color: red;
    font-size: 30px;
    background: lightblue;
}
</style>

</head>

<body>
    <div class="section">
        <h2>jQuery选择器实验室</h2>
        <input style="height: 24px" id="txtSelector" />
        <button id="btnSelect" style="height: 30px">选择</button>
        <hr />
        <div>
            <p id="welcome">欢迎来到选择器实验室</p>
            <ul>
                <li>搜索引擎:<a href="http://www.baidu.com">百度</a> <span> <a
                        style="color: darkgreen" href="http://www.so.com">360</a>
                </span>
                </li>
                <li>电子邮箱:<a href="http://mail.163.com">网易邮箱</a> <span> <a
                        style="color: darkgreen" href="http://mail.qq.com">QQ邮箱</a>
                </span>
                </li>
                <li>中国名校:<a href="http://www.tsinghua.edu.cn">清华大学</a> <span>
                        <a style="color: darkgreen" href="https://www.pku.edu.cn/">北京大学</a>
                </span>
                </li>
            </ul>

            <span class="myclass ">我是拥有myclass类的span标签</span>

            <p class="myclass">我是拥有myclass的p标签</p>
            <form id="info" action="#" method="get">
                <div>
                    用户名:<input type="text" name="uname" value="admin" /> 密码:<input
                        type="password" name="upsd" value="123456" />
                </div>
                <div>
                    婚姻状况: <select id="marital_status">
                        <option value="1">未婚</option>
                        <option value="2">已婚</option>
                        <option value="3">离异</option>
                        <option value="4">丧偶</option>
                    </select>
                </div>
                <div class="left clear-left">
                    <input type="submit" value="提交" /> <input type="reset" value="重置" />
                </div>
            </form>
        </div>
    </div>
    <script type="text/javascript" src="js/jquery-3.1.1.js" ></script>
    <script type="text/javascript">
        /*
            id选择器使用"#id值"进行选择
            css选择器使用".css类名"进行选择
            $(".myclass").addClass("highlight");
        */
        document.getElementById("btnSelect").onclick = function(){
            var selector = document.getElementById("txtSelector").value;
            //jquery选择器方法 选择器表达式
            $("*").removeClass("highlight") //在增加高亮之前 在当前页面将所有的类移除
            $(selector).addClass("highlight"); //对前面所选中的元素追加css类
        }
    </script>
</body>
</html>

Ajax(Asynchronous JavaScript And XML)介绍

Ajax(异步的 JavaScriptXML)

  • Ajax可以**在不刷新页面的前提下,进行页面布局更新,与后台交互**
    [不对整个页面刷新 只对局部数据更新刷新]
  • Ajax不是新技术,不是W3C的标准

Ajax的使用流程

  • 创建XmlHttpRequest对象[用于后台与服务器交换数据 是Ajax的核心]
  • 发送Ajax请求
  • 处理服务器响应
创建XmlHttpRequest对象
// 1.创建XmlHttpRequest对象
     var xmlhttp;
      if(windows.XMLHttpRequest){
       xmlhttp = new XMLHttpRequest();
    }else{
       xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
发送Ajax请求
  • xmlhttp.open() 用于创建请求
//创建请求
xmlhttp.open("Get","http://localhost/test?name=admin",true);
//发送到服务器
xmlhttp.send();
处理服务器响应
  • xmlhttp.onreadystatechange() 事件用于监听Ajax的执行过程
  • xmlhttp.readyState = “number 属性说明XMLHttpRequest当前状态
  • xmlhttp.status 属性服务器响应状态码,200:成功 404:未找到
readyState指 说明
readyState = 0 请求未初始化
readyState = 1 服务器连接已建立
readyState = 2 请求已被接收
readyState = 3 请求正在处理
readyState = 4 响应文本已被接收
完整编写
<body>
    <input id="btnLoad" type="button" value="加载">
    <div id="divContent"></div>
    <script>
        document.getElementById("btnLoad").onclick = function(){
            //1.创建XmlHttpRequest对象
            var xmlhttp;
            if(windows.XMLHttpRequest){
                xmlhttp = new XMLHttpRequest();
            }else{
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            console.log(xmlhttp); //在控制台console里有数据
            //2.发送Ajax请求 //F12中Network content有Response返回
            xmlhttp.open("GET", "/json_war_exploded/news.html", true)
            //3.处理服务器响应
            xmlhttp.onreadystatechange = function(){
            //响应已被接收且服务器处理成功时才执行
                if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                //获取响应体的文本
                var responseText = xmlhttp.responseText;
                //对服务器结果进行处理 
                alert(t);
                //点击加载后 数据输出在页面的div上
                document.getElementById("divContent").innerHTML = t;
                }
            }
        }
    </script>
</body>
ContentServlet.java
@WebServlet("/content")
public class ContentServlet extends HttpServlet{
    public ContentServlet(){
        super();
    }
    //用Ajax进行请求的时候不进行任何页面跳转而是直接输出想产生的数据结果[一般用JSON传递]
    protected void doGet(HttpServletReq req, HttpServletRespon res){
        res.getWritre().println("<b style = 'color:red'>I'm server content</b>");
    }
}

利用Ajax实现新闻列表[案例]

需要下载fast json-1.2.52.jar

News.java
public class News(){
    private String title;
    private String date;
    private String source;
    private String content;
    Getter and Setter + Constructor
}
NewsListServlet.java
@WebServlet("/news_list")
public class NewsListServlet extends HttpServlet{
    public NewsListServlet(){
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List list = new ArrayList();
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        //用于把List或者java对象給json序列化生成对应的字符串
        String json = JSON.toJSONString(list); //提供的java对象
        System.out.println(json);
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().println(json);
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
</head>
<body>
<div id="container">
</div>
<script type="text/javascript">
        //1.创建XmlHttpRequest对象
        var xmlhttp;
        if(window.XMLHttpRequest){
            xmlhttp = new XMLHttpRequest();
        }else{
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        //2.发送Ajax请求 //F12中Network content有Response返回
        //true代表异步执行 false代表同步执行
        xmlhttp.open("GET", "/json_war_exploded/news_list", false)
        //同步:在网络发布的过程中 数据没有返回的话会一直处于阻塞的状态(前面的没做完后面的不让做)
        xmlhttp.send();
        console.log("请求发送完成");
        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            //获取响应体的文本
            var text = xmlhttp.responseText;
            //对服务器结果进行处理
            console.log(text);
            var json = JSON.parse(text);//内置的json对象
            console.log(json);
            var html = "";
            for (var i = 0; i < json.length; i++) {
                var news = json[i];
                html = html + "<h1>" + news.title + "</h1>";
                html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                html = html + "<hr/>"
            }
            document.getElementById("container").innerHTML = html;
        }
        /*3.处理服务器响应 异步[一直往下进行 onreadystatechange数据监控]
        xmlhttp.onreadystatechange = function(){
            //响应已被接收且服务器处理成功时才执行
            if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                //获取响应体的文本
                var text = xmlhttp.responseText;
                //对服务器结果进行处理
                console.log(text);
                var json = JSON.parse(text);//内置的json对象
                console.log(json);
                var html = "";
                for (var i = 0; i < json.length; i++) {
                    var news = json[i];
                    html = html + "<h1>" + news.title + "</h1>";
                    html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                    html = html + "<hr/>";
                }
                //对html进行动态加载
                document.getElementById("container").innerHTML = html;
            }
        }*/
</script>
</body>
</html>

同步与异步

xmlhttp.open(“GET”, “/json_war_exploded/news_list”, false)

**同步(false)**:程序会阻塞 进入等待的状态 数据不返回时程序是不会往下进行的
异步(true)[推荐]:程序不会阻塞 程序依旧往下进行 数据返回是通过触发onreadystatechange数据监控进行处理的

//    2.发送Ajax请求 //F12中Network content有Response返回
        //true代表异步执行 false代表同步执行
        xmlhttp.open("GET", "/json_war_exploded/news_list", false)
        //同步:在网络发布的过程中 数据没有返回的话会一直处于阻塞的状态(前面的没做完后面的不让做)
        xmlhttp.send();
        console.log("请求发送完成");
        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            //获取响应体的文本
            var text = xmlhttp.responseText;
            //对服务器结果进行处理
            console.log(text);
            var json = JSON.parse(text);//内置的json对象
            console.log(json);
            var html = "";
            for (var i = 0; i < json.length; i++) {
                var news = json[i];
                html = html + "<h1>" + news.title + "</h1>";
                html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                html = html + "<hr/>"
            }
            document.getElementById("container").innerHTML = html;
        }
        /*3.处理服务器响应 异步[一直往下进行 onreadystatechange数据监控]
        xmlhttp.onreadystatechange = function(){
            //响应已被接收且服务器处理成功时才执行
            if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                //获取响应体的文本
                var text = xmlhttp.responseText;
                //对服务器结果进行处理
                console.log(text);
                var json = JSON.parse(text);//内置的json对象
                console.log(json);
                var html = "";
                for (var i = 0; i < json.length; i++) {
                    var news = json[i];
                    html = html + "<h1>" + news.title + "</h1>";
                    html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                    html = html + "<hr/>";
                }
                //对html进行动态加载
                document.getElementById("container").innerHTML = html;
            }
        }*/

<button type = "button" onclick="loadXMLDoc()">修改内容</button>
<script>
/* XMLHttpRequest 用于在后台与服务器交换数据。
这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。
*/ 
function loadXMLDoc(){
    //AJAX脚本执行
    var xmlhttp;
    if(windows.XMLHttpRequest){
        xmlhttp=new XMLHttpRequest();
    }else{
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
}
</script>

/*
如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:

open(method,url,async)
method:请求的类型;GET 或 POST
url:文件在服务器上的位置
async:true(常用异步)或 false(同步)

对于 web 开发人员来说,发送异步请求是一个巨大的进步。
很多在服务器执行的任务都相当费时。AJAX 出现之前,这可能会引起应用程序挂起或停止。
通过 AJAX,JavaScript 无需等待服务器的响应,而是:
在等待服务器响应时执行其他脚本
当响应就绪后对响应进行处理
*/ 

xmlhttp.open("GET","ajax_info.html",true);
xmlhttp.send(); //send(string) 仅用于POST请求 

/*
若需要来自服务器的响应,请使用XMLHttpRequest对象的responseText或responseXML属性 
responseText => 获得字符串形式的相应数据 如果来自服务器的响应并非XML
responseXML => 获得XML形式的相应数据 如果来自服务器的响应式XML 
*/ 
// responseText 
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
// responseXML
xmlDoc=xmlhttp.responseXML;
txt="";
x=xmlDoc.getElementsByTagName("ARTIST");
    for (i=0;i<x.length;i++)
    {
        txt=txt + x[i].childNodes[0].nodeValue + "<br>";
    }
document.getElementById("myDiv").innerHTML=txt;
/*onreadystatechange事件中我们规定当服务器响应已做好被处理的准备时所执行的任务
0:请求未初始化  1:服务器连接已建立  2:请求已接收  3:请求处理中  4:请求已完成且响应已就绪 
 */ 
 //如果您的网站上存在多个 AJAX 任务,那么您应该为创建 XMLHttpRequest对象编写一个标准的函数
 //并为每个 AJAX 任务调用该函数 
 xmlhttp.onreadystatechange=function()
{
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
}

jQuery对Ajax的支持

  • jQuery对Ajax进行封装,提供了**$.ajax()**方法
  • 语法:**$.ajax(options)**
常用设置项 说明
url 发送请求地址
type 请求类型get|post
data 向服务器传递的参数
dataType 服务器响应的数据类型
text|json|xml|html|jsonp|script
success 接收响应时的处理函数
error 请求失败时的处理函数
jquery_news.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.1.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $.ajax({
                "url": "/json_war_exploded/news_list",
                "type": "get",
                "data": "t=tiobe",//多个传参的参数用json格式写
                //"data": {"t":"tiobe", "abc":"123", "uu":"777"}
                //但是实际过程中jquery会以url形式传参 t=tiobe&abc=123&uu=777
                "dataType": "json", //解析成json文件
                "success": function (json){
                    console.log(json);
                    for (var i = 0; i < json.length; i++){
                        $("#container").append("<h1>" + json[i].title + "</h1>"); //append追加
                    }
                },   //ajax的核心信息 ↓
                "error": function (xmlhttp, errorText){
                    console.log(xmlhttp);
                    console.log(errorText);
                    if(xmlhttp.status == "405"){
                        alert("无效的请求方式");
                    }else if(xmlhttp.status == "404"){
                        alert("未找到URL资源");
                    }else if(xmlhttp.status == "500"){
                        alert("服务器内部错误,请联系管理员");
                    }else{
                        alert("产生异常,请联系管理员");
                    }
                }
            })
        })
    </script>
</head>
<body>
    <div id = "container"></div>
</body>
</html>

实现二级联动菜单 [省 市 县]

ChannelServlet.java
package com.example.json;

import com.alibaba.fastjson.JSON;

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.List;

@WebServlet("/channel")
public class ChannelServlet extends HttpServlet {
    public ChannelServlet() {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String level = req.getParameter("level");
        String parent = req.getParameter("parent");
        List chlist = new ArrayList();
        if(level.equals("1")) {
            chlist.add(new Channel("ai" , "前沿/区块链/人工智能"));
            chlist.add(new Channel("web" , "前端/小程序/JS"));
        }else if(level.equals("2")) {
            if(parent.equals("ai")) {
                chlist.add(new Channel("micro" , "微服务"));
                chlist.add(new Channel("blockchain" , "区块链"));
                chlist.add(new Channel("other" , "..."));
            }else if(parent.equals("web")){
                chlist.add(new Channel("html" , "HTML"));
                chlist.add(new Channel("css" , "CSS"));
                chlist.add(new Channel("other" , "..."));
            }
        }
        //json序列化
        String json = JSON.toJSONString(chlist);
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().println(json);
    }
}
cascade_menu.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.1.1.js"></script>
    <script type="text/javascript">
        $(function(){
            $.ajax({
                "url" : "/json_war_exploded/channel",
                "data" : {"level" : "1"},
                "type" : "get" ,
                "dataType" : "json" ,
                "success" : function(json){
                    console.log(json);
                    for(var i = 0 ; i < json.length ; i++){
                        var ch = json[i];
                        //append是在组件内部进行追加内容
                        $("#lv1").append("<option value='" + ch.code + "'>" + ch.name + "</option>")
                    }
                }
            })
        })

        $(function(){ //on是绑定事件 change是当变化时候发生function
            $("#lv1").on("change" , function(){
                var parent = $(this).val();//val()获取输入项的值
                console.log(parent);
                $.ajax({
                    "url" : "/json_war_exploded/channel" ,
                    "data" : {"level" : "2" , "parent" : parent}, //parent 一级频道的value值
                    "dataType" : "json" ,
                    "type" : "get" ,
                    "success" : function(json){
                        console.log(json);
                        //移除所有lv2下的原始option选项
                        $("#lv2>option").remove();
                        for(var i = 0 ; i < json.length ; i++){
                            var ch = json[i];
                            $("#lv2").append("<option value='" + ch.code +"'>" + ch.name + "</option>")
                        }
                    }
                })
            })
        })
    </script>
</head>
<body>
<select id="lv1" style="width:200px;height:30px">
    <option selected="selected">请选择</option>
</select>
<select id="lv2" style="width:200px;height:30px"></select>
</body>
</html>

正则表达式

  • 正则表达式是检查、匹配字符串的表达式
  • 正则表达式是描述规则、主流语言都有良好支持
  • 字符串校验、查找与替换是正则表达式主要使用场景
正则表达式的案例
  • 检查输入的身份证号是否合法(15位、18位)
  • 示例:13010220200328091x
正则表达式:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)

字符范围匹配

正则表达式 说明 正确 错误
A 精准匹配单个字符 A a
x|y 允许出现的2个字符 y n
[xyz] 字符集合,允许出现集合内任意单个字符 z c
[a-z] [A-Z] [0-9] 字符范围 a D 8 A a A
[^xyz] [‘^0-9] 集合内字符不允许出现 0 A y 8
训练题目
  • 精准匹配字符串”hallo” 或者 “hello” => h**[ae]**llo
  • 请匹配数字范围(0570-0579) => 057[0-9]
  • 单选题只允许输入ABCD其中一项 => [ABCD] / [A-D]

元字符

正则表达式 说明 正确 错误
\d 匹配任意单个数字 8 i
\D 匹配\d规则之外的任意单个字符 i 8
\w 匹配任意单个字母数字下划线 Y &
\W 匹配\w之外的任意单个字符 & Y
\s 匹配单个空格 x
\n 匹配单个换行符 x
. 匹配任意单个字符(换行符除外)
\。 特殊字符,只匹配 “.” . 1
训练题目
  • 请匹配数字(3213.383219)

    \d\d\d\d\.\d\d\d\d\d\d\d
    
  • 匹配杭州与宁波的座机号码(0571|0574-XXXXXXXX)

    057[14]-\d\d\d\d\d\d\d\d
    
  • 请匹配18位身份证号 [第一位1~6和8]

    [1234568]\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d[0-9xX]
    

多次重复匹配

正则表达式 说明 正确 错误
A{3} 精准N次匹配 AAA AA
A{3,} 最少出现N AAA AA
\d{3,5} 约定出现最少次数与最大次数 1234 12
\d* 可以出现零次至无限次,相当于{0,} 1234
\d+ 最少出现一次, 相当于{1,} 12
\d? 最多出现一次, 相当于{0,1} 1 12
联系题目
  • 验证短信验证码(6位数字) => \d[6]

  • 请匹配全国座机号(区号3或4位-电话号码7或8位) => \d[3,4]-\d[7,8]

  • 请匹配英文姓名(例如:James Watson)

    [A-Z][a-z]{1,}\s[A-Z][a-z]{0,}
    [A-Z][a-z]{1,}\s[A-Z][a-z]*
    ^[A-Z][a-z]{1,}+\s[A-Z][a-z]*$
    

定位匹配

正则表达式 说明 正确 错误
^A.* 头匹配 ABC CBA
**.A$* 尾匹配 CBA ABC
^A.*A$ 全字匹配 ACCCA ACCCB
abb123123ab => ^ab.*ab$

贪婪模式[默认匹配规则]

  • 在满足条件的情况下尽可能匹配到字符串
  • 示例:111222333 正则:\d{6,8}
  • 匹配结果:11122233

非贪婪模式

  • 在满足条件的情况下尽可能匹配到字符串

  • 示例:111222333 正则:\d{6,8}?

  • 匹配结果:111222

贪婪模式举例
<a href = "www.baidu.com"> 百度 </a> <a href =  "www.baidu.com">新浪</a>
规则: 想搜索"www.baidu.com" 仅此一个从"开始 从"结束 中间的片段
正则表达式: ".*" 由于默认是贪婪模式 它会找到的结果如下
"www.baidu.com"> 百度 </a> <a href = “www.sina.com”

改成非贪婪模式 正则表达式:".*?"
"www.baidu.com"  "www.baidu.com"

表达式分组

  • 分组将”正则”分组为多个子表达式
  • 示例:abababcdcdcd
  • 正则表达式:(ab){3}(cd){3}
训练题目
  • 匹配验证码(4位或6位)

    (^\d{4}$)|(^\d{6}$)
    
  • 匹配车牌号(冀B-U888G)

    ^([冀黑粤晋][A-Z])-([A-Z0-9]{5})$
    
  • 中文名字匹配 [Unicall码] [张三…]

    ^[\u4e00-\u9fa5]{2,8}$
    
  • 中英文名字匹配

    (^[\u4e00-\u9fa5]{2,8}$)|(^[A-Z][a-z]*$)
    

正则表达式验证JavaScript表单

<body>
    <form action="#" method="post" id="frmInfo">
        <div id="err" style="color : red">
            
        </div> 
        <div>
            姓名: <input id="name" name="name"/>
        </div>
        <div>
            身份证: <input id="idno" name="idno"/>
        </div>
        <div>
            <input type="submit"/>
        </div>
    </form>
    <script type="text/javascript">
        document.getElementById("frmInfo").onsubmit = function(){
            //在JS中定义正则表达式对象只需要在 /正则表达式/
            var regex1 = /^[\u4e00-\u9fa5]{2,8}$/
            var regex2 = /^rehextal
            var name = document.getElementById("name").value;
            var idno = document.getElementByid("inamd")
            if(regex1.test(name)==false); //返回true或false校验是否成功
                document.getElementById("err").innerHTM="无效姓名";
                return false;
            }else if(regx2.test(idno) == false){
                document.getElementById("err").innerHTM="无效身份证号";
            }else{
                alert("验证通过准备提交")
                return true;
            }
    </script>
</body>

Java中web页面信息提取

sample.html
<!DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<title>国际主要城市</title>
</head>
<body>
    <h1>国际主要城市</h1>
    <ul>
        <li>纽约NewYork</li>
        <li>伦敦London</li>
        <li>东京Tokyo</li>
        <li>巴黎Paris</li>
        <li>香港HongKong</li>
        <li>新加坡Singapore</li>
        <li>悉尼Sydney</li>
        <li>米兰Milano</li>
        <li>上海Shanghai</li>
        <li>北京Beijing</li>
        <li>马德里Madrid</li>
        <li>莫斯科Moscow</li>
        <li>首尔Seoul</li>
        <li>曼谷Bangkok</li>
        <li>多伦多Toronto</li>
        <li>布鲁塞尔Brussels</li>
        <li>芝加哥Chicago</li>
        <li>吉隆坡KualaLumpur</li>
        <li>孟买Mumbai</li>
        <li>华沙Warsaw</li>
        <li>圣保罗SaoPaulo</li>
        <li>苏黎世Zurich</li>
        <li>阿姆斯特丹Amsterdam</li>
        <li>墨西哥城MexicoCity</li>
        <li>雅加达Jakarta</li>
        <li>都柏林Dublin</li>
        <li>曼谷Bangkok</li>
        <li>台北Taipei</li>
        <li>伊斯坦布尔Istanbul</li>
        <li>里斯本Lisbon</li>
        <li>罗马Rome</li>
        <li>法兰克福Frankfurt</li>
        <li>斯德哥尔摩Stockholm</li>
        <li>布拉格Prague</li>
        <li>维也纳Vienna</li>
        <li>布达佩斯Budapest</li>
        <li>雅典Athens</li>
        <li>加拉加斯Caracas</li>
        <li>洛杉矶LosAngeles</li>
        <li>新西兰NewZealand</li>
        <li>圣地亚哥SanDiego</li>
        <li>布宜诺斯艾利斯BuenosAires</li>
        <li>华盛顿Washington</li>
        <li>墨尔本Melbourne</li>
        <li>约翰内斯堡Johannesburg</li>
        <li>亚特兰大Atlanta</li>
        <li>巴塞罗那Barcelona</li>
        <li>旧金山SanFrancisco</li>
        <li>马尼拉Manila</li>
        <li>波哥大Bogota</li>
        <li>特拉维夫TelAviv-Yafo</li>
        <li>新德里NewDelhi</li>
        <li>迪拜Dubai</li>
        <li>布加勒斯特Bucharest</li>
        <li>奥斯陆Oslo</li>
        <li>柏林Berlin</li>
        <li>赫尔辛基Helsinki</li>
        <li>日内瓦Geneva</li>
        <li>利雅得Riyadh</li>
        <li>哥本哈根Copenhagen</li>
        <li>汉堡Hamburg</li>
        <li>开罗Cairo</li>
        <li>卢森堡Luxembourg</li>
        <li>班加罗尔Bangalore</li>
        <li>达拉斯Dallas</li>
        <li>科威特城Kuwaitcity</li>
        <li>波士顿Boston</li>
        <li>慕尼黑Munich</li>
        <li>迈阿密Miami</li>
        <li>利马Lima</li>
        <li>基辅Kiev</li>
        <li>休斯顿Houston</li>
        <li>广州Guangzhou</li>
        <li>贝鲁特Beirut</li>
        <li>卡拉奇Karachi</li>
        <li>索菲亚Sophia</li>
        <li>蒙得维的亚Montevideo</li>
        <li>里约热内卢RioDEJaneiro</li>
        <li>胡志明市HoChiMinhCity</li>
        <li>蒙特利尔Montreal</li>
        <li>内罗毕Nairobi</li>
        <li>巴拿马城Panamacity</li>
        <li>金奈Chennai</li>
        <li>布里斯班Brisbane</li>
        <li>卡萨布兰卡Casablanca</li>
        <li>丹佛Denver</li>
        <li>基多Quito</li>
        <li>斯图加特Stuttgart</li>
        <li>温哥华Vancouver</li>
        <li>麦纳麦MaiNaMai</li>
        <li>危地马拉市Guatemalacity</li>
        <li>开普敦CapeTown</li>
        <li>圣何塞SanJose</li>
        <li>西雅图Seattle</li>
        <li>深圳Shenzhen</li>
        <li>珀斯Perth</li>
        <li>加尔各答Calcutta</li>
        <li>安特卫普Antwerp</li>
        <li>费城Philadelphia</li>
        <li>鹿特丹Rotterdam</li>
        <li>拉各斯Lagos</li>
        <li>波特兰Portland</li>
        <li>底特律Detroit</li>
        <li>曼彻斯特Manchester</li>
        <li>惠灵顿Wellington</li>
        <li>里加Riga</li>
        <li>爱丁堡Edinburgh</li>
        <li>圣彼得堡StPetersburg</li>
        <li>圣迭戈SanDiego</li>
        <li>伊斯兰堡Islamabad</li>
        <li>伯明翰Birmingham</li>
        <li>多哈Doha</li>
        <li>阿拉木图AlmaAtaAlmaty</li>
        <li>卡尔加里Calgary</li>
    </ul>
</body>
</html>
RegexSample.java
package com.imooc.regex;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexSample {
    public static void main(String[] args) {
        StringBuilder content = new StringBuilder();
        try {//读取文件
            FileInputStream fis = new FileInputStream("D:/workspace/regex/WebContent/sample.html");
            InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
            BufferedReader bufferedReader = new BufferedReader(isr);//缓冲提高读取效率
            String lineText = "";//每一行
            while((lineText = bufferedReader.readLine()) != null) {//读取完成
//                System.out.println(lineText);
                content.append(lineText + "\n");//追加到后面完整字符串
            }
            bufferedReader.close();
            System.out.println(content);
        
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        //1.创建正则表达式对象 用group进行分组提取
        //[正则表达式字符串] \\两个斜杠是原意输出 +至少出现一次
        Pattern p = Pattern.compile("<li>([\\u4e00-\\u9fa5]{2,10})([a-zA-Z]+)</li>");
        //2.匹配正则表达式
        Matcher m = p.matcher(content);
        //3.查找匹配的结果
        while(m.find()) {//原始字符串中进行查找 有返回true 无返回false
//            System.out.println(m.group(0)); //完整字符串
            String chs = m.group(1); //group分组
            String eng = m.group(2);
            System.out.println(chs + "-" + eng);
        }
    }
}

初始过滤器-Filter [机场检查]

  • 过滤器(Filter)是J2EE Servlet模块下的组件
  • Filter的作用是对URL进行统一的拦截处理
  • Filter通常用于应用程序层面进行全局处理

开发过滤器三要素

  • 任何过滤器都要实现 javax.servlet.Filter 接口
  • 在Filter接口的doFIlter()方法中编写过滤器的功能代码
  • 在web.xml中对过滤器进行配置,说明拦截URL的范围
MyFirstFilter.java
public class MyFirstFilter implements Filter{
   public void destroy(){}
   public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("过滤器已生效");
       chain.doFilter(req, resp);
   }
   public void init(FilterConfig filterConfig)thorws ServletException{}
}
<filter>
    <filter-name>MyFirstFilter</filter-name>
    <filter-class>filter.MyFirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFirstFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>

<!-- 
    filter-mapping标签用于说明过滤器对URL应用的范围
    1. filter-name 过滤器名称与filter.filter-name保持一致
    2. url-pattern 说明过滤器作用范围 "/*"代表对所有url进行过滤
-->
<body>
    我是默认首页
</body>
HelloServlet.java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletReq req, HttpServletResp resp){
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().println("Hello World!!!")
    }
}

过滤器的生命周期

  • 初始化init() - Filter.init()
  • 提供服务 - Filter.doFilter()
  • 销毁 - Filter.destroy()

过滤器特性

  • 过滤器对象在Web应用启动时被创建且全局唯一
  • 唯一的过滤器对象在并发环境中采用”多线程“提供服务

过滤器两种开发方式

过滤器的配置形式
<filter>
   <filter-name>MyFirstFilter</filter-name>
   <filter-class>filter.MyFirstFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>MyFirstFilter</filter-name>
   <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>
过滤器的注解形式
@WebFilter(filterName = "MyAnnoationFilter", urlPatterns = "/*")
public class MyAnnoationFilter implements Filter{
    
}

MyAnnoationFilter.java      //自定义过滤器名称  设置去过滤哪些url
@@WebFilter(filterName = "MyAnnoationFilter", urlPatterns = "/*")
public class MyAnnoationFilter implements Filter{
    public void destroy(){}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
        System.out.println("过滤器已生效");
        chain.doFilter(req, resp);
    }
    public void init(FilterConfig filterConfig)thorws ServletException{}
}

配置与注解如何选择

  • 配置形式维护性更好,适合应用全局过滤 [中, 大型项目]
  • 注解形式开发体验更好,适合于小型项目敏捷开发

Web中文乱码的解决 [需要强制转换]

  • GET请求-server.xml增加URIEncoding = “UTF-8”;
  • POST请求-使用request.setCharacterEncoding(“UTF-8”);
  • 响应-response.setContentType(“text/html; charset = UTF-8”);
CharacterEncodingFilter.java
public class CharacterEncodingFilter implements Filter{
    public void destroy(){}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       //上面的不是Httpservlet要自己写HttpServlet并导入 解决post请求中的中文乱码
        HttpServletRequest req = (HttpServletRequest)request;//解决中文乱码问题
        req.serCharacterEncoding("UTF-8");
        HttpServletResponse res = (HttpServletResponse)response;//解决中文乱码问题
        res.serContentType("text/html;charset=UTF-8");
        chain.doFilter(req, response);
    }
    public void init(FilterConfig filterConfig)thorws ServletException{}
}
ServletRequest[最顶级] 和 HTTPServletRequest[需继承最顶级]关系所在

web.xml
<filter>
    <filter-name>MyFirstFilter</filter-name>
    <filter-class>filter.MyFirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFirstFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>

====================================================================

<!--或者在CharacterEncodingFilter上方进行注解 必须是全局唯一-->
@WebFilter(filterName="CharacterEncodingFilter", urlPatterns="/*")
HelloServlet.java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletReq req, HttpServletResp resp){
        resp.getWriter().println("你好!世界!!")
    }
}

过滤器开发技巧

过滤器参数化
  • 过滤器为了增强灵活性,允许配置信息放在web.xml
  • 在web.xml中配置**< init-param >**设置过滤器参数
优化字符集过滤器 [为了不去修改java代码]
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value> <!--方便参数发生变化-->
    </init-param>
    <init-param>
        <param-name>v1</param-name>
        <param-value>GBK</param-value>
    </init-param>
    ......
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>
-------------------------------------------------------------
<!--或者在CharacterEncodingFilter上方进行注解 必须是全局唯一-->
@WebFilter(filterName="CharacterEncodingFilter", urlPatterns="/*",initParams={
    @WebInitParam(name="encoding", value="UTF-8"),
    @WebInitParam(name="p1", value="v1")
})
-------------------------注意init与doFilter----------------------------
public class CharacterEncodingFilter implements Filter{
    private String encoding; //类中的全局私有变量
    public void init(FilterConfig filterConfig)thorws ServletException{
        encoding=filterConfig.getInitParameter("encoding");
        System.out.println(encoding); //用debug打开
    }
    public void doFilter(ServletRequest req, ServeltResponse resp, FilterChain chain)throws IOException,ServletException{
       //上面的不是Httpservlet要自己写HttpServlet并导入 解决post请求中的中文乱码
        HttpServletRequest req = (HttpServletRequest)request;//解决中文乱码问题
        req.setCharacterEncoding("encoding");
        HttpServletResponse res = (HttpServletResponse)response;//解决中文乱码问题
        res.setContentType("text/html;charset=" + encoding);// GBK/UTF-8或者其他
        chain.doFilter(req, response);
    }
}

url-pattern设置过滤范围

url-pattern常用写法
  • /index.jsp - 执行资源精准匹配
  • /servlet/* - 以前缀进行模糊匹配
  • *.jsp - 以后缀进行模糊匹配
SampleServlet1.java
@WebServlet("/servlet/sample1")
public class SampleServlet1 extends HttpServlet{
   public SampleServlet1(){}
   protected void doGet(HttpServletRequest req,HttpServletResponse resp){
       resp.getWriter().println("I'm" + this.getClass().getSimpleName());
   }
}
UrlPatternFilter.java
public class UrlPatternFilter implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
//上面的不是Httpservlet要自己写HttpServlet并导入 解决post请求中的中文乱码
    HttpServletRequest req = (HttpServletRequest)request;//解决中文乱码问题
    HttpServletResponse res = (HttpServletResponse)response;//解决中文乱码问题
}
<filter>
    <filter-name>UrlPatternFilter</filter-name>
    <filter-class>filter.UrlPatternFilter</filter-class>
</filter>
    <filter-mapping> <!--  <url-pattern>/test.jsp</url-pattern> <!--只对其进行过滤 -->
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>/servlet/*</url-pattern>
    </filter-mapping>
    ......

在控制台Console显示:拦截到http://localhost:8080/url-pattern/test.jsp 因为这是精准匹配 需要换成 /*

/ 映射的问题

  • / 指映射Web应用根路径, 且只会对Servlet生效
  • 默认首页index.jsp会让 / 失效
  • //* 含义不同, 前者指向根路径, 后者代表所有
SampleServlet2.java
@WebServlet("/")
public class SampleServlet2 extends HttpServlet{
    public SampleServlet1(){}
    protected void doGet(HttpServletRequest req,HttpServletResponse resp){
        resp.getWriter().println("I'm" + this.getClass().getSimpleName());
    }
}
<filter>
    <filter-name>UrlPatternFilter</filter-name>
    <filter-class>filter.UrlPatternFilter</filter-class>
</filter>
    <filter-mapping>
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>/servlet/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    ......
-------------------------------------------------------------
<!--或者在UrlPatternFilter上方进行注解 必须是全局唯一-->
@WebFilter(filterName="UrlPatternFilter", urlPatterns={
    "/","/servlet/*","*.jsp"
})

这个url-pattern 中的**/** 只会映射到根路径的SampleServlet2

@WebServlet(“/“)
public class SampleServlet2 extends HttpServlet{

Web.xml中默认配置了首页为index.jsp 如果想对默认首页拦截需要写
< url-pattern> ***/** < /url-pattern> 或 < url-pattern> /index.jsp < /url-pattern>
默认首页优先级比servlet要高

过滤链

过滤链开发注意事项
  • 每一个过滤器应具有单独职能
  • 过滤器的执行顺序以**< filter-mapping >**的前后顺序为准
  • 调用**chain.doFilter()**将请求向后传递
FilterA.java
public class FilterA implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("I'm Filter A");
       chain.doFilter(req, resp);
    }
    public void destroy(){}

FilterB.java
public class FilterA implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("I'm Filter B");
       chain.doFilter(req, resp);
    }
    public void destroy(){}

FilterC.java
public class FilterA implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("I'm Filter C");
       chain.doFilter(req, resp);
    }
    public void destroy(){}
<filter>
    <filter-name>FilterA</filter-name>
    <filter-class>filter.FilterA</filter-class>
</filter>
<filter>
    <filter-name>FilterB</filter-name>
    <filter-class>filter.FilterA</filter-class>
</filter>
<filter>
    <filter-name>FilterC</filter-name>
    <filter-class>filter.FilterA</filter-class>
</filter>
    <filter-mapping>
        <param-name>FilterA</param-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>FilterB</param-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>FilterC</param-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
---------------------不推荐使用注解--------------------------
用注解方式需要在每个Filter.java上面添加注解 
@WebFilter(filterName="FilterA", urlPatterns="/*")
public class FilterA implements Filter{
    //按照字母表升序排序 且不区分大小写
}
HelloServlet.java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        response.getWriter().print("Hello World!");
        Sysotem.out.println("Hello World!")
    }
}

Console显示:顺序由决定
I’m Filter A //chain.doFilter(req, resp); 请求顺着过滤链往下走
I’m Filter B
I’m Filter C //过滤链没有格外的过滤器了 回到HelloServlet.java最终处理
Hello World!
//servlet处理完以后按照原先的顺序 逆向由Hello、C、B、A顺序返回

刻意去控制过滤器访问 VPN 仅限中国访问 如果是中国地址就调用chain.doFilter进行放行 其本质是防火墙

多端设备自动匹配(Pc端 移动端)

设备适配过滤器
index.html
自动脑补绘制两个html首页
一个是电脑端的图片展示
一个是手机端的图片展示
DeviceAdapterFilter.java
public class DeviceAdapterFilter implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       HttpServletRequest req = (HttpServletRequest)request;//强制类型转换
          HttpServletResponse res = (HttpServletResponse)response;
       /*
       /index.html
        PC: /desktop/index.html
        MOBILE: /mobile/index.html

       /test.html
        PC: /desktop/test.html
        MOBILE: /mobile/test.html
       */
       String uri = req.getRequestURI();
       System.out.println("URI: " + uri);
       if(uri.startsWith("/desktop") || uri.startsWith("/mobile")){
           chain.doFilter(req, resp);
       }else {    //读取客户端请求头 所有字符串转成小写
           String userAgent = req.getHeader("user-agent").toLowerCase(); 
           String targetURL = "";
           if(userAgent.indexOf("android")!=-1 || userAgent.indexOf("iphone")!=-1){
               targetURI = "/mobile" + uri;
               System.out.println("移动端设备正在访问, 重新跳转URI: " + targetURI)
               res.sendRedircet(targetURI); //向/mobile对应页面进行获取
           }else {
               targetURI = "/desktop" + uri;
                System.out.println("PC端设备正在访问, 重新跳转URI: " + targetURI)
               res.sendRedircet(targetURI); //向/mobile对应页面进行获取
           }
       }
    }
    public void destroy(){}
}
<filter>
    <filter-name>DeviceAdapterFilter</filter-name>
    <filter-class>filter.DeviceAdapterFilter</filter-class>
</filter>
    <filter-mapping>
        <param-name>DeviceAdapterFilter</param-name>
        <url-pattern>*.html</url-pattern> <!--对其进行过滤-->
    </filter-mapping>

URI: /index.html
PC端设备正在访问, 重新跳转URI: /desktop/index.html
URI: /desktop/index.html

++++++++++++++++++++++++++++++++++++++++++++

URI: /index.html
移动端设备正在访问, 重新跳转URI: /mobile/index.html
URI: /mobile/index.html

监听器、Freemarker

监听器:对Web应用对象的行为进行监控 [触发事件后进行捕获] ★★★
Freemarker[模板引擎]:模板脚本+数据来实现最终数据的产生 ★★★★★

监听器

生活中的”监听器”:汽车自动刹车系统 自动检测前方障碍物触发自动刹车;实时监控电表水表 钱不足直接断电

监听器 - Listener
  • 监听器(Listener)是J2EE Servlet模块下的组件
  • Listener的作用对Web应用对象的行为进行监控
  • 通过Listener监听自动触发指定的功能代码

三种监听对象

  • ServletContext - 对全局ServletContext及其属性进行监听
  • HttpSession - 对用户会话及其属性操作进行监听
  • ServletRequest - 对请求及属性操作进行监听
过滤器与监听器的区别
  • 过滤器(Filter)的职责是对**URL进行过滤拦截**, 是主动的执行
  • 监听器(Listener)的职责是对**Web对象进行监听**, 是被动触发

开发监听器三要素

  • 实现XxxListener接口, 不同接口对应不同监听对象
  • 实现每个接口中独有的方法, 实现触发监听的后续操作
  • 在web.xml中配置**< listener >**使监听器生效

``

第一个监听器 [全局推荐使用配置形式]

FirstListener.java //Debug启动
@WebListener //启动时tomcat会自动扫描
public class FirstListener implements ServletContextListener{
    //项目初始化所触发
    public void contextInitialized(ServletContextEvent sce){
        System.out.println("ServletContext已初始化");
    }
    //上下文被销毁时所触发 关闭时自动销毁
    public void contextDestoryed(ServletContextEvent sce){
       System.out.println("ServletContext已销毁");
    }
}
<listener>
    <listener-class>com.imooc.listener.FirstListener</listener-class>
</listener>

内置对象监听接口

  • ServletContextListener - 监听ServletContext对象创建、销毁等操作
  • HttpSessionListener - 监听HttpSession对象创建、销毁等操作
  • ServletRequestListener - 监听HttpServletRequest对象创建、销毁等操作
<listener>
    <listener-class>com.example.json.WebListener</listener-class>
</listener>
HelloServlet.java //设置属性的时候会触发WebListener
WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletRequest req,HttpServletResponse resp){
        resp.getWriter().println("Hello World");
        req.getServletContext().setAttribute("sc-attr1","sc-attr-value1");
        req.getSession().setAttribute("session-attr1","session-attr-value1");
        req.setAttribute("request-attr1","request-attr-value1");
    }
}
WebListener.java
package com.example.json;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class WebListener implements ServletContextListener, HttpSessionListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext已初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext已被销毁");
    }

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        System.out.println("Session已被创建, SessionId:" + session);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("Session已被销毁");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("HttpServletRequest已被销毁");
    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest)servletRequestEvent.getServletRequest();
        //请求初始化完毕
        System.out.println("HttpServletRequest已被创建, URI" + request.getRequestURI());
    }
}

//session被创建了以后 第二次从同样的浏览器发出的请求覆盖了sessionid(3min后过期可人为而改变) 隐藏且可用
HttpServletRequest已被创建, URI/json_war_exploded/
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@71ed02bc
HttpServletRequest已被销毁
HttpServletRequest已被创建, URI/json_war_exploded/
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@194a08e0
HttpServletRequest已被销毁

属性监听接口 [了解]

  • ServletContextAttributeListener - 监听全局属性操作
  • HttpSessionAttributeListener - 监听用户会话属性操作
  • ServletRequestAttributeListener - 监听请求属性操作
web.xml 和 helloServlet.java的代码都一样
WebAttributeListener.java
package com.example.json;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class WebAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext新增属性:" + servletContextAttributeEvent.getName() + "->" + servletContextAttributeEvent.getValue());
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {

    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {

    }

    @Override
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("ServletRequestAttributeEvent新增属性:" + servletRequestAttributeEvent.getName() + "->" + servletRequestAttributeEvent.getValue());

    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("HttpSessionBindingEvent新增属性:" + httpSessionBindingEvent.getName() + "->" + httpSessionBindingEvent.getValue());
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}

ServletContext已初始化
[2023-09-28 04:44:52,590] Artifact json:war exploded: Artifact is deployed successfully
[2023-09-28 04:44:52,590] Artifact json:war exploded: Deploy took 394 milliseconds
HttpServletRequest已被创建, URI/json_war_exploded/
ServletContext新增属性:org.apache.jasper.runtime.JspApplicationContextImpl->org.apache.jasper.runtime.JspApplicationContextImpl@7753fcd4
ServletContext新增属性:org.apache.jasper.compiler.ELInterpreter->org.apache.jasper.compiler.ELInterpreterFactory$DefaultELInterpreter@3b424c58
ServletContext新增属性:org.apache.jasper.compiler.StringInterpreter->org.apache.jasper.compiler.StringInterpreterFactory$DefaultStringInterpreter@15bdbfaf
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@6baa8579
HttpServletRequest已被销毁
HttpServletRequest已被创建, URI/json_war_exploded/
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@2843a202
HttpServletRequest已被销毁
HttpServletRequest已被创建, URI/json_war_exploded/hello
ServletContext新增属性:sc-attr1->sc-attr-value1
HttpSessionBindingEvent新增属性:session-attr1->session-attr-value1
ServletRequestAttributeEvent新增属性:request-attr1->request-attr-value1
HttpServletRequest已被销毁

监听器的应用场景

请求流量分析 [以图表形式展现]

流程:在应用启动的时候将两个List初始化 一个保存时间一个保存数值 分情况 若时间不存在初始化1 若存在则在原始+1进行更新操作

web.xml
<listener>
  <listener-class>com.example.json.RequestTotalListener</listener-class>
</listener>
RequestTotalListener.java
package com.example.json;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class RequestTotalListener implements ServletContextListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        List timeList = new ArrayList(); //时间数据
        List valueList = new ArrayList(); //具体时间访问量数据
        servletContextEvent.getServletContext().setAttribute("timeList", timeList);//得到最原始的context
        servletContextEvent.getServletContext().setAttribute("valueList", valueList);//启动ServletContext时自动启动倆请求
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        //请求初始化 每来一个新的请求监听器都会执行此方法 记录某一时间点的访问量
        //TimeList:10:02  10:03  10:04  10:05
        //ValueList: 5       7     10    1+1=2
        List<String> timeList = (List)servletRequestEvent.getServletContext().getAttribute("timeList");
        List<Integer> valueList = (List)servletRequestEvent.getServletContext().getAttribute("valueList");
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); //按照小时分钟来取
        String time = sdf.format(date); //date对象传入
        if (timeList.indexOf(time) == -1) { //查找对应的数据在集合中是否存在
            //若时间不存在 则追加时间
            timeList.add(time);
            valueList.add(1);//当前有1个请求被创建
            servletRequestEvent.getServletContext().setAttribute("timeList", timeList);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }else { //假设时间存在 不增加新数据 在原有数据加1
            int index = timeList.indexOf(time); //10:05返回索引值3
            int value = valueList.get(index);
            valueList.set(index, value+1);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }
    }
}
RequestTotalServlet.java
package com.imooc.total;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
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 com.alibaba.fastjson.JSON;

/**
 * Servlet implementation class RequestTotalServlet
 */
@WebServlet("/rt")
public class RequestTotalServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public RequestTotalServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = request.getServletContext();//得到全局context对象
        List<String> timeList = (List)context.getAttribute("timeList");//提取之前创建的
        List<Integer> valueList = (List)context.getAttribute("valueList");
        response.setContentType("text/html;charset=utf-8");
        /*response.getWriter().println(timeList.toString()); //打印输出时间数值数据
        response.getWriter().println("<br/>");
        response.getWriter().println(valueList.toString());*/
        
        Map result = new HashMap();
        result.put("timeList", timeList);
        result.put("valueList", valueList);
        String json = JSON.toJSONString(result);
        response.getWriter().println(json);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}
text3.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test Page</title>
</head>
<body>
<h1>I'm test page 3</h1>
</body>
</html>

<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/echarts.min.js"></script>
<script type="text/javascript" src="js/jquery-3.1.1.js"></script>
</head>
<body>
   <div id="main" style="width: 600px; height: 400px;"></div>
   <script type = "text/javascript">
     //基于准备好的dom, 初始化echarts实例
    var myChart = echarts.init(document.getElementById('main'));
     //指定图表的配置项和数据
    var option = {
        title:{
            text:'Echarts 入门示例'
        },
        tooltip: {},
        legend: {
            data: ['销量']
        },
        xAxis: {
            data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
        },
        yAxis: {},
        series:[{
            name: '销量',
            type: 'bar',
            data: [5,20,36,10,10,20]
        }]
    };
       //使用刚指定的配置项和数据显示图表
       myChart.setOption(option);
    </script>
</body>

入门百度Echarts组件

利用Ajax和Jquery进行前后端通信

Apache ECharts

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script type="text/javascript" src="js/echarts.min.js"></script>
    <script type="text/javascript" src="js/jquery-3.1.1.js"></script>
</head>
<body>
<div id="main" style="width: 600px; height: 400px;"></div>
<script type="text/javascript">
    //每一秒钟向服务器查询一次数据把变化数据重新写入图表
//需要Javascript定时器完成
    function showChat(){//显示图表
        //基础的通讯部分
    $.ajax({
        url:"./rt",
        type:"get",
        dataType:"json",
        success:function(json){
            console.log(json.timeList);
            console.log(json.valueList);
    //基于准备好的dom, 初始化echarts实例
    var myChart = echarts.init(document.getElementById('main'));
    //指定图表的配置项和数据
    var option = {
        title: {
            text: '请求流量分析统计'
        },
        tooltip: {},
        legend: {
            data: ['访问量']
        },
        xAxis: {
           // data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
            data: json.timeList
        },
        yAxis: {},
        series: [{
            name: '访问量',
            type: 'line', //柱状图  line是折线图
           // data: [5, 20, 36, 10, 10, 20] //图中展示的数值 与商法data对应
            data: json.valueList
        }]
    };
    //激活使用刚指定的配置项和数据显示图表
    myChart.setOption(option);
        }
    })
}
       window.setInterval("shwoChart()", 1000);//间隔某时长去执行指定代码
    //每秒钟通过shwoChart()向服务器"./rt"发送请求 对此请求进行排除用RequestTotalListener去改写
</script>
</body>
</html>
RequestTotalListener.java
package com.example.json;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class RequestTotalListener implements ServletContextListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        List timeList = new ArrayList(); //时间数据
        List valueList = new ArrayList(); //具体时间访问量数据
        servletContextEvent.getServletContext().setAttribute("timeList", timeList);//得到最原始的context
        servletContextEvent.getServletContext().setAttribute("valueList", valueList);//启动ServletContext时自动启动倆请求
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest)servletRequestEvent.getServletRequest();
        String url = request.getRequestURL().toString();
        if (url.endsWith("/rt") == true){
            return; //将rt的url排除之外
        }
        //请求初始化 每来一个新的请求监听器都会执行此方法 记录某一时间点的访问量
        //TimeList:10:02  10:03  10:04  10:05
        //ValueList: 5       7     10    1+1=2
        List<String> timeList = (List)servletRequestEvent.getServletContext().getAttribute("timeList");
        List<Integer> valueList = (List)servletRequestEvent.getServletContext().getAttribute("valueList");
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); //按照小时分钟来取
        String time = sdf.format(date); //date对象传入
        if (timeList.indexOf(time) == -1) { //查找对应的数据在集合中是否存在
            //若时间不存在 则追加时间
            timeList.add(time);
            valueList.add(1);//当前有1个请求被创建
            servletRequestEvent.getServletContext().setAttribute("timeList", timeList);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }else { //假设时间存在 不增加新数据 在原有数据加1
            int index = timeList.indexOf(time); //10:05返回索引值3
            int value = valueList.get(index);
            valueList.set(index, value+1);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }
    }
}
RequestTotalServlet.java
package com.example.json;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
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 com.alibaba.fastjson.JSON;

/**
 * Servlet implementation class RequestTotalServlet
 */
@WebServlet("/rt")
public class RequestTotalServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public RequestTotalServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = request.getServletContext();
        List<String> timeList = (List)context.getAttribute("timeList");
        List<Integer> valueList = (List)context.getAttribute("valueList");
        response.setContentType("text/html;charset=utf-8");
        /*response.getWriter().println(timeList.toString());
        response.getWriter().println("<br/>");
        response.getWriter().println(valueList.toString());*/ //将对象进行封装
        
        Map result = new HashMap();
        result.put("timeList", timeList);
        result.put("valueList", valueList);
        String json = JSON.toJSONString(result); //将java对象转换为json字符串
        response.getWriter().println(json);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}

静态数据预加载

通过监听器在上下文初始化的时候放在系统的全局属性中

Channel.java
public class Channel{
    private String channelName;
    private String url;
    ......
}
StaticDataListener.java
package com.imooc.listener;

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

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.imooc.listener.entity.Channel;

public class StaticDataListener implements ServletContextListener{

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        List list  = new ArrayList();
        list.add(new Channel("免费课程" , "http://www.imooc.com/1"));
        list.add(new Channel("实战课程" , "http://www.imooc.com/2"));
        list.add(new Channel("就业班" , "http://www.imooc.com/3"));
        sce.getServletContext().setAttribute("channelList", list);
        //一次性写入到全局属性种
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        
    }

}
web.xml
<listener>
    <listener-class>listener.StaticDataListener</listener-class>
</listener>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:forEach items="${applicationScope.channelList }" var="c"> <!--有效数据提取-->
    <a href="${c.url }">${c.channelName }</a> | 
</c:forEach>
<hr/>
</body>
</html>

FreeMarker(FTL)模板引擎[可以替代jsp]

FreeMarker是一款用java语言编写的模版引擎,它虽然不是web应用框架,但它很合适作为web应用框架的一个组件。

特点:
1.轻量级模版引擎,不需要Servlet环境就可以很轻松的嵌入到应用程序中
2.能生成各种文本,如html,xml,java
3.入门简单,它是用java编写的,很多语法和java相似

基础语法及入门基础

freemarker模板文件(*.ftl)的基本组成部分
1.文本:直接输出的内容部分
2.注释:不会输出的内容,格式为 <#– 注释内容 –>
3.取值(插值):代替输出数据模型的部分,格式为${数据模型}或#{数据模型}
4.ftl指令:Freemarker指令,类似于HTML标记。

字符输出
$(emp.name?if_exists)  //变量存在,输出该变量,否则不输出
${emp.name!}       //变量存在,输出该变量,否则不输出
${emp.name?default("xxx")} // 变量不存在,取默认值xxx
${emp.name!"xxx"}           // 变量不存在,取默认值xxx
常用内部函数
${"123<br>456"?html}   // 对字符串进行HTML编码,对html中特殊字符进行转义
${"str"?cap_first}    // 使字符串第一个字母大写 
${"Str"?lower_case}    // 将字符串转换成小写
${"Str"?upper_case}   // 将字符串转换成大写
${"str"?trim}            // 去掉字符串前后的空白字符
字符串的两种拼接方法
${"hello${emp.name!}"} //输出hello+变量名
${"hello"+emp.name!}   //使用+号来连接,输出hello+变量名
截取子串
可以通过如下语法来截取子串:
<#assign str = "abcdefghijklmn"/>
// 方法1${str?substring(0,4)} // 输出abcd
// 方法2${str[0]}${str[4]} // 结果是ae
${str[1..4]}     // 结果是bcde// 返回指定字符的索引${str?index_of("n")}
日期输出
${emp.date?string('yyyy-MM-dd')} //日期格式
数字输出(以数字20为例)
${emp.name?string.number} //输出20
${emp.name?string.currency} //¥20.00
${emp.name?string.precent} //20%
${1.222?int}          // 将小数转为int,输出1
<#setting number_format="percent"/> // 设置数字默认输出方式('percent',百分比)
<#assign answer=42/>          // 声明变量 answer 42
#{answer}          // 输出 4,200%
${answer?string}      // 输出 4,200%
${answer?string.number}   // 输出 42
${answer?string.currency} // 输出 ¥42.00
${answer?string.percent}  // 输出 4,200%
#{answer}         // 输出 42
比较运算符
表达式中支持的比较运算符有如下几个:= 或 == :判断两个值是否相等.
!= :判断两个值是否不等.
> 或 gt :判断左边值是否大于右边值
>= 或 gte :判断左边值是否大于等于右边值
< 或 lt :判断左边值是否小于右边值
<= 或 lte :判断左边值是否小于等于右边值
算术运算符
FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , % 注意:
(1)运算符两边必须是数字
(2)使用+运算符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串再连接,如:${3 + "5"},结果是:35
FreeMarker中的运算符优先级如下(由高到低排列):

①、一元运算符:!
②、内建函数:?
③、乘除法:*, / , %
④、加减法:- , +
⑤、比较:> , < , >= , <= (lt , lte , gt , gte)
⑥、相等:== , = , !=
⑦、逻辑与:&&
⑧、逻辑或:||
⑨、数字范围:..实际上,我们在开发过程中应该使用括号来严格区分,这样的可读性好,出错少

if逻辑判断(注意:elseif 不加空格)
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
<#else>
...
</#if>
switch(条件可为数字,可为字符串)
<#switch value>
 <#case refValue1>
....
<#break> 
<#case refValue2> 
....
<#break>
 <#case refValueN>
 ....
<#break>
 <#default>
 ....
 </#switch>
集合 & 循环
//遍历集合
<#list empList! as emp>
    ${emp.name!}    
</#list>

//使用<#break>跳出循环
<#if emp_index = 0><#break></#if>

//集合长度判断 
<#if empList?size!=0></#if> //判断=的时候,注意只要一个=符号,而不是==
<#assign l=0..100/>       // 定义一个int区间的0~100的集合,数字范围也支持反递增,如100..2
<#list 0..100 as i>   // 等效于java for(int i=0; i <= 100; i++)
  ${i}
</#list>

// 截取子集合:
empList[3..5] //返回empList集合的子集合,子集合中的元素是empList集合中的第4-6个元素

// 创建集合:
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x>

// 集合连接运算,将两个集合连接成一个新的集合
<#list ["星期一","星期二","星期三"] + ["星期四","星期五","星期六","星期天"] as x>

// 除此之外,集合元素也可以是表达式,例子如下:
[2 + 2, [1, 2, 3, 4], "whatnot"]

// seq_contains:判断序列中的元素是否存在
<#assign x = ["red", 16, "blue", "cyan"]> 
${x?seq_contains("blue")?string("yes", "no")}    // yes
${x?seq_contains("yellow")?string("yes", "no")}  // no
${x?seq_contains(16)?string("yes", "no")}        // yes
${x?seq_contains("16")?string("yes", "no")}      // no

// seq_index_of:第一次出现的索引
<#assign x = ["red", 16, "blue", "cyan", "blue"]> 
${x?seq_index_of("blue")}  // 2

// sort_by:排序(升序)
<#list movies?sort_by("showtime") as movie></#list>

// sort_by:排序(降序)
<#list movies?sort_by("showtime")?reverse as movie></#list>

// 具体介绍:
// 不排序的情况:
<#list movies as moive>
  <a href="${moive.url}">${moive.name}</a>
</#list>
//要是排序,则用
<#list movies?sort as movie>
  <a href="${movie.url}">${movie.name}</a>
</#list>
    
/ 这是按元素的首字母排序。若要按list中对象元素的某一属性排序的话,则用
<#list moives?sort_by(["name"]) as movie>
  <a href="${movie.url}">${movie.name}</a>
</#list>

//这个是按list中对象元素的[name]属性排序的,是升序,如果需要降序的话,如下所示:
<#list movies?sort_by(["name"])?reverse as movie>
  <a href="${movie.url}">${movie.name}</a>
</#list>
    
//Map对象 创建map
<#assign scores = {"语文":86,"数学":78}>

// Map连接运算符
<#assign scores = {"语文":86,"数学":78} + {"数学":87,"Java":93}>

// Map元素输出
emp.name       // 全部使用点语法
emp["name"]    // 使用方括号

// FreeMarker支持如下转义字符:
\" :双引号(u0022)
\' :单引号(u0027)
\\ :反斜杠(u005C)
\n :换行(u000A)
\r :回车(u000D)
\t :Tab(u0009)
\b :退格键(u0008)
\f :Form feed(u000C)
\l :<
\g :>
\a :&
\{ :{
\xCode :直接通过4位的16进制数来指定Unicode码,输出该unicode码对应的字符.
    
// include指令的作用类似于JSP的包含指令:
<#include "/test.ftl" encoding="UTF-8" parse=true>

// 在上面的语法格式中,两个参数的解释如下:
encoding="GBK"  // 编码格式
parse=true    // 是否作为ftl语法解析,默认是true,false就是以文本方式引入,
注意:在ftl文件里布尔值都是直接赋值的如parse=true,而不是parse="true"
    
//import指令
类似于jsp里的import,它导入文件,然后就可以在当前文件里使用被导入文件里的宏组件
<#import "/libs/mylib.ftl" as my>
上面的代码将导入/lib/common.ftl模板文件中的所有变量,
交将这些变量放置在一个名为com的Map对象中,"my"在freemarker里被称作namespace

数据 + 模板 = 结果

数据 [Java代码]
User user = new User();
user.setName(“张三”);

模板 [HTML简化]
< span >
${user.name}
< /span >

结果
< span >
张三
< /span >

  • Freemarker是免费开源的模板引擎技术
  • Freemarker脚本为(Freemarker Template Language)
  • Freemarker提供了大量内建函数来简化开发

JSP与Freemarker

JSP Freemarker
官方标准
执行方式 编译型 解释型
执行效率
开发效率
扩展能力
数据提取 JSTL + EL 内置标签

前端工程师把数据写在ftl 然后后端工程师拿着名称去添加后台数据

package com.example.json;


import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class FreemarkerSample1 {
    public static void main(String[] args) throws IOException, TemplateException {
        //1. 加载模板
        Configuration confige = new Configuration(Configuration.VERSION_2_3_32);
                     // FreemarkerSample1类所在包中加载ftl所在文件
        confige.setClassForTemplateLoading(FreemarkerSample1.class, "");
        Template t = confige.getTemplate("sample1.ftl");
        //2. 创建数据
        Map<String,Object> data = new HashMap<String, Object>();
        data.put("site","百度");
        data.put("url","https://www.abc.com");
        data.put("data",new Date());
        data.put("number",83719.88321);
        Map info = new HashMap();
        info.put("cpu","i5");
        Computer c1 = new Computer("123456","ALIENWARE",1,"李四",new Date(), 12900f)
        data.put("computer",c1);
        //3. 产生输出    向目标输出
        t.process(data, new OutputStreamWriter(System.out));
    }
}
${site}
${url}
<#--类似于EL表达式-->
Computer.java
public class Computer{
    private String sn; //序列号
    private String mode1; //型号
    private int state; //状态 1-在用 2-闲置 3-报废
    private String user; //使用人
    private Date dog; //采购日期
    private Float price; //购买价格
    private Map info; //电脑配置信息
    ......
}
sample1.ftl
<#-- Freemarker取值 -->
${site}
${url}

<#-- !默认值 -->
${author!"不存在的属性"}

<#-- ?string格式化输出 -->
${date?string("yyyy年MM月dd日 HH:mm:ss SSS")}
${number?string("0.00")}

SN:${computer.sn}
型号:${computer.mode1}
状态:${computer.state}
用户:${computer.user}
采购时间:${computer.dop?string("yyyy年MM月dd日")}
采购价格:${computer.price?string("0.00")}

//对map的提取
CPU:${computer.info["cpu"]}
内存:${computer.info["memory"]!"无内存信息"}

FTL(FreeMarker Template Language)取值

  • ${属性名} 取值,可对属性进行计算

  • ${属性名!默认值} 使用默认值[解决空值异常]
    ${author!”不存在的属性”} => 不存在的属性

  • ${属性名?string} 格式化输出
    ${data?string(“yyyy年MM月dd日 HH:mm:ss SSS”)}

    ${number?string(“0.00”)} 小数部位保留两位

if分支判断
<#if 条件1>
  条件1成立执行代码
<#if 条件2>
  条件2成立执行代码
<#if 条件3>
  条件3成立执行代码
<#else>
  其他情况下执行代码
switch分支判断
<#switch value>
  <#case refValue1>
    ...
    <#break>
  <#case refValue2>
    ...
    <#break>
  <#case refValueN>
    ...
    <#break>
<#default>
...
</#switch>

sample1.ftl
<#-- Freemarker取值 -->
${site}
${url}

<#-- !默认值 -->
${author!"不存在的属性"}

<#-- ?string格式化输出 -->
${date?string("yyyy年MM月dd日 HH:mm:ss SSS")}
${number?string("0.00")}
<#if computer.sn == "1234567">
重要设备
</#if>

SN:${computer.sn}
型号:${computer.mode1}
状态:${computer.state}

<#if computer.state == 1>
状态:正在使用
<#elseif computer.state == 2>
状态:闲置
<#elseif computer.state == 3>
状态:已作废
</#if>

<#if computer.user??> //??代表判断对象是否为空,true不为空,false为空
用户:${computer.user}
</#if>
--------------------------------------------------------
<#switch computer.state>  //同一个属性的不同值进行判断
    <#case1>
        状态:正在使用
        <#break>
    <#case2>
        状态:闲置
        <#break>
    <#case3>
        状态:已作废
        <#break>
    <#default>
        状态:无效状态
--------------------------------------------------------
采购时间:${computer.dop?string("yyyy年MM月dd日")}
采购价格:${computer.price?string("0.00")}

//对map的提取
CPU:${computer.info["cpu"]}
内存:${computer.info["memory"]!"无内存信息"}

list迭代列表

<#list students as stu>
  <li>${stu_index}-${stu.name}</li>
</#list>
list迭代Map
<#list map?keys as key>
  ${key}:${map[key]}
</#list>
FreemarkerSample2.java
package com.example.json;


import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class FreemarkerSample1 {
    public static void main(String[] args) throws IOException, TemplateException {
        //1. 加载模板
        Configuration confige = new Configuration(Configuration.VERSION_2_3_32);
          // FreemarkerSample1类所在包中加载ftl所在文件
        confige.setClassForTemplateLoading(FreemarkerSample2.class, "");
        Template t = confige.getTemplate("sample1.ftl"); //得到模板对象
        //2. 创建数据
        Map<String,Object> data = new HashMap<String,object>();
        List<Computer> computers = new ArrayList();
        computers.add(new Computer("123456","ALIENWARE",2,null,new Date(), 12900f, new HashMap()));
        computers.add(new Computer("123456","HP XXX",1,"张三",new Date(), 12900f, new HashMap()));
        computers.add(new Computer("1233145","DELL XXX",3,"李四",new Date(), 12900f, new HashMap()));
        computers.add(new Computer("1234536","ACER XXX",1,"王五",new Date(), 12900f, new HashMap()));
        computers.add(new Computer("12344456","MSI XXX",1,"赵六",new Date(), 12900f, new HashMap()));
        data.put("computers", computers);
        //LinkedHashMap可以保证数据按存放顺序进行提取
        Map computerMap = new LinkedHashMap();
        for(Computer c : computers){
            computerMap.put(c.getSn(), c);
        } //下方新增
        //3. 产生输出    向目标输出
        t.process(data, new OutputStreamWriter(System.out));
    }
}
sample2.ftl
<#list computers as c> //迭代循环 用变量c
序号:${c_index} //迭代变量 index保存了循环的索引,从0开始 +1则从1开始
SN:${c.sn}
型号:${c.mode1}
<#switch c.state>  //同一个属性的不同值进行判断
    <#case1>
        状态:正在使用
        <#break>
    <#case2>
        状态:闲置
        <#break>
    <#case3>
        状态:已作废
        <#break>
    <#default>
        状态:无效状态
</#switch>
<#if c.user??>
状态:${c.state}
</#if>
用户:${c.user}
采购时间:${c.dop?string("yyyy-MM-dd")}
采购价格:${c.price?string("0.00")}
</#list>
-------------------------------------------------------------
<#list computer_map?keys as k >
${k}-${computer_map[k].model}
${computer_map[k].price?string("0.00")}
</#list>

Freemarker内建函数[看成java代码 就是点”.”换成了问号”?”]

freemarker.foofun.cn 在线学习

函数名 说明 示例
lower_case/upper_case 大小写转换 “abcde”?upper_case
cap_first 首字母大写 “”abcde””?cap_first
index_of 查找字符索引 “abcde”?index_of(“b”)
length 返回字符串长度 “abcde”?length
round/floor/ceiling 四舍五入/下取整/上取整 pi?floor
size 得到集合元素总数 students?size
first/last 获取第一个/最后一个元素 students?first
sort_by 按某个属性对集合排序 list?sort_by(“time”)
example:
Map<String,Object> data = new HashMap<String,Object>();
data.put("name", "jackson");
----------------------------
${name?cap_first}
//违禁字母屏蔽
${words?replace("blood", "******")}
${words?index_of("blood")}
//三目运算 
${(words?index_of("blood") != -1)?string("包含敏感词汇","不包含敏感词汇")}

公司共有${computers?size}台电脑
//集合排序 返回一个新的集合
<#list computers?sort_by("price") as c >
    ${c.sn}-${c.price}
</#list>
//集合排序{降序}
<#list computers?sort_by("price")?reverse as c >
    ${c.sn}-${c.price}
</#list>

Freemarker与Servlet整合

web.xml
<servlet>
  <servlet-name>freemarker</servlet-name>
  <servlet-class>freemarkerServlet</servlet-class>
  <init-param>
      <param-name>TemplatePath</param-name>
    <param-value>/WEB-INF/ftl</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>freemarker</servlet-name>
  <url-pattern>*.ftl</url-pattern> //地址映射 输入这样的后缀时自动去/WEB-INF/ftl查找
</servlet-mapping>
test.ftl
这是一个测试FTL文件

localhost:8080/fm-web/test.ftl
employee.ftl
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>员工列表</title>
    <link href="./css/bootstrap.css" type="text/css" rel="stylesheet"/>
    <script type="text/javascript" src="./js/jquery-1.11.1.min.js"></script>
    <script type="text/javascript" src="./js/bootstrap.js"></script>

    <style type="text/css">
        .pagination {
            margin: 0px
        }

        .pagination > li > a, .pagination > li > span {
            margin: 0 5px;
            border: 1px solid #dddddd;
        }

        .glyphicon {
            margin-right: 3px;
        }

        .form-control[readonly] {
            cursor: pointer;
            background-color: white;
        }
        #dlgPhoto .modal-body{
            text-align: center;
        }
        .preview{

            max-width: 500px;
        }
    </style>
    <script>
        $(function () {
            
            $("#btnAdd").click(function () {
                $('#dlgForm').modal()
            });
        })
    </script>
</head>
<body>

<div class="container">
    <div class="row">
        <h1 style="text-align: center">IMOOC员工信息表</h1>
        <div class="panel panel-default">
            <div class="clearfix panel-heading ">
                <div class="input-group" style="width: 500px;">
                    <button class="btn btn-primary" id="btnAdd"><span class="glyphicon glyphicon-zoom-in"></span>新增
                    </button>
                </div>
            </div>

            <table class="table table-bordered table-hover">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>员工编号</th>
                    <th>姓名</th>
                    <th>部门</th>
                    <th>职务</th>
                    <th>工资</th>
                    <th>&nbsp;</th>
                </tr>
                </thead>
                <tbody>
//<c:forEach items="${applicationScope.employees}" var="emp" varStatus="idx">
                <#list employee_list as emp>
                <tr>
                    <td>${emp_index + 1}</td>
                    <td>${emp.empno?string("0")}</td> //整数输出用0初始化
                    <td>${emp.ename}</td>
                    <td>${emp.department}</td>
                    <td>${emp.job}</td>
//<td style="color: red;font-weight: bold">¥<fmt:formatNumber value="${emp.salary}" pattern="0,000.00"></fmt:formatNumber></td>
                    <td style="color: red;font-weight: bold">¥${emp.salary?string("0.00")}</td>
                </tr>
                </c:forEach>
            </table>
        </div>
    </div>
</div>

<!-- 表单 -->
<div class="modal fade" tabindex="-1" role="dialog" id="dlgForm">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">新增员工</h4>
            </div>
            <div class="modal-body">
                <form action="/untitled_war_exploded/create" method="post" >
                    <div class="form-group">
                        <label for="empno">员工编号</label>
                        <input type="text" name="empno" class="form-control" id="empno" placeholder="请输入员工编号">
                    </div>
                    <div class="form-group">
                        <label for="ename">员工姓名</label>
                        <input type="text" name="ename" class="form-control" id="ename" placeholder="请输入员工姓名">
                    </div>
                    <div class="form-group">
                        <label>部门</label>
                        <select id="dname" name="department" class="form-control">
                            <option selected="selected">请选择部门</option>
                            <option value="市场部">市场部</option>
                            <option value="研发部">研发部</option>
                            <option value="后勤部">后勤部</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <label>职务</label>
                        <input type="text" name="job" class="form-control" id="sal" placeholder="请输入职务">
                    </div>

                    <div class="form-group">
                        <label for="sal">工资</label>
                        <input type="text" name="salary" class="form-control" id="sal" placeholder="请输入工资">
                    </div>

                    <div class="form-group" style="text-align: center;">
                        <button type="submit" class="btn btn-primary">保存</button>
                    </div>
                </form>
            </div>

        </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</body>
</html>
Employee.java
public class Employee {
    private Integer empno;
    private String ename;
    private String department;
    private String job;
    private Float salary;
    ...
}
ListServlet.java
package com.imooc.freemarker.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ListServlet
 */
@WebServlet("/list")
public class ListServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ListServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List list = new ArrayList();
        list.add(new Employee(7731,"张三" , "市场部" , "客户代表" , 8000f));
        list.add(new Employee(8871,"李四" , "研发部" , "运维工程师" , 7000f));
        request.getServletContext().setAttribute("employee_list", list);
        request.getRequestDispatcher("/employee.ftl").forward(request, response);
    }
}