首页 > 技术文章 > 网站入门

cqbstyx 2019-01-25 16:34 原文

第二章.创建web项目.
file->new->web project.
设置 java version 1.8
设置 Target Tomcat8.0
web项目下面src JAVA源码目录
WebRoot 网站目录
WEB-INF classes及lib目录.

webroot下添加HTML.
如何创建一个web项目,了解了web项目的一般结构.

2.2部署web项目.
1.创建web项目.

在浏览器打开网页时, 汉字出现乱码?
解决办法:手工修改html,在<head>里 添加 <meta charset='UTF-8' />
在tomcat目录下,<Host>标签内最后添加<Context path="/demo" docBase="E:\JavaWeb\demo0202\WebRoot"  />    
其中, docBase 指向 WebRoot目录的路径 ,path是网站的映射路径 
注意是WebRoot的目录,不是项目目录

第三章 Servlet
servlet 服务小程序.
java web很重要的功能.
在src下面的packge下面新建一个servlet
每次记得修改tomcat的server.xml的docBase
Servlet运行原理
        get/servlet
浏览器(客户端)--------------->Tomcat(服务器)
          <---------------
        200 OK 资源内容

tomcat在接收到客户端请求后,会根据请求的资源路径URI,来分派给不同的Servlet来处理
例如: /SimpleServlet ->my.SimpleServlet

3.2    创建时间服务
1.创建showtimeservlet服务.
2.修改@WebServlet("/ShowTimeServlet")->@WebServlet("/showtime")  //修改的就是uri的地址.
3.doGet代码修改.
response 是应答,request是请求.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestr =sdf.format(new Date()); //当前时间
        response.getWriter().write("Now:" + timestr); //暂时不用中文避免乱码.
        
    }

注意:
1.uri区分大小写.

servlet运行原理
        get/servlet
浏览器(客户端)--------------->Tomcat(服务器)
          <---------------
        200 OK 时间
tomcat在接收到客户端请求后,会根据请求的资源路径URI,来分派给不同的Servlet来处理
例如: /SimpleServlet ->my.SimpleServlet
/showtime->my.ShowTimeServlet.

doGet()可以修改返回数值和方法.

3.3    生成HTML
添加一个Servlet,用于生成HTML页面.
-添加HtmlServlet
-映射URI为 /test/1.html (称为映射路径)

//映射路径  @WebServlet("/test/1.html")

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");     //设置字符集编码
        response.setContentType("text/html");         //设置内容格式
        PrintWriter out =response.getWriter();
        out.write("<html><body> 如何<html><body>");    //返回客户端的数据
    }

servlet运行原理
        get/test/1.html
浏览器(客户端)--------------->Tomcat(服务器)
          <---------------
        200 OK 时间

1.一个URI 可能是一个文件,也可能是一个Servlet
2.客户端不能区分,也没必要区分
3.如果既不是Servlet,又不是文件,返回404错误
4.如果即是Servlet,又是文件呢.

3.4    生成JavaScript
Jscript
@WebServlet("/sample.js")
public class JscriptServlet extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        response.setCharacterEncoding("UTF-8"); // 设置字符集编码
        response.setContentType("text/plain"); // 设置内容格式
        PrintWriter out = response.getWriter();

        // 返回一行javaScript文本
        out.write("var author='仁豪'; ");
        out.write("var name='yyf'; ");
    }

}
写一个.java文件
创建html,导入juery.js,
<html>
  <head>
    <title>RH.html</title>
       <script type="text/javascript" src="js/jquery-3.3.1.min.js" ></script>
       <script type="text/javascript" src="sample.js" ></script>
    

  </head>
  
  <body>
      <div class='main' style="height: 35px; ">    
      </div>
      <div class='ame'>
      </div>
    
  </body>
  
  <script>
      $('.main').html ( author + author );
    $('.ame').html (name + name);
  </script>
</html>

此外tomcat的配置         
<Context path="/demo" docBase="E:\javaweb\demo0201\WebRoot"
        reloadable="true" /> 
docBase写到WebRoot一级目录才行.

4.1    POJO
简单的java类.
-属性:简单类型 int String boolean...
-方法:Getter/Setter
POJO类可以重写toString()


4.2    JSON javascript对象的文本化表示
jar包放在WebRoot下面的WEB-INF 下面的lib里面,自动bulid path

JSON多用于数据传输
{
"id":20111111, //示例
"name":"仁豪",
"phone":"1300099";
}
使用JSON-ORG 或者jsonlib来操作JSON

其中,大括号表示一个JSON对象。在这个对象里,包含4个字段。
例如,
"id"    字段的值为  20180001,是数字类型 ( Number )
"name" 字段的值为 "邵",     是字符串类型 ( String ) 
"sex"  字段的值为 true,是布尔类型 (Boolean)

可以很直观的发现,
如果值是一个String,则应该用双引号,如"邵发"
如果值是一个Number,则不加双引号,如20180001
如果值是一个Boolean,则应该是 true 或者是 false, 不加双引号

加入JSON-java  JSON-lib

        JSONObject jobj = new JSONObject();
        jobj.put("id", 20180001); // Integer
        jobj.put("name", "邵发"); // String
        jobj.put("sex", true);   // Boolean
        jobj.put("cellphone", "13810012345"); // String
        
        String jsonstr = jobj.toString(2);
        System.out.println(jsonstr);


JSONObject里的字段显示顺序是无关的,谁先上、谁在下不影响最终结果。


    // POJO -> JSONObject
    // 如果是POJO对象 ( 已经添加了 getter ),则可以直接转成JSONObject
    public static void test3()
    {
        Student stu = new Student("邵发", 1238909, true, "13810012345");
        JSONObject j = new JSONObject(stu);
        String jsonstr = j.toString(2); // 缩进2
        System.out.println(jsonstr);
    }

JSON的语法格式
用JSON可以表示Object信息,以一对大括号包围,里面可以多个字段。
例如:
{
    "name": "邵发",
    "id": 1208439,
    "sex": true,
    "phone": "13810012345"
}
语法要点:
- 以大括号包围
- 字段名称要加双引号
- 字段的值可以为String, Integer, Boolean, Array, null 等类型
- 每个字段以逗号分隔 (最后一个字段末尾不能加逗号)
- 字段的顺序无关
注:JSONObject在概念上对应于Java里的Map


public class TestJson
{
    public static void test1()
    {
        Student pojo =new Student(20180001,"仁豪",true,"13800000");
        JSONObject jobj =new JSONObject(pojo);
        String js =jobj.toString(2);    //縮放為2是
        System.out.println(js);
    }
    
    public static void test2()
    {
        ArrayList<Student> students = new ArrayList();
        students.add(new Student(20180001,"仁豪",true,"13800000"));
        students.add(new Student(20180002,"x",true,"138000001"));
        students.add(new Student(20180003,"y",true,"138000002"));
        students.add(new Student(20180004,"z",true,"138000003"));
        
        JSONArray jarray =new JSONArray(students);
        System.out.println(jarray.toString(2));
        
    }
    public static void main(String[] args)
    {
        test2();
    }

}

直接添加一个main方法,单元测试.

