Sevlet+Ajax+jQuery学习总结

来源:互联网 发布:javascript编程精粹 编辑:程序博客网 时间:2024/06/11 13:08
day01 
1 1)单机程序:不需要网络就能运行的程序
  2)网络应用程序
     1)主机、终端
       a、特点:
        主机负责所有得分业务计算和数据的管理
      终端提供简单的操作界面
       b、优点:
           可靠,安全
       c、缺点:
            昂贵,而且扩展困难
      2)clien/server
          两层的c/s
          a、特点:
             使用数据库充当服务器(不仅管理数据,还要处理大部分的业务逻辑) client主要负责提供操作
         界面和部分业务逻辑的处理
          b/优点
            开发的效率高(client一般使用vb,vc,dephi登语言)
          c 缺点
             可移值性差(比如,换数据库,需要重写业务逻辑,
    因为数据库编程语言是依赖于数据库的)。另外,也
    不适当开发大型的应用(因为client需要跟数据库建立
    一个持续的网络连接)。
     三层的c/s
        a、特点
          将所有的业务逻辑写在应用服务器里面,数据库负责数据库的管理,client负责提供操作界面。
        b、优点:
          可移植性好。适合开发大型的应用
        c、缺点
              client需要单独安装和维护,开发相对复杂(需要自定义协议,需要开发通信协议)
     3)browser/server
         a、特点
           clien使用浏览器。应用服务器有web服务器来充当,数据库任然负责数据的管理
         b、优点:
          client不在需要单独安装和维护,开发相对简单一些(浏览器与web服务器已经提供了通信机制,使用    标准的http协议来通信)
2、什么是servlet?
    sun公司制定的一种扩展web服务器功能的组件规范。
    1)扩展web服务器功能
         (需要事先写好html文件,并且保存到服务器制定的文件夹)
         web服务器能够处理动态资源(需要计算,生成相应的html) 
    2)组件规范
       a、什么是组件?
            符合一定规范,实现部分功能,需要部署到响应的容器上才能运行的软件模块。
       b、容器是什么?
           符合一定规范,为组件提供运行环境的程序。
3、如何开发servlet?
      1)tomcat的安装和简单的使用
          www.apache.org
      2)步骤
         step1,写一个java类,实现Servlet接口后者继承HttpServlet抽象类
         step2:编译 java -cp servlet -api.jar -d . helloServlet
         step3:打包(建立一个具有如写结构的文件夹)
            appname
              WEB-INF
                CLASS(字节码文件)
                lib(可选,放.jar文件)
                web.xml(部署描述文件)
         注意:除了appname可以自己命名,其他不可以更改
        step4,部署
            将step3生成的文件夹拷贝到容器特定的文件件下面,比如,tomcat对应的是webapps文件夹。
        step5,启动服务器,访问servlet
          http://ip:port/appname/servlet-url
4、使用工具(myeclipse)来开发一个简单的servlet
         step1.启动MyEclipse,仍MyEclipse管理tomcat
5、常见错误
day02
1、servlet是如何运行的?
 比如在浏览器地址栏输入:
 http://ip:port/web01/hello
 step1,浏览器依据ip,port建立与servlet容器
 (servlet容器也是一个简单的web服务器)之间的连接。
 step2,浏览器将请求参数、请求资源路径等等打包
 (要按照http协议的要求)。
 step3,浏览器将请求数据包发送给servlet容器。
 step4,容器收到请求之后,对请求数据包进行解析(
 拆包),然后将解析之后的结果封装到request对象上,
 同时,容器还会创建一个response对象。
 step5,容器依据请求资源路径("/web01/hello")找到
 应用所在的文件夹,然后依据web.xml找到对应的
 servlet配置(servlet的类名),然后容器创建该servlet
 对象。
 step6,容器调用servlet对象的service方法(会将事先
 创建好的request,response作为参数传递进来)。
 step7,servlet可以通过request对象获得请求参数,
 进行相应的处理,然后将处理结果缓存到response对
 象上。
 step8,容器从response对象上获取之前处理的结果,
 然后打包发送给浏览器。
 step9,浏览器拆包(解析容器返回过来的响应数据包)
 ,依据获取的数据生成相应的页面。
 
 http://ip:port/web02/hello?name=jetty
 "?"表示后面是请求参数,"name"是请求参数名,
 "jetty"是请求参数值。
 
2、常见错误及解决方式
 (1)404
  a,错误原因:
   依据请求资源路径找不到对应的资源。
  b,解决方式:
   依据http://ip:port/appname/servlet-url
  检查你的请求地址。
   检查<servlet-name>是否一致。
 (2)500
  a,错误原因:
   系统错误
  b,解决方式:
   检查servlet类有没有继承HttpServlet。
   检查<servlet-class>的类名是否正确。
   检查程序是否正确(比如在没有检查请求参数
   的情况下就做类型转换)。
 (3)405
  a,错误原因
   容器找不到service方法。
  b,解决方式
   检查service方法的签名(方法名、返回类型、
   参数类型、异常类型)。
3、http协议(了解)
 (1)什么是http(超文本传输控制协议)协议
  由w3c制订的一种网络应用层协议,用来规范
 浏览器与web服务器之间通信的过程及数据格式。
  1)通信的过程:
   step1,浏览器建立与web服务器之间的连接。
   step2,浏览器将请求数据打包并发送给web
   服务器。
   step3,服务器将响应数据打包并发送给浏览器。
   step4,服务器关闭连接。
  如果浏览器要再发请求,必须重新建立一个
 新的连接。
  2)特点
   “一次请求,一次连接”,优点是web服务器可以
  利用有效的连接个数为尽可能多的客户端(浏览器)
  服务。
 (2)数据包的结构
  1)请求数据包
   a,请求行:请求方式(get/post) 请求资源路径 
       协议的类型和版本
   b,消息头: 是一些key,value对,由w3c定义,
   可以由通信的双方彼此发送,比如,浏览器
   可以发送一个"user-agent"消息头给服务器,
   告诉服务器,浏览器的类型和版本。
   c,实体内容:
     只有发送post请求时,实体内容才会有
    数据(即请求参数)。如果发送的是get请求,
    请求参数会添加到请求资源路径的后面。
  2)响应数据包
   a,状态行: 协议类型和版本 状态码  状态描述
    状态码:是一个三位数字,由w3c定义的,表
    示服务器处理请求的一种状态。
      200:正常
      500:系统异常
      404:依据请求资源路径找不到对应的资源
   b,消息头:
     服务器也可以发送一些消息头给浏览器,
    比如"content-type",告诉浏览器,服务器返回
    的数据类型和编码。
   c,实体内容:
     程序处理的结果