4.3使用json
public class StudentQuery extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        ArrayList<Student> students = new ArrayList();
        students.add(new Student(20180001,"仁豪",true,"13800000"));
        students.add(new Student(20180002,"x",true,"138000001"));
        students.add(new Student(20180003,"y",true,"138000002"));
        students.add(new Student(20180004,"z",true,"138000003"));
        
        JSONArray jarray =new JSONArray(students);
        
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");      //設置內容格式
        PrintWriter out = response.getWriter();
        
        //返回javascript文本
        out.write(jarray.toString(2));
    }
}

4.4    web项目的调试
java调试的一般方法.
1.打印调试.
2.单步调试
两者结合使用.

单步调试.
以debug方式运行服务器
点debug下面的debug configurations
选择tomcat8.0下面的source(来源)
add,javaproject
勾选目标project文件夹,然后ok ,apply,close.
然后在代码添加断点,在浏览器输入地址.
myeclipse弹出提示是否切换debug页面.
如果关闭,在Windows,show view里面找

source not found  ,第三方jar不用管.

web项目的调试,被调试的主体其实是tomcat程序,tomcat程序会间接的调用我们的servlet代码.

第五章 5.1    数据查询.
查询和显示数据库中的数据.
模拟:一个假的数据库.
DemoDb:用于模拟一个数据库.
实现一个查询功能,可以返回所有数据行.
URL查询.
@WebServlet("/QueryAll")
public class QueryAll extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException
    {
        List<Student> rows = DemoDb.i.list();
        
        // List -> JSONArray
        JSONArray result = new JSONArray(rows);
        
        // 返回应答数据
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");
        Writer writer = response.getWriter();
        writer.write( result.toString(2));
        writer.close();
    }

}

5.2    URL参数
按学号范围进行查询.
比如,查询20180001-20180004.
客户端需要把请求的参数发给服务器.
请求可以附在URL末尾
例如:http://127.0.0.1:8080/demo/QueryById?from=20180001&to=20180004

URL参数的写法规则:
1.以问号引导QueryById?xxxxx
2.多个参数对中间用&分开
示例:?key1=value1&key2=value2&key3=value3
3.中文参数比较复杂.

使用request.getParameter("...")获取URL的参数.
@WebServlet("/QueryById")
public class QueryById extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        //从request 里取得参数
        String strFrom = request.getParameter("from");
        String strTo = request.getParameter("to");    
        //参数处理
        int from = Integer.valueOf(strFrom);
        int to = Integer.valueOf(strTo);    
        //数据查询
        List<Student> rows = DemoDb.i.list(from, to);
        //List->JSONArray
        JSONArray result = new JSONArray(rows);
        //返回应答数据
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");
        Writer writer =    response.getWriter();
        writer.write(result.toString(2));
        writer.close();            
    }

}

5.3    HTTP网络抓包演示
请求/应答模式.

 

 

Wireshark 网络鲨鱼.
可以抓取本机和其他机器的数据交互.
HTTP协议:应答数据.
HTTP/1.1 200状态码.
200-OK,可以访问
403-Forbidden 无权访问
404-Not Found 资源不存在
Content-Length:287
表示正文部分的长度为287字节
正文内容
    头部按行定义,每行末行为\r\n
    头部与正文之间有一个空行.

5.4    Chrome浏览器抓包
请求/应答 模式.
-F12,开发者工具.
-切换到network面板.
headers表示请求 response应答.

第六章
6.1    AJAX
后台提供的服务(接口),前台如何调用?
http://127.0.0.1:8080/demo6/QueryAll
http://127.0.0.1:8080/demo6/QueryById?from=1&to=10000000
前端希望取得后台数据,并且显示在页面上.

AJAX,一种在网页上调用后台接口的方式.
后台接口:指后台代码实现的服务
类似 /demo/QueryAll    /demo/QueryById

jQuery里提供的AJAX调用方法.
示例: $.ajax({...参数...})
其中,
-$.ajax()等同于jQuery.ajax()
-参数里面是一个JS对象

test.html

<!DOCTYPE html>
<html>
<head>
<title>test.html</title>
<meta charset='UTF-8' >
<script type="text/javascript" src="js/query.min.js"></script>
</head>

<body>
    <div class='main'>
        <div class='form'>
            <button class='commit' onclick="query()">查询</button>
        </div>
    </div>
</body>
<script>
    function query() {
         //juery ajax调用的一般写法
         //$.ajax() 或写成jQuery.ajax()是一样的
        $.ajax({
        type:'GET',    //请求类型GET/POST
        url:'QueryAll',        //服务URI,使用相对地址
        dataType:'json',    //期望服务器返回的数据类型
        success:function(resp){        //已经将服务器返回的数据转成JS对象
            console.log(resp);
        }
        });
    }
</script>
</html>

$.ajax()参数是一个JS对象,其中的属性:
type:'GET'
url:接口地址,使用相对路径
success:当服务器返回应答时,调用此function处理(回调方法)
注意:url容易写错!不要加IP,也不要加前缀.

6.2    处理返回的数据.
显示返回的数据
当AJAX完成时,回调方法被调用,前端页面应处理并显示服务器的返回的数据.
演示:
-可以在HBuilder里进行HTML页面编辑
-最基本的JS字符串拼接处理.

    <script>
        function query()
        {
            $.ajax({
                type:'GET',       /* 请求类型 GET / POST */ 
                url:'QueryAll',  /* 服务URI, 用相对地址 */                
                dataType: 'json', /* 期望服务器返回的数据类型 */
                success: function(resp){  /* 已经将服务器返回的数据转成 JS对象 */
                    //console.log(resp);     //将console.log(resp)
                    showResult(resp);    //替换为showResult(resp)方法
                }
            });
        }
        // 格式化数据并显示
        function showResult(result)
        {
            var target = $(".main .content tbody");
            target.html(""); // 清空
            for(var row of result)
            {
                var str = "<tr>"
                    + "<td>" + row.id + "</td>"
                    + "<td>" + row.name + "</td>"
                    + "<td>" + ( row.sex?'男':'女') + "</td>"
                    + "<td>" + row.phone + "</td>"
                    + "</tr>"
                    ;
                target.append( str );
            }
            
            // 如果没有数据,则把下面的提示显示出来
            if(result.length > 0) 
                $(".main .content .empty").hide();
            else 
                $(".main .content .empty").show();
        }
        
    </script>


6.3     参数查询.
在用ajax()访问后台接口时,可以附加请求参数
示例:
    $.ajax({
        type:'GET',
        url:'QueryById',
        data:req,
        ....
        }


1 添加用户输入控件
    <div class='toolbar'>
        <input type='text' class='from' placeholder="开始学号"  />
        -
        <input type='text' class='to' placeholder="结束学号"  />
        
        <button onclick='query()'> 查询 </button>
    </div>

2 ajax() 里添加请求参数
        function query()
        {
            // 请求参数
            var req = {};
            req.from = $('.toolbar .from').val().trim();    //.trim()去掉两边的空格.
            req.to = $('.toolbar .to').val().trim();
            
            $.ajax({
                type:'GET',       /* 请求类型 GET / POST */ 
                url:'QueryById',   /* 服务URI, 用相对地址 */        
                data: req,        /* 附加请求参数 */
                dataType: 'json', /* 期望服务器返回的数据类型 */
                success: function(resp){  /* 已经将服务器返回的数据转成 JS对象 */
                    //console.log(resp);
                    showResult(resp);
                }
            });
        }
注: ajax()会自动的把 data 里的数据构造成 ?from=xxx&to=yyy 的形式附加在 URL后面

6.4    中文URL编码
示例:添加一个QueryByName服务,实现按学生的姓名查询.
此时,需要把一个中文参数传递到后台.
//后台的姓名查询servlet
@WebServlet("/QueryByName")
public class QueryByName extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException
    {
        //从request里取得参数
        String filter = request.getParameter("filter");
        
        //数据查询
        List<Student> rows = DemoDb.i.list(filter);
        
        //List->JSONArray
        JSONArray result = new JSONArray(rows);
        
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");
        Writer writer=    response.getWriter();
        writer.write( result.toString(2));
        writer.close();
    }

}
//前端script
    function query()
        {
            // 请求参数
            var req = {};
            req.filter = $('.toolbar .filter').val().trim();    //.trim()去掉两边的空格.
            
        $.ajax({
                type:'GET',       /* 请求类型 GET / POST */ 
                url:'QueryByName',   /* 服务URI, 用相对地址 */        
                data: req,        /* 附加请求参数 */
                dataType: 'json', /* 期望服务器返回的数据类型 */
                success: function(resp){  /* 已经将服务器返回的数据转成 JS对象 */
                    //console.log(resp);
                    showResult(resp);
                }
            });
        }

中文不能直接放在URL里,
例如:http://xx.x.x/QueryById?filter=张
这样是不支持的,必须把中文字符转成百分号形式的编码.
在Chrome里观测Http交互数据.

URL编码:
'张'->'%E5%BC%A0'
URL解码:
'%E5%BC%A0'->'张'
注:以上均为UTF-8编码.
public class UTF
{
    // '张'->'%E5%BC%A0'
    public static void testEncode() throws Exception
    {
        String str = "张";
        String query = URLEncoder.encode(str, "UTF-8");
        System.out.println("编码后:" + query);
    }

    // '%E5%BC%A0'->'张'
    public static void testDecode() throws Exception
    {
        String str = "%E5%BC%A0";
        String query = URLDecoder.decode(str, "UTF-8");
        System.out.println("编码后:" + query);
    }

    public static void main(String[] args) throws Exception
    {
        testEncode();
        testDecode();
    }
}
小结:
-需要明白URL里的百分号形式编码
-在框架未能正确处理URL编码的时候,应该会自己实现中文的URL编码/解码

6.5    异步调用
ajax()默认是异步方式:
-不等待服务器返回结果,立刻返回.
-当服务器返回结果时,调用回调方法进行处理.
可以想象,与服务器的交互实在另外一个线程里处理的.

6.6    浏览器抓包调试
前端问题,还是后台问题,如何界定
第一步:浏览器抓包
确定问题是前台还是后台
第二步:单步调试
如果前端问题,在前端单步调试JavaScript
如果后台问题,在后台单步调试Java代码.

第七章    
7.1表单
表单,是一种最原始的前后台数据交互方式.
<form method='POST' action='ServiceUri'>
    <input type='text' name='xxx'>
    ...
    <input type='submit'/>
</form>

例:
      <div class='main'>
          <!--  method: GET/POST,通常为POST  action: 即后台接口相对路径 -->
        <form method='POST' action='AddStudent' >        //添加一个AddSutdent.java的Servlet.
            学号: <input type='text' name='id' />  <br>  <!-- name: 即参数名 -->
            姓名: <input type='text' name='name' /> <br>
            手机: <input type='text' name='phone' /> <br>
            <select name='sex' >
                <option value='male'> 男 </option>
                <option value='female'> 女 </option>
            </select> <br>
            <input type='submit' value='提交' />  <!-- type=submit表示自动执行提交功能 -->
        </form>     
        </div>

后台servlet,注意这里使用的是servlet的doPost方法.

@WebServlet("/AddStudent")
public class AddStudent extends HttpServlet
{
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException
    {
        // 因为请求字段里的中文数据,所以要显式设置一下字符编码
        request.setCharacterEncoding("UTF-8");
        
        // 从请求里提取参数字段的值
        int id = Integer.valueOf(request.getParameter("id"));
        String name = request.getParameter("name");
        String phone = request.getParameter("phone");
        boolean sex = "male".equals(request.getParameter("sex")); 
        
        // 添加到数据库
        Student s = new Student(id, name, sex, phone);
        DemoDb.i.add( s );
        
        // 返回应答数据
        JSONObject jresp = new JSONObject();
        jresp.put("error", 0); // 错误码,0表示成功
        jresp.put("reason", "OK"); // 错误原因描述, 如果没有错误则提示OK
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");
        Writer writer = response.getWriter();
        writer.write( jresp.toString(2));
        writer.close();
    }

}

添加一个servlet处理表单请求:
对应关系:
AddSutdent->对应action='AddStudent'
doPost()->对应method='POST'
request.getParameter("xxx")->对应name='xxx'
介绍了表单方式来提交数据.
此种方法,以后基本不会用到.

7.2    表单抓包
POST方式的特点:
1.第一行为POST
2.数据时放在HTTP正文部分的提交数据
3.正文仍要用URL编码


7.3    AJAX POST
使用ajax()也可以发送POST请求
演示:
$.ajax({
    type:'post',    //请求类型GET/POST
    url:'AddStudent',//服务URI,相对地址
    data:req,    //附加请求参数
    ...
});


示例:
  <body>
      <div class='main'>
          <div class='info'>
              学号:<input type='text' class='id' /> <br>
              姓名:<input type='text' class='name' /> <br>
              手机:<input type='text' class='phone' /> <br>
              性别:<select class='sex'>
                  <option value='male'> 男 </option>
                  <option value='female'> 女 </option>
              </select> <br>
              <button onclick='doSubmit()'> 提交</button>
          </div>
        
    </div>
  </body>

  <script>
 function doSubmit()
  {
      //请求参数
      var f = $('.info');
      var req={};
      req.id = $('.id,f').val().trim();
      req.name = $('.name,f').val().trim();
      req.phone= $('.phone,f').val().trim();
      req.sex = $('.sex,f').val().trim();
      
      $.ajax({
          type:'POST',            //请求类型GET/POST
          url:'AddStudent',        //服务URI,相对地址
          data:req,                //附加请求参数
          dataType:'json',        //期望服务器返回的数据类型
          success:function(resp){    //已经将服务器返回的数据转成JS对象.
              console.log(resp);
              if(resp.error == 0)
                  alert('成功');
              else
                  alert('出错:' + resp.reason);
          },
          error:function(jqXHR,textStatus,errorThrown)
          {
              alert("错误:"+jqXHR.status);
          }
      });
  }
  </script>

以后,将以$.ajax()来发送post

第八章    RESTful
8.1    RESTful
RESTful,一种通用的前台后台交互方式
(注:此术语本身没哟严格的定义)
RESTful一般是指这种方式:
1.使用HTTP POST(或者GET)进行数据交互
2.请求数据和应答数据均为JSON格式(或XML)