4、get请求与post请求
 (1)哪一些情况下,浏览器会发送get请求?
  a,直接在浏览器地址栏输入某个地址。
  b,点击链接
  c,表单默认的提交方式
 (2)哪一些情况下,浏览器会发送post请求?
  a,设置表单method="post"。
 (3)get请求的特点 
  a,请求参数会添加到请求资源路径的后面,只能添加
 少量的参数(因为请求行只有一行,大约只能存放2K
 左右的数据)。
  b,请求参数会显示在浏览器地址栏)路由器会记录
 请求地址)。
 (4)post请求的特点
  a,请求参数添加到实体内容里面,可以添加大量的
 参数。
  b,相对安全,但是,post请求不会对请求参数进行
 加密处理(可以使用https协议来保证数据安全)。
 
5、如何获得请求参数值?
 1) String request.getParameter(String paramName);
 注意:
  如果paraName对应的值不存在,返回null。
 2) String[] request.getParameterValues(String 
 paramName);
  当有多个参数名相同的时候,用该方法。
6、编码相关的问题
 (1)servlet如何输出中文?
  a,乱码问题产生的原因
   默认情况下,out.println会使用"iso-8859-1"
  来进行编码。
  b,解决方式
   response.setContentType(
    "text/html;charset=utf-8");
   注意,编码格式只要支持中文即可,比如,
  还可以设置为 gbk。
   这行代码的作用有两个:
   作用1:生成一个消息头(content-type),告诉
  浏览器,服务器返回的数据类型和编码格式
     作用2:也设置了out.println在输出时使用的
  编码格式。
 (2)表单有中文参数,如何处理?
  a,乱码问题产生的原因
   当表单提交时,浏览器会对表单中的中文使用
  浏览器打开表单所在的页面时的编码格式进行编码。
  b,解决方式:
   方式一:
    step1,要保证表单所在的页面按照指定的
   编码格式打开。
    <meta http-equiv="content-type" 
   content="text/html;charset=utf-8">
    step2,让服务器按照指定的编码格式进行
   解码。
     request.setCharacterEncoding("utf-8");
    只对post请求有效。
   方式二:
    step1,同上。
    step2,
    name = new String(
    name.getBytes("iso-8859-1"),"utf-8");
 常识:
  a,java语言在内存当中以unicode编码格式(字符集)
  来保存一个字符。
  b,编码:将unicode编码格式对应的字节数组转换成
  某种本地编码格式(gbk,utf-8,gb2312)对应的字节
  数组。
  c,解码:将某种本地编码格式对应的字节数组转换成
  unicode编码格式对应的字节数组。
day03
1、使用jdbc访问数据库
 step1,将jdbc驱动拷贝到WEB-INF\lib下。
 step2,要注意异常的处理。
  
2、mysql数据库的简单使用
 (1)登录mysql
  mysql -uroot;
 (2)查看当前有哪些数据库
  show databases;
 (3)创建一个新的数据库
  比如,创建一个名称jsd1308db的数据库,
  该数据库使用的字符集是utf-8
  create database jsd1308db default
  character set utf8;
 (4)使用某个数据库
  use jsd1308db;
 (5)查看当前数据库有哪些表
  show tables;
 (6)建表
  create table t_emp(
   id int primary key auto_increment,
   name varchar(50),
   salary double,
   age int
  )type=innodb;
  
  type=innodb的作用:让这个表支持事务。
  auto_increment: 自增长列,当插入记录时,
  数据库自动为该列赋一个唯一的值。
练习:
  写一个ListEmpServlet,以表格的方式展现所有
 员工的信息。
 比如  http://ip:port/web03_lab/list
  
day04:
1、dao (data access object 数据访问对象)
 (1)dao是什么?
  封装了数据访问逻辑的模块。
   数据访问逻辑:指的是针对数据资源的访问,
  数据资源可以是数据库、文件、目录服务器等等。
  大部分情况下,都是用数据库来存放数据。
 (2)如何写一个dao?
  step1,实体类
  比如,可以设计一个Employee类,与t_emp
  表对应。
  step2,dao接口
   在该接口当中,声明一系列方法,这些方法
  不要涉及任何具体的实现技术。
   比如:
   EmployeeDAO接口
   public ResultSet findAll();  error
   public List<Employee> findAll();
  step3,实现dao接口
   比如:
   EmployeeDAOImpl类,使用jdbc技术来实现
   数据库访问。
  step4,工厂
   "工厂"是一种设计模式,工厂为调用者提供
  符合接口要求的对象(即产品)。
2、将中文数据插入数据库
 要注意两点:
  1)数据库要支持中文,即数据库要设置
 正确的字符集(比如,gbk,utf-8)
  create database jsd1308db
  default character set utf8;
  2)jdbc驱动要能够进行正确的编码和解码:
  jdbc驱动在插入中文数据时,要做编码处理;
  在查询中文数据时,要做解码处理。
  mysql的jdbc驱动的某些版本,不能够正确地
  进行编码和解码,必须要加上两个参数:
   jdbc:mysql://localhost:3306/jsd1308db?
   useUnicode=true&characterEncoding=utf8
 
 练习:
  使用dao完成删除员工操作。
  