客户端  请求数据:JSON给服务器
服务器  应答数据:JSON给客户端

请求和应答都是JSON格式,其交互数据为JSON格式,则称为RESTful.

8.2    RESTful的接口实现
前端:在发送请求时,把请求转成JSON字符串
var jsonstr = JSON.stringify(req);
$.ajax({
    type:'POST',
    url:'AddStudent',
    data:jsonstr,    //JSON字符串
});

RESTful后台实现
后台:手工读取请求数据,转成JSONObject,然后再做处理
String reqText = readAsText(request.getInputStream(),"UTF-8");
JSONObject jreq = new JSONObject(reqText);

实现AddStudent
@WebServlet("/AddStudent")
public class AddStudent extends HttpServlet
{
        
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException
    {
        // 读取请求数据, 转成字符串
        String reqText = readAsText(request.getInputStream(), "UTF-8");
        // 转成 JSON
        JSONObject jreq = new JSONObject(reqText);
        
        // 处理请求数据
        int id = jreq.getInt("id");
        String name = jreq.getString("name");
        String phone = jreq.getString("phone");
        boolean sex = "male".equals( jreq.getString("sex")); 
        
        Student s = new Student(id, name, sex, phone);
        DemoDb.i.add( s );
        
        // 返回应答数据
        JSONObject jresp = new JSONObject();
        jresp.put("error", 0); // 错误码,0表示成功
        jresp.put("reason", "OK"); // 错误原因描述, 如果没有错误则提示OK
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");
        Writer writer = response.getWriter();
        writer.write( jresp.toString(2));
        writer.close();        
    }
    
    // 流操作在后面的《网络编程篇》里讲解,属于高级课程,暂不要求大家掌握
    // 大家只需要知道 这个方法实现的功能就行:功能就是从streamIn里读取数据转成字符串
    public String readAsText(InputStream streamIn, String charset)
            throws IOException 
    {
        ByteArrayOutputStream cache = new ByteArrayOutputStream(1024*16);  
        byte[] data = new byte[1024];  
        while (true)
        {
            int n = streamIn.read(data); // n: 实际读取的字节数
            if(n < 0) break; // 连接已经断开
            if(n == 0) continue;// 数据未完 // TODO: 要防止超时 

            // 缓存起来
            cache.write(data, 0, n);            
            if(cache.size() > 1024*512) // 上限, 最多读取512K
                break;
        }  
        
        return cache.toString(charset);
    }
}
前端使用ajax(),后台使用servlet.

8.3    RESTful附加URL参数的处理
一般来说,使用RESTful接口时,所有的请求参数直接放在请求数据JSON中即可
但有的时候,还有少许参数是放在URL里的.
前端处理
在AJAX请求里,需要把参数字符串附加在URL末尾
$.ajax({
    type:'POST',
    url:'AddStudent?mode=admin&token=12930289',
    data:jsonstr, //json字符串
    .....
});

后台处理
在Servlet里,需取出查询字符串,自己解析
String query = request.getQueryString();
HashMap queryParams = parseQuery(query);

    // 读取请求数据, 转成字符串
    String reqText = readAsText(request.getInputStream(), "UTF-8");
    // 转成 JSON
    JSONObject jreq = new JSONObject(reqText);

    //URL末尾,由问号引导的字符串(在RESTful中也可能有部分参数在URL里)
    //例如 mode=admin&token=12930289
    String query=request.getQueryString();
    HashMap<String,String> queryParms= parseQuery(query);

    public HashMap<String,String> parseQuery(String query)
    {
        HashMap<String,String> params = new HashMap<String,String>();
        String[] ppp = query.split("&"); // 用&分隔
        for(String p : ppp)
        {
            String[] kv = p.split("="); // key=value
            String key = kv[0];
            String value = "";
            if(key.length() > 1) value = kv[1]; // 有时候参数里传的是空值
            params.put(key, value); // TODO: value 视情况进行 URLDecoder
        }
        return params;
    }

8.4    通用RESTful服务框架.
AfRestfulService:实现RESTful服务接口的支持的通用基础类.
使用方法.
1.新建一个子类,继承于AfRestfulService
2.重写execute()方法
3.添加映射路径@WebService("/path_of_your_service")

添加一个class AddBookService extends AfRestfulService
由于继承于一个抽象类,需要添加add unimplemented methods
重写execute()方法,由于是创建的class类,需要手动添加映射路径@WebServlet("/AddBook")

@WebServlet("/AddBook")
public class AddBookService extends AfRestfulService
{
    @Override
    protected Object execute(HttpServletRequest httpReq, 
            HttpServletResponse httpResp, 
            JSONObject jreq,
            HashMap<String, String> queryParams) throws Exception
    {
        String title = jreq.getString("title");
        String author = jreq.getString("author");
        String isbn = jreq.getString("isbn");
        String press = jreq.getString("press");
        
        // 后台处理部分 .. 省略 ...
        
        System.out.println("书名:" + title + ",作者: " + author
                + ", ISBN: " + isbn + ",出版社:" + press);    
        
        return null; // JSONArray, JSONObject ,
    }
}
介绍了AfRestfulService的使用,可以用于快速实现一个RESTful服务接口
所有网站相关的基础API封装在af.web.*9.1    学生管理-新增与列表
1.1 后台
添加 StudentListService,映射路径 /StudentList

DemoDb里添加 save() 与 load() 方法
数据实际存储位置 c:\webdata\ 目录,使用JSON格式存储

1.2 前台
新建list.html

包含 js/jquery.min.js  和  js / afquery.js 

使用 Af.rest () 来调用 RESTful 接口 (Af.rest() 在 afquery.js 里定义)

使用 AfTemplate来处理数据 ( AfTemplate在 afquery.js里定义)

( AfTemplate 的使用说明在本文档的第三部分)


二、增加记录
2.1 后台
新建 StudentAddService ,映射到 /StudentAdd
保存数据到 DemoDb

2.2 前台
新建 add.html
调用 Af.rest() 与后台交互

三、AfTemplate 使用说明
为了方便格式化HTML,所以提供一个AfTemplate 工具,在afquery.js 里定义。
示例,
        var html = "<label target='{#id}'> {#title} </label>";
        var tp = new AfTemplate(html);
        var data = {
            id: 1234,
            title: '阿发你好',
        };
        var output = tp.replace(data);
        console.log(output);
则输出为:
    <label target='1234'> 阿发你好 </label>        

其中,html为模板,data为数据      
AfTemplate负责把模板中的变量{#VVV} 替代成数据的值( html + data => output)



9.2    学生管理-删除
一、    删除记录
1 后台

修改 DemoDb : 增加 remove(id) 方法,用于删除一个学生的记录

新增 StudentRemoveService : 映射到 /StudentRemove

2 前台
修改 list.html :

(1)    表格增加一列
        <td> 
            <button data='{#id}' onclick='doRemove(this)'> 删除 </button>
        </td>
(2)    点删除按钮时,删除本行
function doRemove ( e )
{
}

代码:
添加一个新的    webservlet.
@WebServlet("/StudentRemove")
public class StudentRemoveService extends AfRestfulService
{

    @Override
    protected Object execute(HttpServletRequest httpReq, HttpServletResponse httpResp, JSONObject jreq,
            HashMap<String, String> queryParams) throws Exception
    {
        int id = jreq.getInt("id");
        
        if( DemoDb.i.remove(id))
        {
            DemoDb.i.save(); // 数据有变化时,调用 save()同步到文件
        }
        
        return null;
    }

}
在list页面添加删除按钮的方法.

        //e:DOM元素对象,即被点击的按钮
        function doRemove(e)
        {
            var req={};
            req.id=$(e).attr("data");    //取得'data'属性
            Af.rest("StudentRemove", req, function(data) {
                //删除该行
                $(e).parent().parent().remove();
                alert("删除成功");
                })
        }

// DemoDB里实现按学号删除
    public boolean remove(int id)
    {
        Iterator<Student> iter = data.iterator();
        while(iter.hasNext())
        {
            Student s = iter.next();
            if(s.getId() == id)
            {
                iter.remove();
                return true;
            }
        }
        return false;
    }

9.3    查询功能.
写一个webservlet
@WebServlet("/StudentQuery")
public class StudentQueryService extends AfRestfulService
{

    @Override
    protected Object execute(HttpServletRequest httpReq,
            HttpServletResponse httpResp, JSONObject jreq,
            HashMap<String, String> queryParams) throws Exception
    {
        String name =jreq.getString("filter");
        List<Student> rows = DemoDb.i.list(name);
        JSONArray result =new JSONArray(rows);
        return result;
    }

}

写一个query方法
        function query()
        {
            // 请求参数
            var req = {};
            req.filter =$('.toolbar .filter').val().trim();
            
            Af.rest("StudentQuery", req, function(data){
                // 后台传回来的 error , reason 在 afquery.js 里已经解出
                // 这里只需要处理 data
                showResult(data);
            });
        }

查询方法
    // 按名字查询
    public List<Student> list(String name)
    {
        List<Student> result = new ArrayList<Student>();
        for(Student s: data)
        {
            if(s.name.indexOf( name ) >=0 ) 
            {
                result.add( s );
            }
        }
        return result;
    }

10.1    XML方式配置
Servlet的配置方式有两种:
-注解方式@WebServlet("/Example")
-XML方式(web.xml)
新建一个webproject,选择jdk1.8+tomcat8.0
然后next,next勾选Generate web.xml deployment descriptor
在webroot下面的web-inf下面有一个web.xml
web.xml有source和design两种模式,source是源码模式

  <servlet>
      <servlet-name>Example1</servlet-name>
      <servlet-class>my.Example1</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>Example1</servlet-name>
      <url-pattern>/Example1</url-pattern>
  </servlet-mapping>

加载过程
XML方式:
-tomcat启动后
-加载WEB-INF\web.xml里的Servlet
注解方式:
-tomcat启动
-扫描WEB-INF\classes下的Servlet

xml方式:可读性高
注解方式:操作简单

web.xml是一个必须掌握的配置文件
<welcome-file>:指定默认页
即http://127.0.0.1:8080/demol/    URL里不指下文件名时默认加载的文件
web.xml时应该保持存在的,因为以后还需要添加其他配置

10.2    Serlvet实例
现在有一个Servlet:
"/Example1"->my.Example1
考虑:当用户多次访问该服务时,后台创建了几个Example1对象?
验证
在my.Example1的构造方法里添加打印构造
观察:
-当tomcat启动时,会立刻创建Example1对象?
-当用户访问该服务时.
-当用户再次访问该服务时.

Servlet实例
Tomcat启动时,会创建一个全局的列表
servletConfigList
{
    "/Example1","my.Example1",servlet(null)
    "/Example2","my.Example2",servlet(null)
    ...
}
默认在启动时,不会立即创建Servlet实例

当Tomcat接收到访问时:
1.检查URI
2.找到servletConfigList,创建实例
    servlet = new my.Example1()
3.处理请求
    servlet.doGet()/doPost()
当客户端再次访问该URL时,会怎么处理.

随应用启动
一个WEB项目称为Web Application
可以配置Servlet在应用启动时实例化
  <servlet>
      <servlet-name>Example1</servlet-name>
      <servlet-class>my.Example1</servlet-class>
     <load-on-startup>1</load-on-startup>        //添加load-on-startup
  </servlet>
  <servlet-mapping>
      <servlet-name>Example1</servlet-name>
      <url-pattern>/Example1</url-pattern>
  </servlet-mapping>

或者@WebServlet(name="Example2",urlPatterns="/Example2",loadOnStartup=1)

规则:
1.<load-on-startup>表示,当该应用被启动时,自动创建Servlet实例,其数字表示启动顺序.
2.当存在多个自启动Servlet时,数字越小越优先启动.
3.如果没有<load-on-startup>配置,则默认不会创建实例,直到第一次被访问时才创建实例.

-默认情况下,第一次访问才创建
-该实例不会销毁,直到应用关闭
-可以配置成一启动就创建.
另外知道了怎么检查一个类是单例还是多例

10.3    Servlet生命周期
Lifecycle
指一个对象何时被创建,何时被销毁
生命周期的回调
init():在创建对象后,调用此方法进行初始化
-----------
doGet():当用户以GET方式访问此服务时调用
doPost():当用户以POST方式访问此服务时调用
-----------
destroy():应用退出前,调用此方法做善后处理    

10.4    Tomcat请求处理流程
并发访问:指多路客户端同时访问同一台服务器
Tomcat服务器支持多路并发访问
多个用户可以同时访问一个Tomcat服务器

当接收到一个请求时,会创建一个线程来处理该请求
new Thread(){        //以下为伪代码
    run(){
        if(匹配servlet路径)servlet.doGet/doPost()
        else if(匹配本地文件)读取文件内容发送给客户端
        else "404 Not Found"
    }
}

注意
单凭一个路径,不能判定是Servlet还是文件
例如:/images/1230.jpg    看上去是一个文件,但也可能是一个服务
/SimpleQuery    它看上去是一个服务,但也可能是只是一个文件
小结
提到了多线程处理的概念,每一个请求都在各自的线程里处理的.

第十一章
11.1     线程重入
指多个线程同时运行同一对象的同一方法.
对于Servlet来说,要求doPost()/doGet()可重入.

比如:/Example1=>my.Example1
当多个客户端同时访问/Example1时
-找到匹配的Servlet对象s
-创建多个线程
-每个线程里都调用s.doPost()方法
显示,此时要求doPost()方法可以重入

不可重入最简单的原则
简单原则:在Servlet的所有方法里,均不使用类的属性
很简单,如果不使用属性,就不会产生重入的问题.
不把数据存到属性里,直接传递调用.

11.2    单例多例转换
单例转多例,时一种更简单的办法,可以保证doPost()是可以重入的.
@WebServlet("/Example2")
public class Example2 extends HttpServlet
{

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        new ServiceHandler(request,response).handle();
    }

}

public class ServiceHandler
{
    HttpServletRequest request;
    HttpServletResponse response;
    