3、servlet容器如何处理请求资源路径
 比如,在浏览器地址栏输入
 http://ip:port/web04_2/abc.html
 请求资源路径("/web04_2/abc.html")
 step1,容器要依据请求资源路径找到应用所在的
 文件夹,接下来,容器会假设访问的是一个servlet,
 然后查找web.xml文件中与之匹配的servlet。
 step2,匹配(查找web.xml文件中与之匹配的servlet):
  方式一: 精确匹配,要求
  web.xml文件当中,必须这样配置:
   <url-pattern>/abc.html</url-pattern>
  方式二: 通配符匹配,使用"*"匹配任意的
  0个,1个或者多个字符。比如
   <url-pattern>/*</url-pattern>
  方式三:后缀匹配,使用"*."开头,后接任意的1个或者
  多个字符。比如 
   <url-pattern>*.do</url-pattern>
   匹配所有以".do"结尾的请求。
 step3,如果以上都不匹配,容器会查找相应的文件,
 如果找到,则返回,找不到,返回404。
 
4、如何让一个servlet处理多种请求?
 step1,使用后缀匹配模式
 step2,分析请求资源路径
  //获取请求资源路径
  String uri = request.getRequestURI();
               
  
练习:
  将员工信息管理中的5个servlet合并成一个(ems03)。
  ftp上的ems02.zip是员工信息管理2.0的所有代码。
  
   
   
day05:
day05
1、servlet生命周期
 (1)什么是servlet生命周期?
        servlet容器如何创建servlet对象、如何初始化、
 如何调用servlet对象的方法处理请求,以及如何
 销毁servlet对象的整个过程。
 (2)生命周期的四个阶段:
  1)实例化
   a,什么是实例化
   容器调用servlet的构造器创建一个servlet
   对象,在默认情况下,不管有多少个请求,容器
   只会创建一个servlet对象。
   b,什么时候实例化?
      情况1:在默认情况下,容器只有收到请求之后,
   才会创建servlet对象。
      情况2: 容器在启动的时候,就将某些servlet
   对象创建好。这些servlet必须配置一个参数
   <load-on-startup>参数</load-on-startup>
   参数:
    大于等于0的整数。
    值越小,优先级越高。
  2)初始化
   a,什么是初始化
      容器创建好servlet对象之后,会立即调用
   init方法。
   b,怎样实现初始化处理逻辑?
       b1,一般情况下,不需要写init方法,因为
   GenericServlet已经实现了init方法:
       将容器创建的ServletConfig对象保存下来,
   并且提供了getServletConfig方法来获得该对象。
      调用了一个空的init方法(该init方法用于子类
   去override)。
      b2,如果要实现自己的初始化处理逻辑,只要
   override init()方法。
      b3,初始化方法只会执行一次。
      b4,ServletConfig对象可以用来访问。servlet的初始化参数。
  3)就绪
   a,什么是就绪
     servlet容器收到请求之后,会调用servlet对象的service方法来处理请求。
   b,如何编写业务处理逻辑?
    方式一:override HttpServlet的service方法。
    HttpServlet的service方法是这样实现的:
    依据请求类型调用doGet或者doPost方法,但是,
    doGet方法和doPost方法只是简单地抛出了一个异常。
   方式二:override HttpServlet的doGet和doPost方法。
  4)销毁
   a,什么是销毁
      容器依据自身的算法来决定是否销毁
   servlet对象。容器在销毁servlet对象之前,
   会调用servlet对象的destroy方法。
   b,destroy方法只会执行一次。
 (3)servlet生命周期相关的几个接口与类
  a,Servlet接口
   init(ServletConfig config)
   service(ServletRequest req,ServletResponse res)
   destroy()
  b,GenericServlet抽象类:
   实现了Servlet接口中的部分方法(实现了
   init,destroy方法)。
  c,HttpServlet抽象类:
   继承了GenericServlet,实现了service方法。
  d,ServletConfig接口 
  e,ServletRequest接口是HttpServletRequest
  接口的父接口,ServletResponse接口是
  HttpServletResponse的父接口。
 
2、jsp(java server page java服务器端页面技术)
 (1)什么是jsp?
     sun公司制订的一种服务器端动态页面生成技术规范。
   因为直接使用servlet虽然也可以生成动态页面,但是过于繁琐(大量使用out.println语句),维护也困难,所以,sun公司制订了jsp规范:
    jsp实际是一个以".jsp"为后缀的文件,该文件
  主要由html和少量的java代码组成,容器会将jsp
  文件先转换成一个对应的servlet然后再去执行。
 (2)如何写一个jsp文件
  step1,写一个以".jsp"为后缀的文件。
  step2,添加如下的内容:
   1) html(css,javascript): 直接写。
   2) java代码
    a, java代码片断:
     <%   java代码   %> 
    b, jsp表达式:
     <%=  java表达式 %>
   3) jsp隐含对象
    a,什么是隐含对象?
     在jsp文件里面,不用声明和创建,就可以
    直接使用的对象,比如out,request,response。
    b,为什么隐含对象可以直接使用?
     容器会自动添加声明和创建这些对象的语句。
   4)指令
    a,什么是指令?
     告诉容器,在将jsp文件转换成java类
    (也就是servlet)时做一些额外的处理,比如
    导包。
    b,语法
     <%@ 指令名称 属性名称=属性值 %>
    c, page指令
     import属性:导包,比如
     <%@page import="java.util.*"%>
      <%@page import="java.util.*,java.text.*"%>
    contentType属性:设置response.setContentType的内容。
    pageEncoding属性:告诉容器,jsp文件的编码格式。因为有部分
    容器默认会使用iso-8859-1去解码(容器要读取jsp文件的内容),如                                果添加了该属性,容器就可以进行正确地解码了。
 (3)jsp是如何执行的?
  step1, 容器要将jsp文件转换成一个java类
 (其实质就是一个servlet)。
  a, html (css,js) ------> service方法里面,
      使用out.write输出。
  b, <%        %> ------> service方法里面,
       照搬。
  step2, 容器对该servlet类进行编译、实例化、
  初始化、调用service方法处理请求。
day06:
day06
1、转发
 (1)什么是转发?
  一个web组件(servlet或者jsp)将未完成的处理
 通过容器交给另一个web组件继续完成。
  常见的情况是:一个servlet获得数据之后,
  转发给一个jsp,由这个jsp来负责展现这些数据。
 (2)如何转发?
  step1,绑订数据到request对象上。
   request.setAttribute(String name,Object obj);
  step2,获得转发器
   //url: 转发的目的地地址
  RequestDispatcher rd = request.getRequestDispatcher(String url);
  step3,转发
   rd.forward(request,response);
   //依据绑订名称找到绑订值,如果值不存在,
   //返回null。
   Object request.getAttribute(String name);
 (3)转发的特点
  a,转发之后,浏览器地址栏的地址不变。
  b,转发的目的地仅限于同一个应用。
 (4)转发需要注意的问题
  a,转发之前,不能够调用out.close()。
  b,转发之前,容器会先清空response对象上
  缓存的数据。
  
练习:
  使用转发完成员工列表
  
2、include指令
 (1)作用:将file属性所指定的文件的内容插入到指令所在的位置。
 (2)用法:
  <%@include file="要包含的文件"%>
  
3、处理servlet运行时产生的异常
 1)方式一
  使用转发
 2)方式二
  将异常交给servlet容器来处理
  step1,将异常抛给容器,比如
   throw new ServletException(e);
  step2,配置一个错误处理页面。
  <error-page>
     <exception-type>javax.servlet.ServletException</exception-type>
     <location>/error.jsp</location>
    </error-page>
    告诉容器,捕获到指定的异常之后,调用location指定的页面。
4、路径问题
 (1)什么是路径问题?
  链接地址、表单提交地址、重定向地址、
 转发的地址应该如何填写。
 (2)什么是相对路径
  不以"/"的路径
   比如 <form action="add.do">
 (3)什么是绝对路径
  以"/"开头的路径
 (4)如何写绝对路径
  链接地址、表单提交地址、重定向地址从应用名
 开始写;转发从应用名之后开始写。
  //获得应用名
  String request.getContextPath();
 
day07:
1、转发与重定向的区别
 (1)转发所涉及的各个组件可以共享同一个
 request对象和同一个response对象,而
 重定向不行。
  当容器收到请求,会创建request,response
 ,当容器发送响应之后,会立即删除request,response。
 也就是说,request,response的生存时间很短暂
 (一次请求与响应期间)。
 (2)转发的目的地地址必须是同一个应用内部某个
地址,而重定向地址不限。
 (3)转发之后,浏览器地址栏的地址不变;而
 重定向会变。
 (4)转发是一件事未做完,而重定向是一件事已经
 做完,然后再去做另一件事。
2、状态管理
 (1)什么是状态管理?
  将浏览器与web服务器之间多次交互当作一个
 整体来看待,并且,将多次交互所涉及的数据
 (即状态)保存下来。
 (2)如何进行状态管理?
  1)客户端的状态管理
   将状态保存在客户端(浏览器),比如cookie。
  2)服务器端的状态管理
   将状态保存在服务器端,比如session。
 (3)cookie
  1)什么是cookie?
  当浏览器访问服务器时,服务器会发送少量的数据
 给浏览器(使用set-cookie消息头),浏览器会将这些数据
 保存下来;当浏览器再次访问服务器时,会将之前
 保存的这些数据发送给服务器(使用cookie消息头)。
  2)创建cookie
  Cookie c = new Cookie(String name,String value);
  response.addCookie(c);
  3)查询cookie
  Cookie[] cookies = request.getCookies();
  注意:该方法有可能返回null。
  String cookie.getName();  //获得cookie的名称
  String cookie.getValue();  //获得cookie的值
  4)cookie的生存时间
   cookie.setMaxAge(int seconds);
  seconds:单位是秒
  seconds > 0: 浏览器会把cookie保存到硬盘上,
    超过指定的时间(seconds),浏览器会删除
    该cookie。
      < 0: 缺省值,浏览器会把cookie保存到
      内存里面,只要浏览器关闭,cookie
      就会被销毁。
      = 0: 删除cookie
       比如,要删除一个名叫userprofile的cookie:
        Cookie c = new Cookie("userprofile","");
        c.setMaxAge(0);
        response.addCookie(c);
  练习:
   写一个Find_AddCookieServlet,该servlet
  先查询有没有一个名称为"userprofile"的cookie,
  如果有,则显示该cookie的值;否则,创建
  该cookie。
  4)编码问题
   cookie只能保存合法的ascii字符,中文很显示
  不是ascii字符,需要将其转换成合法的ascii字符。
   String URLEncoder.encode(String str,
   String code);
   比如   URLEncoder.encode("李白","utf-8");
   URLDecoder.decode(String str,String code);
  5)路径问题
   a,什么是路径问题?
    浏览器在访问服务器的某个地址的时候,会
   比较cookie的路径是否与该地址匹配 ,只有
   匹配的cookie才会发送。
   b,匹配的规则
    要访问的地址必须是cookie的路径或者其
   子路径。
   c,cookie有一个缺省的路径,值等于创建该
   cookie的web组件的路径:
    比如: /web07/sub01/addCookie.jsp添加了
   一个cookie,则该cookie的缺省的路径等于
   "/web07/sub01"。
    /web07/findCookie.jsp  error
    /web07/sub01/findCookie2.jsp  ok
    /web07/sub01/aaa/findCookie3.jsp ok
   d,修改cookie的路径:
    cookie.setPath(String path);
    一般情况下,会将path设置为应用名,比如 
    cookie.setPath("/web07");
  添加一个cookie
   Cookie c = new Cookie("cookie1",
   URLEncoder.encode("李白","utf-8"));  
   c.setMaxAge(3600);
   c.setPath("/appname");
   response.addCookie(c);
  6)cookie的限制
   a, cookie可以被用户禁止。
   b, cookie保存在客户端(浏览器),不安全,
   所以,如果要将敏感数据(比如帐号、密码)
   保存在客户端,一定要加密。
   c, cookie保存的数据大小有限制,大约是
   4k左右(跟浏览器有关系)。
   d, 浏览器保存的cookie的数量也有限制,大
   约是300个左右(跟浏览器有关系)。
   
 (4)session(会话)
  1)什么是session
   浏览器访问服务器时,服务器会创建一个
  session对象(该对象有一个唯一的id,一般称之为
  sessionId),然后,服务器在默认情况下,会使用
  cookie机制(使用set-cookie消息头)将sessionId发
  送给浏览器。浏览器再次访问服务器时,会将
  sessionId发送给服务器,服务器依据sessionId
  找到之前创建的session对象。
  2)如何获得一个session对象
   方式一
                HttpSession s =request.getSession(boolean flag);
              当flag = true时:
   先查看请求当中是否有sessionId,如果没有,则创
  建一个session对象;如果有,则依据sessionId查找
  对应的session对象,如果找到,则返回,找不到,
  创建一个新的session对象。
             当flag = false时:
   先查看请求当中是否有sessionId,如果没有,则
   返回 null;如果有,则依据sessionId查找
   对应的session对象,如果找到,则返回,找不到,
     返回null。
   方式二
    HttpSession s = 
    request.getSession();
   等价于  request.getSession(true)。
  3)常用方法
   //获得sessionId
   String getId(); 
   setAttribute(String name,Object obj);
   Object getAttribute(String name);
   removeAttribute(String name);
  4)session超时
   a,什么是session超时?
    容器会将空闲时间过长的session对象删除掉。
   容器默认的超时时间限制一般是30分钟。
   b,修改容器缺省的超时限制
    conf/web.xml
     <session-config>
            <session-timeout>30</session-timeout>
        </session-config>
       也可以将以上配置放到某个具体的应用的
       web.xml文件里面。
      c, setMaxInactiveInterval(int seconds);
     5)删除session
      invalidate()   
      
 
day08:
1、session验证
 (1)登录
  step1,建表
   drop table if exists t_user;
   create table t_user(
    id int primary key auto_increment,
    username varchar(50) unique,
    pwd varchar(30),
    name varchar(255),
    gender char(1)
   )type=innodb;
   insert into t_user(username,pwd,name,gender)
   values('tom','test','李白','m');
  step2,实体类(User)
  step3,DAO(UserDAO) 
  step4,ActionServlet
  step5, login.jsp,  main.jsp
 (2)session验证
  step1,登录成功以后,绑订数据到session
  对象上。
   比如: 
     session.setAttribute("user",user);
  step2,对于需要保护的资源(必须登录成功以后,
  才能访问的地址),添加session验证代码。
   比如:
     Object obj = session.getAttribute("user");
     if(obj == null){
      response.sendRedirect("login.jsp");
                                        return;
     }
     
 练习:
   完成登录模块(可以使用美工提供的页面)
   
2、验证码
 练习: 生成一个验证码,要求长度为5,并且随机
从"A~Z,0~9"中选取。
 练习: 完成注册模块(使用美工提供的页面)
  如果用户名被占用,提示用户,否则将用户
 信息插入数据库并跳转到登录页面。
day09:
1、摘要加密
 
 (1)摘要加密的特点
  
  a,不可逆性: 按照摘要加密算法生成的
密文(也叫摘要),即使知道了 加密算法,也
不能够反推出明文。
  
  b,唯一性:不同的明文,对应唯一的摘要。
2、如果禁止cookie,如何继续使用session?(了解)
 
 (1)可以使用url重写来解决这个问题
 
 (2)什么是url重写?
  
      浏览器访问服务器上的某个地址时,需要使用
服务器提供的地址(该地址后面会添加sessionId)。
 
 (3)编程
  
    a, response.encodeURL(String url);
 该方法用于链接地址、表单提交地址的处理。
  
    b, response.encodeRedirectURL(String url);
 该方法用于重定向地址的处理,比如
  
           response.sendRedirect(
response.encodeRedirectURL("list.do"));
  
    c,转发不用考虑。 
  
3、session的优缺点(跟cookie相比)
 
1),优点
  
a,安全(session将状态保存在服务器端,而
cookie将状态保存在浏览器端)。
  
b,保存的数据量更大(cookie大约只能保存4k左右)。c,保存的数据类型更丰富(cookie只能保存字符串)。
 
2),缺点
  
session要将状态保存的服务器端,会占用服务器
更多的内存空间,而cookie将状态保存在客户端(
 浏览器),对服务器不会占用过多的内存空间。
 
4、过滤器
 
(1)什么过滤器
  
servlet规范当中定义的一种特殊的组件,用来
拦截servlet容器的调用过程并进行相应的处理。
 
(2)怎样写一个过滤器
  step1,写一个java类,实现Filter接口。
  
        step2,在doFilter方法里面,编写相应的拦截处理
代码。
 
         step3,配置过滤器(web.xml,让容器知道哪一些请求
需要拦截)。
 
(3)过滤器的优先级
  
当有多个过滤器都满足过滤条件,则容器按照
<filter-mapping>出现的先后顺序来调用各个过滤
器。
 
(4)初始化参数
  step1,在web.xml文件里面,使用
<init-param>
<param-name>length</param-name>
 
<param-value>20</param-value>
</init-param>
    step2,调用
 String FilterConfig.getInitParameter(String paramName);
   
(5)过滤器的优点
    
a,可以实现代码的“可插拔性":即增加或者减少
    某个功能模块,不会影响程序的正常执行。
    
b,可以将多个模块相同的处理逻辑集中写在
过滤器里面,方便代码的维护。
 
练习:
   写一个过滤器,检查评论的字数,如果超过
  10个字符,则提示“评论的字数过多"。
  
5、监听器
 
(1)什么是监听器
  
servlet规范当中定义的一种特殊的组件,用于监听
servlet容器产生的事件并进行相应的处理。
  
容器产生的事件主要有两大类:
  
1)生命周期相关的事件:容器创建或者销毁
  
request,session,servlet上下文时产生的事件。
  
2)绑订相关的事件:容器调用了request,session,
  
servlet上下文的setAttribute,removeAttribute
产生的事件。
 
(2)如何写一个监听器
  
step1,写一个java类,实现相应的监听器
 接口。比如,要关心session的创建和销毁,则
需要实现HttpSessionListener。
  
step2,将监听处理逻辑写在相应的方法里面。
  
step3,注册监听器(web.xml)
 (3)servlet上下文
  
1)什么是servlet上下文
容器在启动之后,会为每一个web应用都创建一个符合ServletContext接口要求的对象,该对象称为servlet上下文。
   
唯一性: 一个web应用一个servlet上下文。
   
一直存在: 只要容器不关闭,servlet上下文
会一直存在。
  
2)如何获得servlet上下文
   a, ServletConfig
   
                        b, FilterConfig
   
                        c, GenericServlet
   
                        d, HttpSession
  都提供了getServletContext方法。
  
3) servlet上下文的作用
   
    a,绑订数据
setAttribute,removeAttribute,getAttribute
   
注意:
    request,session,servlet上下文都能绑订数据,
区别:
    
区别1: 绑订的数据存在的时间不一样,request < session < servlet上下文。
    
如果都能解决绑订的问题,应该优先使用生命
   
周期短的。
    
区别2: 访问的范围不一样,request对象上
    
绑订的数据可以被同一个请求中所涉及的各个
    
组件访问;session绑订的数据可以被同一个
    
会话所涉及的组件访问;servlet上下文绑订的
    
数据可以被同一个应用所涉及的所有组件访问。
   
b,访问全局的初始化参数
    
b1,什么是全局的初始化参数:
    
同一个应用所包含的各个组件都可以访问的
    
初始化参数。
    
b2,如何配置
     
<context-param>
        
<param-name>compay</param-name>
        
<param-value>IBM</param-value>
       
</context-param>
      
b3,如何访问
       
ServletContext.getInitParameter(String 
paramName);
     
c, 依据逻辑路径(path)获得实际部署时的物理路径
      
String getRealPath(String path); 
 
  
当容器启动,开始一个任务(每隔5秒钟,输出当前
 
系统时间),当容器卸载该应用时,关闭这个任务。
 
  
Timer TimerTask 定时处理
  
Quartz 开源框架 
  
  
   
  
 
 

day10:
1、上传文件(扩展)
 (1)编程步骤
  step1,设置表单的提交方式为"post",
 还要设置表单的enctype属性值为
 "multipart/form-data";
  比如:
   <form action="" method="post"
   enctype="multipart/form-data">
  step2,在服务器端,不能够使用
 request.getParameter方法了,要使用
 InputStream request.getInputStream方法。
 此时,需要分析InputStream。一般使用一些
 工具(比如,apache提供的commons-fileupload)来
 分析这个InputStream。
2、servlet的线程安全问题
 (1)servlet存在线程安全问题的原因
  a,容器收到一个请求,会启动一个线程来处理
 该请求。
  b,容器在默认情况下,对于某个servlet类型,只
 会创建一个实例。
  当容器收到多个请求,则会有多个线程同时访问
 某个servlet实例的情况,就有可能产生线程安全问题。
 比如,多个线程同时去修改servlet实例的属性。
 (2)解决方式
  a,加锁
   使用synchronized对有线程安全问题的代码加锁。
  b, 让servlet实现SingleThreadModel接口
  (不建议使用)。
   容器会为servlet创建多个实例(一个线程一个实例),
  因为有可能会产生过多的实例,影响性能,所
  以不建议使用。
3、servlet小结
 (1)servlet基础
  1)什么是servlet?
  2)什么是servlet容器?
  3)如何开发一个servlet?
  4)http协议相关知识(了解)
 (2)servlet核心
  1)get请求与post请求
  2)表单处理(中文参数值)
  3)如何获得请求参数值
  4)servlet如何输出中文
  5)转发与重定向
  6)servlet的生命周期
  7)servlet线程安全问题
  8)servlet上下文
 (3)状态管理
  1)什么是状态管理
  2)cookie技术
  3)session技术
  4)如果禁止cookie,如何继续使用session(了解)
  5)session验证、验证码
 (4)数据库访问
  1)如何使用jdbc访问数据库
  2)dao
 (5)过滤器和监听器
 (6)案例和扩展 
  1)员工信息管理
  2)session验证
  3)验证码
  4)网上交友(可选)
  5)上传文件
    
day11:
1、jsp基础
 (1)什么是jsp?
 (2)怎样写一个jsp?
  step1,写一个以".jsp"为后缀的文件
  step2,添加
   1)html : 直接写
   2)java代码:
    a, <%     %>
    b, <%=    %>
    c, jsp声明(a1.jsp)
     <%!      %>
   3)指令
    a,什么是指令
    通知容器,在将.jsp文件转换成.java文件
    时,做一些额外的处理,比如导包。
    b,指令的基本语法
     <%@指令名称 属性名称=属性值%>
    c,三个指令:
     page
      import属性:导包
      pageEncoding属性:jsp文件的编码
      contentType属性: response.setContentType
      的内容。
      session属性(a2.jsp):true(缺省)/false,当值为false
      时,容器不再添加获得session对象的代码。
      errorPage属性(a3.jsp,a4.jsp):指定一个错误处理页面。
      isErrorPage属性:true/false(缺省),如果值为
      true,表示这是一个错误处理页面。
     include
      file属性:将file属性指定的文件的内容插入
      到指令所在的位置。
     taglib
      用于jsp标签技术,作用是导入jsp标签。
   4)隐含对象
    a,什么是隐含对象?
     可以在jsp文件里面直接使用的对象。
    b,为什么可以直接使用隐含对象?
     因为jsp要先转换成一个servlet,容器会
    自动添加创建这些对象的语句。
    c,有哪些隐含对象
     out
     request
     response
     session
     application
     exception:只有当isErrorPage=true时,才
     能使用,可以调用该对象的方法来获得jsp
     运行时产生的异常信息。
     pageContext: 容器会为每一个jsp实例(
     jsp对应的那个servlet对象)创建唯一的一个
     PageContext对象(该对象会一直存在,除非
     jsp实例被销毁)。
      作用1:绑订数据 (a5.jsp,a6.jsp)
        setAttribute,getAttribute,removeAttribute
       其绑订的数据只有对应的jsp实例能访问。
      作用2:可以找到其它八个隐含对象
       jsp标签技术会用到
     config(a7.jsp): ServletConfig实例。
     page: 相当于this,jsp实例本身。
   5)注释 (a8.jsp)
    a, <!--注释的内容--> :
    注释的内容可以是java代码,如果是java
    代码,会被容器执行。
    b, <%--注释的内容--%>,注释的内容不
    能够出现java代码,如果有,会被容器
    忽略。
 (3)jsp是如何运行的?
  1)step1,容器要将.jsp文件转换成.java文件(
  servlet类)。
   a, html   --------> service, out.write输出
   b, <%   %> ------> service,照搬
   c, <%=  %> ------> service,out.print输出
   d, <%!   %> ------> 给servlet添加新的属性
         或者方法。
  2)调用servlet
2、jsp标签和el表达式
 (1)jsp标签是什么?
  sun公司制订的一种用来代替jsp文件中的java代码的
 技术,其实质是利用类似于html的标签(比如 if)来代替
 java代码。容器在执行时,依据jsp标签可以找到
 对应的标签类,然后调用标签类中的代码。
  因为直接在jsp文件里面写java代码,不利于jsp文件
 的维护(比如,将jsp文件交给美工去修改就很不方便),
 所以,sun才制订了这样一种技术。
 (2)什么是el表达式?
  是一套简单的计算规则,用于给jsp标签的属性赋值。
 现在,也可以脱离jsp标签,直接运行。
 (3)如何使用el表达式
  1)访问bean的属性
   方式一   (e1.jsp)
    ${user.name}
     a,执行过程:
      容器会依次从pageContext,request,
     session,application中查找(getAttribute)
     绑订名称为"user"的对象。接下来,调用
     该对象的"getName"方法,然后输出。
     b,注意:
      会将null转换成""输出。
      如果找不到对应的对象,会输出""。
      属性名不要写错。
      c,如果要指定查找范围,可以使用
      pageScope,requestScope,sessionScope,
      appliationScope。
       比如: ${sessionScope.user.name} 
   方式二 (e2.jsp)
    ${user["name"]},允许[]里面出现一个绑订名,
    比如:
     ${user[propname]}
    此外,[]里还允许出现下标(从0开始的整数),
    用来访问数组中的某个元素。
  2)进行一些简单的计算 (e3.jsp)
   a,算术运算: +,-,*,/,%, 需要注意的是,+号
   操作只能求和。
   b,关系运算: >,>=,<,<=,==,!=
   c,逻辑运算: &&,||,!
   d,empty运算: 判断一个字符串是否为空,或者
   一个集合是否为空。
    以下四种情况,结果都是true。
     空字符串
     空的集合
     null
     找不到值
  3) 获取参数值 (e4.jsp)
   ${param.name} 等价于
    request.getParameter("name");
   ${paramValues.interest} 等价于
    request.getParameterValues("interest");
 练习:
   写一个EmpServlet,将一个员工对象(Employee)
  绑订到request对象上,然后转发给一个EmpInfo.jsp
  (该jsp使用el表达式输出员工的信息) 。
 (4)jsp标签的使用
  1)jstl (java standard tag lib)
   a,jstl是什么?
    apache开发的一套标签。
   b,编程步骤
    step1,将标签对应的jar文件放到
    WEB-INF/lib下。
     tep2,使用taglib指令导入标签。
     taglib指令
     <%@taglib uri="" prefix=""%>
     uri:指定命名空间(参考.tld文件的<uri>)
     prefix:命名空间前缀
   c,核心标签
    if标签: (empInfo.jsp)
     <c:if test="" var="" scope="">
      标签体(可以是html,java代码)
     </c:if>
     当test属性为true时,容器会执行标签体的
     内容。
     test属性可以使用el表达式来赋值。
     var属性:用来指定一个绑订名称
     scope属性:指定绑订的范围(page,
     request,session,application)
    choose标签  (empInfo2.jsp)
     <c:choose>
      <c:when test="">
      </c:when>
      <c:when test="">
      </c:when>
      ...
      <c:otherwise>
      </c:otherwise>
     </c:choose>
     when表示一个分支,可以出现1次或者多次;
     otherwise表示例外,可以出现0次或者1次
    forEach标签 (empList.jsp)
     用来遍历集合
     <c:forEach items="" var="" varStatus="">
     </c:forEach>
     items属性指定要遍历的集合,可以使用
     el表达式来赋值
     var属性指定绑订名称,绑订范围是pageContext,
     绑订值是集合中的一个对象。
     varStatus属性指定绑订名称,绑订范围是
     pageContext,绑订值是容器创建的一个对象,
     该对象封装了当前迭代的状态:
      getIndex(): 返回当前正在被迭代的对象的
      下标(从0开始)
      getCount():返回是第几次迭代。下标从1
      开始。
   d,jsp标签运行过程
     容器依据标签的命名空间找到标签的描述
    文件(.tld文件),然后依据标签的名称找到
    标签类的名称,接下来,创建这个标签类的
    实例并调用相应的方法。
  2)自定义标签 
DAY12:
2)自定义标签
 step1,写一个java类,继承SimpleTagSupport类。
 step2,在doTag方法里面,编写相应的处理逻辑。
 step3,描述标签。要在.tld文件里面描述,并且,
 .tld文件要放到WEB-INF\下。
  注意:
   body-content用来告诉容器,标签有没有标签
  体,如果有,可以出现哪些内容。
  //没有标签体
  <body-content>empty</body-content>
  //可以有标签体,但是,标签体的内容不能够出现java代码(<%   %>,<%=  %>,<%!  %>)。
  <body-content>scriptless</body-content>
  //有标签体,并且,标签体的内容可以出现java代码。只有复杂标签技术才支持JSP,简单标签技术只支持empty,scriptless
  <body-content>JSP</body-content>
3)javaee5如何使用el表达式和jstl?
 javaee5对应servlet2.5,对应的容器是tomcat6.0及以上版本。
 j2ee1.4对应servlet2.4,对应的容器是tomcat5.5及以上。
 a,要使用el表达式,要使用tomcat6.0及以上版本。
 b,要使用jstl,不再需要拷贝jar文件了,javaee5已经包含了jstl对应的jar文件。
 
1、mvc(model,view,controller)
 (1)mvc是什么?
  是一种软件架构思想,将一个软件的组件成部分
 划分成三个部分,分别是:
  模型: 封装业务逻辑(加工数据)。
  视图:表示逻辑(展现数据)
  控制器:协调模型和视图(视图向控制器发请求,
  由控制器调用相应的模型来处理;模型返回的
  处理结果也要给控制器,由控制器选择合适的
  视图来展现)。
   使用mvc来开发一个软件,有很多优点,
  其中,最重要的一点是可以实现模型的复用:
   1)模型只返回处理结果(可以是简单的文本、
   xml文档),可以交给不同的视图来展现这些
   处理结果。
   2)可以使用不同的视图来访问模型。
 (2)如何使用mvc(javaee)?
  1)责任的分配
   a,模型:java类
   b,视图: jsp
   c,控制器:servlet或者filter
  2) 如何交互
  
 申请贷款
  客户端:用户输入帐号(accountNo)和贷款金额
  (amount), 服务器端:处理流程如下:
  step1,先检查帐号是否存在,如果不存在,提示用户;
 否则进行下一步。
  step2,检查贷款金额是否满足如下条件:
   amount <= 帐户余额(balance) * 10
 如果不满足条件,提示用户,否则进行下一步。
  step3,生成一个序号并保存到数据库,同时,
 将该序号显示给客户端。
  
 step1,建表
  create table t_account(
   id int primary key auto_increment,
   accountNo varchar(16) unique,
   balance double
  )type=innodb;
  insert into t_account(accountNo,balance) 
  values('6225881003192000',1000);
DAY13:
1、mvc的优点和缺点
 1)优点
  a,模型的复用:
  b,维护性好:
    修改或者替换模型,不会影响视图,反过来,
  修改或者替换视图,也不会影响模型。
  c,方便测试:
    比如,如果将业务逻辑写在servlet里面,
   需要部署之后通过浏览器发请求来测试,而
   将业务逻辑写在一个java类里面,可以直接测试。
 2)缺点
  按照mvc的方式来开发,会增加设计的难度,并且
 相应增加代码量和软件开发的成本。
 
2、ajax(asynchronous javascript and xml)
 (1)什么是ajax?
  ajax是一种用来改善用户体验的技术,其实质是利用
 浏览器内部的一个特殊的对象(XMLHttpRequest对象,
 一般称之为一个ajax对象)向服务器发请求(在发请求时,
 浏览器不会销毁当前页面,用户仍然可以对当前页
 面做其它的任何操作),服务器返回部分数据(不是一个
 完整的页面),在浏览器端,利用服务器返回的这些数据
 更新当前页面。整个过程,页面无刷新,不打断用户
 的操作。
 (2)如何获得ajax对象
  function getXhr(){
   var xhr = null;
   if(window.XMLHttpRequest){
    //非ie浏览器
    xhr = new XMLHttpRequest();
   }else{
    //ie浏览器
             xhr = new ActiveXObject('MicroSoft.XMLHttp');
   }
   return xhr;
  }
 (3)ajax的属性
  1)onreadystatechange:绑订事件处理函数,处理
  readystatechange事件(当ajax对象的readystate
  属性值发生了改变,比如从0变成了1,就会产生
  该事件)。
  2)resposeText:获得服务器返回的文本。
  3)responseXML:获得服务器返回的xml文档。
  4)status:获得服务器返回的状态码。
  5)readyState: 表示ajax对象与服务器进行通信的
  状态,有5个值,分别是0,1,2,3,4。比如4表示
  ajax对象已经获取了服务器返回的所有的数据。
 (4)编程步骤
  step1,获得ajax对象
  比如:
    var xhr = getXhr();
  step2,发请求
   1)发get请求
    //true:表示发送异步请求
    //false:表示发送同步请求(发送同步请求时,
    浏览器会锁定当前页面,用户不能对当前页面
    做其它操作)。
    xhr.open('get',
    'check_username.do?username=tom',true);
    //绑订一个事件处理函数
    xhr.onreadystatechange=f1;
    //send方法会将请求数据包发送出去。
    xhr.send(null);
   2)发post请求
    xhr.open('post',
    'check_username.do',true);
    //添加一个content-type消息头
    xhr.setRequestHeader('content-type',
    'application/x-www-form-urlencoded');
    xhr.onreadystatechange=f1;
    xhr.send('username=tom');
  step3,编写服务器端的处理逻辑,一般只需要
  返回部分的数据。
  step4,编写事件处理函数(处理服务器返回的数据)
    function f1(){
     //因为只有其值等于4,才能获得服务器
     //返回的所有的数据
     if(xhr.readyState == 4){
      //获得服务器返回的文本
      var txt = xhr.responseText;
      //更新(dom操作)
      ...
     }
    }
 (5)编码问题 
  1)发送get请求
   a,产生乱码问题的原因
    ie浏览器对应的ajax对象对中文参数值会使用
   gbk进行编码,而其它浏览器会使用utf-8进行编码。
    web服务器默认情况下,会使用iso-8859-1进行
   解码。
   b,解决方式
    step1,使用encodeURI函数对请求地址进行编码。
  该函数会对其中的中文参数值按照utf-8进行编码。
    step2,让服务器统一使用utf-8进行解码。
   比如,可以修改tomcat的配置文件。
    conf/server.xml
    对<Connector>添加URIEncoding="utf-8"。
    tomcat会对所有的get请求中的参数使用utf-8
    进行解码。
  2)发送post请求
   a,产生乱码问题的原因
    所有浏览器对应的ajax对象对中文参数都
   使用utf-8进行编码。服务器使用iso-8859-1
   进行解码。
   b,解决方式
    request.setCharacterEncoding("utf-8"); 
    
 练习:使用ajax技术实现 "级联下拉列表",
当用户选择左边的某个省,右边下拉列表会出现
对应的市。
 练习:使用ajax技术来检查验证码是否正确。
DAY14:
1、ajax的优点
 (1)改善用户的体验:
  不打断用户的操作,页面无刷新。
 (2)提升程序的性能:
  按照需要获取数据(不再需要返回一个完整的新的
  页面,只需要返回部分的数据)。
 (3)标准化的技术:
  不需要下载任何的插件(有些类似ajax的技术,比如
  Flex,是需要下载插件的)。
2、json (javascript object notation)
 (1)json是什么?
  是一种轻量级的数据交换标准。
  1)数据交换
   将数据转换成一种与平台无关的数据格式并传递
  给接收方。
   比如:服务器端有一个order对象,要发送给
  浏览器,可以将order对象表示的数据转换成一个
  xml文档然后发送给浏览器,在浏览器端,再去
  解析这个xml文档,就可以获得order信息。
   <order>
    <name>order001</name>
    <price>100</price>
    ....
   </order>
   也可以将order对象表示的数据转换成一个
  json字符串
   {'name':'order001','price':100,....}然后
  发送给浏览器,在浏览器端,可以很方便地将这个
  json字符串转换成一个js对象,然后获得order信息。
  2)轻量级 
   相对于xml作为数据交换标准,json所需要的
  文档大小更小,解析的速度更快。
 (2)json的语法   www.json.org
  1)表示一个对象
   {属性名称1:属性值1,属性名称2:属性值2...}
   注意:
    a,属性名称要用引号(单引号或者双引号)括起来。
    b,属性值可以是
     number,string,boolean(true/false),
     null,object
    c,如果属性值是string,要用引号括起来。
  2)表示一个对象组成的数组
   [{},{},{}...] 
 (3)使用json编写ajax应用程序
  1) java对象转换成json字符串。
   或者java对象组成的集合转换成json字符串。
   使用json提供的官方的工具
    JSONObject,JSONArray
  2)json字符串转换成javascript对象
   使用一些js框架提供的函数,比如
   prototype提供的evalJSON函数。
  
  prototype.js提供了很多有用的函数,比如:
   $(id): document.getElementById(id)
   $F(id): $(id).value
   $(id1,id2,id3....): 分别查找id为id1,id2,id3...
   的节点,然后返回一个由这些节点组成的数组。
   strip(): 除掉字符串两端的空格。
   evalJSON():将json字符串转换成相应的js
   对象或者js对象组成的数组。
 
 练习:
  参见tts6第二天 经典案例“热卖商品动态显示"。
 
3、发送同步请求
 (1)什么是同步请求
  当ajax对象向服务器发请求时,浏览器会锁定
 当前页面,用户不能对当前页面做其它的操作。
 (2)如何发送同步请求
  open(请求方式,请求地址,false);
0 0
原创粉丝点击