    public ServiceHandler(HttpServletRequest request,HttpServletResponse response)
    {
        this.request =request;
        this.response=response;
    }
    
    public void handle()throws ServletException, IOException
    {
                        
    }
}

Example2:单例,仅一个实例
ServiceHandler:多例,每个线程创建一个实例
单例转多例的设计方法,
把所有的处理放在多例ServiceHandler里,就可以即好,又简单的避免"不可重入"问题
在ServiceHandler类里,可以随意使用自己的属性,不会产生问题.


12.1    本地文件
web项目里,文件的路径可以指定绝对路径,也可以指定相对路径
绝对路径:例如 C:/webdata/student.json
相对路径.    

示例:实现一个功能,在页面输入标题和内容后将数据保存到文件.
-保存到data\目录下
-每次根据标题创建一个文件
创建Sava.html,前端页面,实现sava function
    <body>
        <div class='main'>
            <div>
                <input type='text' class='title' />
                
            </div>
            <br>
            <div>
                <textarea class='content' style="height:100px; width:200px;"></textarea>
            </div>
            <div>
                <button onclick="save()">save</button>
            </div>
        </div>
    </body>
    <script>
        window.save=function()
        {
            var req={};
            req.title=$('.title').val().trim();
            req.content=$('.content').val().trim();
            Af.rest("SaveContent",req,function(data){
                alert("保存成功");
                $(".main input").val("");
                $(".main textarea").val("");
            })
        }
    </script>

创建一个Servlet  SaveContent实现存储
@WebServlet("/SaveContent")
public class ServletSaveContent extends AfRestfulService
{

    @Override
    protected Object execute(HttpServletRequest httpReq,
            HttpServletResponse httpResp, JSONObject jreq,
            HashMap<String, String> queryParams) throws Exception
    {
        //取得参数
        String title =jreq.getString("title");
        String content=jreq.getString("content");
        //获取项目实际路径
        String webrootPath =httpReq.getServletContext().getRealPath("/");
        File webroot = new File(webrootPath);
        
        //存储到$(webroot)/data/xxx.txt
        File dataDir=new File(webroot,"data/");
        dataDir.mkdirs();
        
        File f=new File(dataDir,title+".txt");
        AfTextFile.write(f, content, "UTF-8");
        return null;
    }

}

可以在代码获取当前项目的部署路径    
使用httpReq.getServletContext().getRealPath("/");        //path路径
注:web项目没有"当前目录"的概念.

12.2     资源文件
在Java项目里,资源文件是指存放在Classpath里的文件
非源码文件就称为资源文件resource(资源)

演示:在src下的xml文件,会被MyEclipse自动拷贝到classes目录下
(注:也可以使用jpg txt等文件类型试验)

资源文件路径:和类路径类似
例如:/config.xml    /my/example/logo.png

在项目里添加一个配置文件/course.xml 配置有课程的信息.
InputStream stream = getClass().getResourceAsStream("/course.xml")
SAXReader reader =new SAXReader();
Document doc =reader.read(stream);
stream.close();

可以在程序里访问资源文件
InputStream stream = getClass().getResourceAsStream("/course.xml")
-资源文件是只读的
-资源文件路径和类路径写法相同
-程序实际访问不是src下的文件,而是classes下的文件.

第十三章    通用服务框架
13.1通用的Web服务框架,使用这个框架可以很快捷的添加web服务.

添加AfGenericService,并在web.xml里添加配置
注意:
(1)URI匹配规则:*.api表示所有后缀为*.api的请求都由这个Servlet处理
(2)<load-on-startup>表示当应用被加载时就创建Servlet实例

    <servlet>
        <servlet-name>AfGenericService</servlet-name>
        <servlet-class>af.web.service.AfGenericService</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AfGenericService</servlet-name>
        <url-pattern>*.api</url-pattern>
    </servlet-mapping>

http://127.0.0.1:8080/demo/de1.api任意以.api结尾的都由AfGenericService处理.

public class AfGenericService extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        // 处理请求数据    
        try{
            handleRequest(request, response);
        }        
        catch(Exception e)
        {
            e.printStackTrace();
            response.sendError(500, e.getMessage());
            return;
        }
    }
    
    private void handleRequest(HttpServletRequest request, 
            HttpServletResponse response) throws Exception
    {
        // 从URL中解析API的名字
        // servletPath: "/.../hello.api"
        String servletPath = request.getServletPath();
        int p1 = servletPath.lastIndexOf('/');
        int p2 = servletPath.lastIndexOf('.');
        String apiName = servletPath.substring(p1 + 1, p2);    //substring(a,b)从a开始取直到b之前
        System.out.println("服务名:" + apiName);
        
        // 发送应答给客户端
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain");
        //response.setHeader("Connection", "close");
        Writer writer = response.getWriter();
        writer.write( "OK,收到您的请求 :" + apiName );
        writer.close();    
    }

}

13.2    通用服务框架2
单例转多例的设计方法,根据不同的服务名,创建不同得到对象来处理.
对应关系:
Hello.api->my.HelloApi
HowOld.api->my.HowOldApi
AreYouGood.api->my.AreYourGood.api

        // 根据服务名, 创建不同的实例进行处理 ( 单例 -> 多例 )
        AfGenericApi instance = null;
        if("Hello".equals(apiName))
            instance = new HelloApi();
        else if("HowOld".equals(apiName))
            instance = new HowOldApi();
        else if("AreYouGood".equals(apiName))
            instance = new AreYouGoodApi();
        else if("Time".equals(apiName))
            instance = new WhatTimeNowApi();
        else
            throw new Exception("不支持这个服务: " + apiName);
        
        // 读取请求数据 和 URL里的参数
        String charset = "UTF-8";
        String strReq = AfServiceUtils.readAsText(request.getInputStream(), charset, MAX_REQUEST_SIZE);
        String query = request.getQueryString(); 
        HashMap<String,String> queryParams = AfServiceUtils.parseQuery(query, charset);
        
        // 读取请求数据, 转成字符串, 转成 JSON
        instance.httpReq = request;
        instance.httpResp = response;
        instance.queryParams = queryParams;
        instance.charset = charset;
        String strResp = instance.execute(strReq); // 具体的请求处理在execute()里

api继承于抽象类AfGenericApi
在各个api中重写execute().

13.3    通用服务框架3
使用反射技术改造框架    
创建xml,new file,选择目录src下面创建af-service.xml   右键properties   other UTF-8
用af-service.xml来描述对应关系:
Hello.api->my.HelloApi
HowOld.api->my.HowOldApi
AreYouGood.api->my.AreYourGood.api

af-service.xml
<?xml version="1.0"    encoding="UTF-8"?>
<config>
    <service name="Hello" class="my.HelloApi" />
    
    <service name="HowOld" class="my.HowOldApi" />
    
    <service name="AreYouGood" class="my.AreYouGoodApi" />
    
</config>
然后在AfGenericService中重写init()方法.        //init():在创建对象后,调用此方法进行初始化
    @Override
    public void init() throws ServletException
    {
        //从xml配置文件中读取配置
        try
        {
            loadConfig();
        }catch(Exception e)
        {
            e.printStackTrace();
            throw new Error("af-service.xml 格式不正确!启动终止启动!");
        }
    }
再添加一个loadConfig()方法来实现读取
    // 从 af-service.xml 中获取配置
    private void loadConfig() throws Exception
    {
        InputStream stream = this.getClass().getResourceAsStream(
                "/af-service.xml");
        SAXReader reader = new SAXReader();
        Document doc = reader.read(stream);
        stream.close();
        
        Element root = doc.getRootElement();
        List<Element> xServiceList = root.elements("service");
        for (Element e : xServiceList)
        {
            String name = e.attributeValue("name");
            String clazzName = e.attributeValue("class");            
            configs.put(name, new ConfigItem(name, clazzName));
        }

    protected HashMap<String, ConfigItem> configs = new HashMap<String, ConfigItem>();

af-config.xml中的配置项
    // af-service.xml 中的配置项
    class ConfigItem
    {
        public String name;       // 服务接口名
        public String clazzName;  // 类名
        public Class  clazz;      // 类的实体
        public String charset = "UTF-8";
        
        public ConfigItem(String name, String clazzName)
        {
            this.name = name;
            this.clazzName = clazzName;
        }
    }

框架代码和业务代码分离
在af.web.service.*里不再包含my.*里的类

13.4    通用服务框架4        //AfRestfulApi代码未看.
基本通用服务框架,来实现RESTful接口:
RESTful的特点:请求JSON,应用JSON
所以,建立一个AfRestfulApi作为基类统一处理

添加RESTful接口
步骤:
1.创建一个类,继承于AfRestfulApi
2.在af-service.xml里添加描述

13.5    通用服务框架的使用
如何在一个新的web项目下使用af-web-1.0.jar

13.6使用afservice框架改造9.4学生管理系统
afweb.jar里不仅包含afservice,还将包含其他框架.

14.1    创建JSP
JSP, Java Server Page
Java服务端页面,一种前端和后端混合的开发方式.
历史上曾经流行的JSP,PHP,ASP三大服务端页面之一.

新建JSP页面
在WebRoot目录下,右键,新建一个JSP页面
观察JSP的内容形式:
-整体上还是HTML
-部分以<% %>引起来的内容,时特殊代码.

<%@page import="java.text.SimpleDateFormat"%>
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'showtime.jsp' starting page</title>
    
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
  <%
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  String timestr =sdf.format(System.currentTimeMillis());
  out.write(timestr);    //PrintWriter
   %>
    This is my JSP page. <br>
  </body>
</html>

JSP是一种在HTML中嵌入JAVA代码的页面
<% %>中间的是特殊代码

14.2     JSP运行原理
1.tomcat会把每一个JSP文件自动转换为Java类
(观察tomcat\work\目录下的生成物)

2.客户端请求*.jsp时,由*.class进行处理
所以,JSP本质上就是Servlet

JSP所有的规则,都是为了方便转换为JAVA代码
例如:
<%@ %>用于设置页面的参数
<%  %>直接代码
<%= %>变量引用
上下文对象:request ,response, out

JSP的缺陷
开发效率问题:
前端代码和后端代码混杂,不利于开发管理
运行效率问题:
生成Java代码运行效率低.
JSP技术只能是一种历史.

14.3    JSP代码分离
<%@ %>用于设置页面的参数(放在页面顶部)
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
language语言,import导入的包,pageEncoding字符集格式,如果需要使用中文应该使用"UTF-8"

<%  %>直接代码(应杜绝使用)
<%= %>变量引用
上下文对象:request ,response, out

<jsp:include page="xxx.jsp" /> 包含另一个页面

如果一定要使用JSP,那么java代码尽量不要写在JSP里面,减少HTML和java代码的混杂

1.新建一个Java类
my.jsp.JspSupport    //Support支持

public class JspSupport    
{    
    HttpServletRequest request;    
    public JspSupport(HttpServletRequest request)
    {
        this.request = request;
    }    
    public JSONArray getList()
    {
        List<Student> rows = DemoDb.i.list();
        return new JSONArray(rows);
    }
}

把相关的处理放在Java类里。例如,添加一个 getList() 方法,返回所有学生的数据。

2 在JSP里引用
先在顶上指定字符集,并导入 my.jsp.*
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="my.jsp.*"%>

然后
var data = <%= new JspSupport(request).getList() %>;

这样,就在最大程度上对 HTML 与 Java代码进行了分离。

介绍了JSP代码分离的实现方法
把业务代码放在Java类里,添加方法:
-输入参数request,response
-返回值JSONObject/JSONArray
这样就可以最大程度上分离了HTML和Java代码.

第15章
15.1    JavaWeb和JDBC
JavaWeb:用于Java实现的网站后台
JDBC:用Java实现的与数据库交互
一般情况下,网站的数据要存储在数据库里
在JavaWeb里使用JDBC技术,是自然而然的事情.

一般网站的构架
客户端浏览器1------->互
客户端浏览器2------->联------>Web服务器(Tomcat)<---->数据库(MySQL)
客户端浏览器3------->15.2    数据库支持(afsql)
一,数据库的设计
导入数据库 af_school.sql
二,在Web项目中添加数据库支持
1.在WEB-INF\lib目录下添加jar包
2.在src目录下添加c3p0-config.xml
并修改里面的数据库配置:数据库IP,数据库名,用户名,密码等
3.新建packge,添加C3P0Factory
4.生成POJO类
打开POJO生成器,生成POJO类,把生成的Java拷贝到项目里.

15.3     数据库测试
写一个TestDb.java
package my.dbutil;
import java.util.List;
import my.db.*;
public class TestDb
{
    public static void listStudent()throws Exception
    {
        String sql="select * from student";
        List<Student> rows =C3P0Factory.executeQuery(sql, Student.class);
        System.out.println("获取到:" + rows.size());
    }

    public static void addStudnet()throws Exception
    {
        Student s =new Student();
        s.setId(20180100);
        s.setName("xxxx");
        s.setSex(false);
        s.setPhone("13996000000");
        C3P0Factory.insert(s);
    }

    public static void main(String[] args)
    {
        try
        {
            addStudent();
            listStudent();
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }

}

获取到:12

15.4    RESTful框架支持
-WEB-INF\lib
-web.xml
-af-service.xml
-添加API...


15.5    网站的一般架构

一般网站的构架
客户端浏览器1------->互
客户端浏览器2------->联------>Web服务器(Tomcat)<---->数据库(MySQL)
客户端浏览器3------->网           公网服务器

客户端:浏览器,app等.

客户端为什么不直接访问数据库服务器

16.1    文件的上传
文件上传:即通过网页的方式,打开文件对话框,选择文件并上传到服务器
比如:发生email时选择附件,修改用户头像等..

前端:使用<form>
后端:使用Servlet接收文件数据
(借助apache commons工具类解析请求数据)
一,前端
//encode type
<form method="post" action="FileUploadService" enctype="multipart/form-data">
<input type="file" name="fileUpload" /> <br>
<input type="submit" value="上传" /> <br>
</form>
注意:<form> 标签要添加一个属性 enctype
<input type="submit" /> 定义提交按钮。提交按钮用于向服务器发送表单数据。数据会发送到表单的 action 属性中指定的页面。
<input type="file" /> 用于文件上传。
<input type="file"/>文件选择按钮,用于打开文件对话框
当点submit按钮时,浏览器会自行将文件传给后台

二,后端
1.添加commons库的支持
commons-fileupload-1.3.1.jar
commons-io-2.4.jar
都是apache.org提供的基础开发包,免费开源.
2.添加FileUploadService

小结:
1.浏览器会把文件的数据传递到后台.
数据的格式是HTTP POST multi-part
2.后台创建Servlet,处理POST请求
使用apache commons fileupload 的API来解析请求数据,存储到本地文件里

16.2     JS文件上传
使用JavaScript可以实现文件上传
(不再使用<form>标签)
优点:
-可以显示上传进度,可以取消上传
-界面更加灵活,美观.

后台代码不变,添加upload2.html.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript" src="js/jquery.min.js" ></script>
        <script type="text/javascript" src="js/afquery.js" ></script>
        
        <style>
            .main{
                
            }
            
            .main input{
                margin: 20px;
            }
            .main .upload{
                background-color: cornflowerblue;
                color: #fff;
                border: 1px solid #ccc;
                border-radius: 1px;
                padding: 8px 18px;
            }
        </style>
    </head>
    <body>
        <div class='main'>
            <input type='file' class='filebutton' style='display:none' onchange='fileSelected()'  /> <br>
            <button class="upload" onclick='openFileDialog()' > 选择文件上传 </button>
       </div>
    </body>
    
    <script>
        
        // 点击普通按钮
        function openFileDialog()
        {
            // 模拟触发点击事件
            $('.filebutton').click();
        }
        
        // 用户选择了一个文件 onchange事件被触发
        function fileSelected()
        {
            var fbutton = $('.filebutton')[0]; // DOM
            var file = fbutton.files[0]; // fbutton.files 可能一次选了多个文件
            fbutton.value = ''; // 清空选择
            startUpload ( file ); // 开始上传
        }
        
        // 开始上传, 参数为 File 对象
        function startUpload( file )
        {
            var uploadUrl = "FileUploadService";
            
            // 手工构造一个 form 对象
            var formData = new FormData();
            formData.append('file', file); // 'file' 为HTTP Post里的字段名, file 对浏览器里的File对象
                    
            // 手工构造一个请求对象,用这个对象来发送表单数据
            // 设置 progress, load, error, abort 4个事件处理器
            var request = new XMLHttpRequest();        
            request.upload.addEventListener("progress", window.evt_upload_progress, false);
            request.addEventListener("load", window.evt_upload_complete, false);
            request.addEventListener("error", window.evt_upload_failed, false);
            request.addEventListener("abort", window.evt_upload_cancel, false);            
            request.open("POST", uploadUrl ); // 设置服务URL
            request.send(formData);  // 发送表单数据
        }
        
        window.evt_upload_progress = function (evt) 
        {
            if (evt.lengthComputable)
            {
                var progress = Math.round(evt.loaded * 100 / evt.total);                
                Af.log ("上传进度: " + progress);        
            }            
        };
        window.evt_upload_complete = function (evt)
        {
            if(evt.loaded == 0)
            {
                Af.log ("上传失败!");
            }
            else
            {
                Af.log ("上传完成!");
                var response = JSON.parse(evt.target.responseText);
                   Af.log (response);
            }            
        };         
        window.evt_upload_failed = function (evt) 
        {            
            Af.log  ("上传出错"); 
        };
        window.evt_upload_cancel = function (evt) 
        {
            Af.log( "上传中止!");    
        };

        
    </script>
</html>


styple='display:none' 隐藏
onchange='fileSelcted()'表示点击时触发
小结:
学习了使用JS控制文件上传的原理
在这个原理的基础上,可以灵活的实现多种形式的上传.比如,多个文件同时上传...

 

16.3    JS文件上传工具
JS文件上传工具类:AfFileUploader(在afquery.js里提供)
-文件上传
-进度及事件
-支持取消操作uploader.cancelUpload()
-支持上传条判断(如文件大小判断)


1 包含 jquery 和 afquery
    <script type="text/javascript" src="js/jquery.min.js" ></script>
    <script type="text/javascript" src="js/afquery.js" ></script>

2 添加一个 <input type='file' />
    <input type='file' class='filebutton' style='display:none'  />

3 创建 AfFileUploader
        var uploader = new AfFileUploader();
        uploader.setButton('.filebutton');  // 设置file input控件
        uploader.setUploadUrl('FileUploadService'); // 设置服务URL
        uploader.setLogEnabled( false );
        uploader.setObserver(observer); // 设置监听器

4 创建 AfFileUploader 的监听器 
        var observer = {};        
        // 上传事件处理 'start' 'progress' 'complete' 'error' 'abort'
        observer.uploadHandleEvent = function( msg , uploader) 
        {
            if(msg == 'progress')
            {
                Af.log("进度: " + uploader.progress);
            }
            else if(msg == 'complete')
            {
                Af.log("完成上传");
            }
        }        
        // 上传前的检查
        observer.uploadTestFile = function ( uploader) 
        {
            if(uploader.file.size > 1000000)
            {
                alert("文件太大!");
                return false;
            }
            return true;
        }

5 打开文件选择框
        function selectFile()
        {
            uploader.openFileDialog();
        }

16.4    上传路径
在本例中,上传路径放在WebRoot/upload/下
放在这有什么好处
文件的绝对URL
绝对URL:
/demo1601/upload/123.jpg
其中,/demo1601称为ContextPath,是部署应用的时候配置的值.

提供一个简便的处理办法,直接把上传的文件放在WebRoot目录下,
以便直接访问.
也可以存在WebRoot目录之外.

17.1    通用文件上传框架
通用文件上传框架(单例转多例)
AfFileUploadService
AfFileUploadHandler

映射路径*.upload,根据不同的服务名交给不能的Handler来处理.
下载项目源码,对照文字教程.


注:
- 调试网页时注意浏览器强制刷新 CTRL + F5 
- 请拷贝示例项目下的 afquery.js 到你的项目 ( 不同章节的afquery.js 可能略有差异 ,以本章节的为准 )

1 web.xml
配置 web.xml,映射路径 *.upload
    <!-- 通用文件上传服务框架 -->
    <servlet>
        <servlet-name>AfFileUploadService</servlet-name>            
<servlet-class>af.web.fileupload.AfFileUploadService</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AfFileUploadService</servlet-name>
        <url-pattern>*.upload</url-pattern>
    </servlet-mapping>


2 af-service.xml
里面可以添加多个文件上传处理器
    <upload name="BasicFile" class="my.BasicFileUpload" />


3 AfFileUploadService
根据不同的名字,创建不同的处理器进行处理
(1)    解出服务器
(2)    从服务器找到相应的处理类
(3)    new出对象
(4)    处理

处理过程:
-    上传开始前,调用 tmpFile = handler.uploadStarted() , 由Handler 决定临时文件的位置
-    上传过程中,调用 handler.uploadProgress( fileSize) ,由 Handler决定最大文件大小
-    上完完成后,调用 handler.uploadComplete() ,由 Handler 决定给客户端的返回数据

 

推荐阅读