JSP、Servlet、JDBC学习笔记

  • WEB的学习
    * 服务器
    * 网络的架构(面试题)
    * C/S client/server 客户端/服务器端 例子:QQ 快播 暴风影音
    * 优点:交互性好,服务器压力小。
    * 缺点:客户端更新了,下载。

            * B/S   browser/server  浏览器/服务器端        例子:购物网站 12306   
                * 缺点:服务器压力大。
                * 优点:服务器更新就ok。
    
        * WEB网页 javaweb 使用java开发网页。
        * 静态的web资源
            * HTML、CSS、JAVASCRIPT
        * 动态的web资源  
            * 数据发生变化。例子:
            * Servlet/JSP   
            * ASP   .net    微软开发
            * PHP   开源(开发网站那速度真快),提供模板。
            * RUBY  小日本开发的。
            * JAVA做什么?后台的管理系统,处理大数据。
    
        * 服务器的介绍
            * 原理:网络编程。
            * 概念:
                * 硬件:就是一台主机。
                * 软件:安装了服务器的软件(tomcat)。 
                * 安装软件后,称为WEB服务器。
                * 启动服务器,访问资源。
                * 访问:http://+ip+端口号 找到主机。如果资源的文件,就可以访问了。
                * 访问百度:http://www.baidu.com/
                * HTTP协议默认端口号是80,可以不写。
                * ping www.baidu.com    61.135.169.121
    
                * 如果想访问本机的服务器(扩展)
                    * http://localhost:80
                    * http://127.0.0.1:80
    
        * 常见服务器
            * WebLogic  BEA公司开发的(被Oracle收购了)    收费的 支持JAVAEE所有的规范(EJB servlet/jsp规范)
                * (JAVA MySql(Oracle)   WebLogic)
    
            * WebSphere IBM公司开发的    收费的 支持JAVAEE所有的规范(EJB servlet/jsp规范)
                * 银行喜欢用
    
            * Tomcat    apache(开源的组织,非常的伟大)只Servlet/JSP规范。免费的。
    
        * Tomcat的安装和注意实现
            * (前提条件)安装JDK,配置java的环境变量。
            * 安装版(给客户用)
            * 解压版:解压当前文件夹,目录不要有中文。
            * 启动服务器:tomcat/bin/startup.bat(启动服务器)   
            * 访问:http://192.168.24.215:8080访问服务器的主页。
            * 关闭服务器:点关闭按钮,shutdown.bat
    
    
        * 常见启动问题
            * 配置环境变量。C:\Program Files (x86)\Java\jdk1.6.0_16
                * 一闪而过,环境变量没配置好。
    
            * 端口占用问题
                * java.net.BindException: Address already in use: JVM_Bind
                * 端口被占用。
    
                * 解决问题:
                    * 结束掉占用端口的应用程序。
                        * 找到占用端口的应用程序。
                        * 通过命令  netstat –ano,在任务管理器中结束程序。
    
                    * 修改端口号。
                        * 修改配置文件了。
                        * tomcat/conf/server.xml配置文件
                        <Connector port="80" protocol="HTTP/1.1" 
                           connectionTimeout="20000" 
                           redirectPort="8443" />
    
                    * 有应用程序占用80端口,用上边的方式解决。
                    * 有的WIN7系统自带World wide web publish IIS服务,默认占用80端口。
                        * 如果有。去服务中,该手动启动。
    
                    * 概率非常小。操作系统占用80端口。
    
        * Tomcat目录结构
            * bin(**)           启动项,关闭项。    
            * conf(*****)       里面Tomcat配置文件
            * lib(**)           服务器运行使用的jar包
            * logs(***)     日志文件,运行时产生的日志。
            * temp(**)      运行时临时文件
            * webapps(*****)    web applications(web的应用们)
            * work(*****)       JSP翻译成Servlet程序
    
    
        * 如果动态的WEB资源,把WEB资源发布到服务器的webapps目录下的时候,有固定的目录结构。
            * 静态的web资源
                * HTML、CSS、JAVASCRIPT
            * 动态的web资源  
                * 数据发生变化。例子:
                * Servlet/JSP   
    
        * 开发动态的WEB资源程序,目录结构如下(必须记住)
            * Servlet/JSP只要包含,就称为动态的WEB资源
            website
                |   ---     存放  HTML CSS JAVASCRIPT JSP 图片
                WEB-INF
                    |
                    web.xml     程序的入口。配置文件(必须有的)
                    classes     文件夹,名称固定的  可选的
                    lib         文件夹,名称固定    可选的
    
    
        * Tomcat和MyEclipse的集成
            * window -- 选项 -- MyEclipse -- servers -- 选择tomcat -- 选择Enable -- 选择tomcat根目录
            * 配置JDK -- 选择JDK。
            * 启动服务器。
    
            * 虚拟路径(访问路径)
                * 默认和项目名称是相同(不要去修改)。
                * WebRoot变成了虚拟路径(访问的路径)
    
            * 直接复制项目,需要修改虚拟路径。
                * 项目上右键 -- 选择属性 -- MyEclipse -- web -- 修改虚拟路径。
    
            * 扩展
                * window -- show view -- servers 
    
        * Tomcat管理员的配置(了解)
            * 在tomcat/conf/tomcat-user.xml
    
                <role rolename="manager"/>
                <user username="admin" password="admin" roles="manager"/>
    
    
        * 如何部署WEB程序(三种)
            * 项目复制到webapps目录下。
    
            * 通过配置虚拟路径的方式。
                * 直接修改配置文件
                    * 写到tomcat/conf/server.xml
                    * 找到<Host>标签,配置到Host标签的中间
                    * 目的:通过配置,配置访问路径,准确找到c:\bb的文件
                        * <Context docBase="文件夹的真实目录" path="虚拟路径(访问路径)" ><Context>
                        * <Context docBase="C:\bb" path="/itcast" ></Context>
                        * 访问:http://localhost:80/itcast
    
                * 自己编写一个配置文件(格式)(推荐使用)
                    * 自定义xxx.xml结尾文件,在$CATALINA_HOME/conf/[enginename]/[hostname]/ directory.目录下。
                    * 把xxx当成虚拟(访问)路径。
    
                    * 在xml的文件中编写。
                        * 在哪个目录下:
                            * $CATALINA_HOME/conf/[enginename]/[hostname]/ directory.
                            * 如果找引擎的名称和主机的名称,在server.xml中找。
                            * tomcat/conf/Catalina/localhost/ccc.xml
    
                        * ccc.xml的文件编写什么内容?
                            * <Context docBase="C:\cc"></Context>
                        * 访问:http://localhost:80/ccc
    
    
        * 配置虚拟主机(了解)                
    
    
    * HTTP的协议
        * 啥是HTTP的协议:超文本传输协议。
        * 基于TCP链接的传输协议。
        * HTTP协议默认是80
        * 基于请求和响应的模式。(先有请求,后有响应)
            * www.baidu.com(访问百度的服务器)
            * 百度的首页返回给我
        * 客户端连上web服务器后,若想获得web服务器中的某个web资源,
            需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式
    
    
        * 使用telnet命令,快速了解http协议。
        * 如果是win的系统,开启telnet的命令。    
    
    
            启动服务器.
            cmd>telnet localhost 80  回车
            telnet命令有一个乱码问题  解决乱码.
            解决  ctrl+]  回车
            注意:命令行中输入的内容不能写错,一旦写错了,不支持删除。
            请求:
    
            GET /aa/1.html HTTP/1.1
            host:localhost
            输入两次回车
    
        * 使用telnet命令发送了一次请求
            * 请求:
                GET /aa/1.html HTTP/1.1
                host:localhost
    
            * 响应:       
                HTTP/1.1 200 OK
                Server: Apache-Coyote/1.1
                ETag: W/"21-1416624196782"
                Last-Modified: Sat, 22 Nov 2014 02:43:16 GMT
                Content-Type: text/html
                Content-Length: 21
                Date: Sat, 22 Nov 2014 07:33:53 GMT
    
                <h1>hello java!!</h1>
    
    
        * HTTP协议的版本
            * HTTP/1.0
                * 链接后,只能获取一个web资源。
                * 链接后,发送请求,服务器做出响应,链接立即断开。
                    GET /aa/1.html HTTP/1.0
                    host:localhost  
    
            * HTTP/1.1(使用)
                * 链接后,可以获取多个web资源。
                * 链接后,发送请求,服务器做出响应,链接不会立即断开。
                    再次发送请求,直接有一段时间没操作,自动断开。
                    GET /aa/1.html HTTP/1.1
                    host:localhost
    
        * 请求:
            * 请求行
                * 请求方式
                    * POST、GET、HEAD、OPTIONS、DELETE、TRACE、PUT、CONNECT
                    * 常用post和get
                    * 区别:
                        * get把参数显示在地址栏上,安全级别低,不支持大数据。
                        * post把参数封装请求体中,安全级别高,支持大数据。
    
                * 请求地址
                    * 请求资源
                * 协议版本
                    * HTTP/1.1
            * 请求头
                Accept: text/html,image/*    
                Accept-Charset: ISO-8859-1
                Accept-Encoding: gzip
                Accept-Language:zh-cn 
                Host: www.itcast.com:80
                If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT
                Referer: http://www.itcast.com/index.jsp
                User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
                Connection: close/Keep-Alive   
                Date: Tue, 11 Jul 2000 18:23:51 GMT 
    
                * 重要的头
                    * If-Modified-Since     必须和响应头信息一起来完成控制本地的缓存。
                    * Referer               当前的网页的来源。(防止盗链)
                    * User-Agent            判断浏览器的版本(文件下载的时候)
    
            * 空行
            * 请求体
                * 封装post参数列表。
    
        * 响应
            * 响应行
                * 协议版本
                    * HTTP/1.1
                * 状态码
                    200 :请求成功处理,一切OK
                    302 :请求重定向
                    304 :服务器端资源没有改动,通知客户端查找本地缓存
                    404 :客户端访问资源不存在
                    500 :服务器内部出错
    
                * 状态码描述
    
            * 响应头
                Location: http://www.it315.org/index.jsp 
                Server:apache tomcat
                Content-Encoding: gzip 
                Content-Length: 80 
                Content-Language: zh-cn 
                Content-Type: text/html; charset=GB2312 
                Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT
                Refresh: 1;url=http://www.it315.org
                Content-Disposition: attachment; filename=aaa.zip
                Expires: -1
                Cache-Control: no-cache  
                Pragma: no-cache   
                Connection: close/Keep-Alive   
                Date: Tue, 11 Jul 2000 18:23:51 GMT
    
                * 重要的头
                    * Location                  和302一起完成重定向。
                    * Last-Modified             和请求头If-Modified-Since一起控制缓存。和状态码304
                    * Refresh                   完成页面的定时跳转
                    * Content-Disposition       设置文件是以附件打开
    
                    Expires: -1
                    Cache-Control: no-cache  
                    Pragma: no-cache
                    * 禁用缓存(网银系统)
    
            * 空行
            * 响应体
                * 存放真正的数据。
    
  • Servlet(好好学)
    * 动态WEB的资源。
    * 什么是Servlet
    * 实现Servlet接口,重写5个方法。
    * Servlet是一个小的java程序,运行在服务器中,接收和响应从客户端(浏览器)发送过来的请求。

    * 快速入门
        * 编写一个类,实现Servlet接口,重写5个方法。
            * 编写一个类,继承GenericServlet类,重写一个方法。
        * 配置文件,配置Servlet信息。(必须会)
            <!-- 先配置Servlet信息 -->
            <servlet>
                <!-- 配置Servlet名称,名称必须唯一 -->
                <servlet-name>ServletDemo1</servlet-name>
                <!-- 配置Servlet的完全路径(包名+类名) -->
                <servlet-class>cn.itcast.servlet.ServletDemo1</servlet-class>
            </servlet>
    
            <!-- 配置Servlet映射(访问路径) -->
            <servlet-mapping>
                <!-- 配置Servlet名称,和上面的名称必须相同 -->
                <servlet-name>ServletDemo1</servlet-name>
                <!-- 配置虚拟路径(访问路径) -->
                <url-pattern>/demo1</url-pattern>
            </servlet-mapping>
    
    * 编译
        * javac -d . HelloServlet.java
        * HelloServlet.java:4: 软件包 javax.servlet 不存在
        * set classpath=%classpath%;servlet-api.jar 设置临时的环境变量,只对当前的窗口有效。
    
    
    * Servlet的生命周期(面试题)
        * 生命周期:实例被创建,对外提供服务,销毁。
            * Servlet被创建后,然后调用init方法进行初始化
                void init(ServletConfig config) 
            * 从客户端发送所有的请求是service方法进行处理的。
                void service(ServletRequest req, ServletResponse res)  
            * 从服务器中移除服务,调用destroy方法。
                void destroy()  
    
        * Servlet的生命周期:第一次请求的时候,Servlet实例被创建,立即调用init方法进行初始化。
                                实例通过service方法提供服务。服务器关闭或者移除服务时,调用destroy方法进行销毁。
    
    * Servlet的关系
    
        Servlet接口
            |
        GenericServlet(重写5个方法)
            |
        HttpServlet(继承GenericServlet实现了Servlet接口)   
            |
        MyServlet   
    
    
        * 为什么会有GenericServlet?为什么有HttpServlet?
        * 注意事项:
            * 如果想重写init方法,重写无参数的init方法。
    
    * 开发步骤(最终)
        * 编写一个类,继承HttpServlet
        * 重写doGet和doPost方法
            * 在doPost方法中调用doGet方法。
            * 表单是get方式,调用doGet
            * 表单是post方法,调用doPost方法
            * doGet和doPost互相调用。
    
                <form action="http://localhost:80/day09/demo5" method="post">
                    姓名:<input type="text" name="username" />
                    <input type="submit" value="查询" />
                </form>
    
        * 在web.xml配置
    
    
    * 修改Servlet模板
        * 先找到MyEclipse的安装路径。
        * \myeclipse10.7\Common\plugins\com.genuitec.eclipse.wizards.xxxx.jar
            * 我自己的:com.genuitec.eclipse.wizards_9.0.0.me201211011550.jar
    
        * 千万别解压,右键--选择压缩工具打开--templates--Servlet.java
        * 拖回去之前,先MyEclipse关闭。
    
    
    * Servlet自动加载
        * Servlet默认是第一次访问时候创建实例。通过配置,服务器启动,创建实例。
        * init做初始化的操作,非常消耗时间的。
    
        * 在<servlet>标签下
            <load-on-startup>3</load-on-startup>
        * 值是正整数
            * 如果值越小,优先级越高。
    
    * 配置虚拟路径(访问路径)
        <servlet-mapping>
            <url-pattern>/demo5</url-pattern>
        </servlet-mapping>  
    
        * 完全路径匹配
            * 以/开头的         /demo5      /servlet/demo5
            * 访问:http://localhost/day09/demo5
        * 目录匹配
            * 以/开头的         /*
            * 访问:http://localhost/day09/demo5可以访问
        * 扩展名匹配
            * 不能以/开头的       *.do    *.action    
            * 访问:http://localhost/day09/demo5.do
    
        * 优先级:完全路径匹配    > 目录匹配 > 扩展名匹配(*****)
    
    
    * WEB开发中路径的问题   
        * 相对路径
            * 一个文件相对于另一个文件的位置的关系。
            * 不能以/开头    写法: ./demo  demo    ../demo
    
            * 访问1.html: http://localhost/day09/1.html
            * 访问demo5:  http://localhost/day09/demo5
            * 从1.html中去访问demo5:./demo5  demo5
    
            * 访问2.html: http://localhost/day09/html/2.html
            * 访问demo5:  http://localhost/day09/demo5
            * 从2.html访问demo5:../demo5
    
        * 绝对路径(推荐使用)
            * 以/开头的
            * 访问demo5:  http://localhost/day09/demo5
            * 从1.html使用绝对路径访问demo5:http://localhost/day09/demo5
            * 简写方式:/day09/demo5
    
            * 客户端绝对路径
                * /day09/demo5  需要写项目名
    
            * 服务器绝对路径   
                * /demo5    不能写项目名
    
    
    * ServletConfig对象和配置文件相关
        * 配置初始化参数
            * 需要在<servlet></servlet>标签下配置。
            * 如果要是配置在某个servlet的标签下,那么只能在该servlet中获取初始化参数。
            <init-param>
                <param-name>username</param-name>
                <param-value>root</param-value>
            </init-param>
    
        * String getServletName()                   获取配置文件中servlet的名称
        * String getInitParameter(String name)      获取初始化参数
        * Enumeration getInitParameterNames()       获取初始化参数的名称们
    
    
    * ServletContext对象(域对象) (*****)
        * 定义:WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
        一个WEB应用对应一个ServletContext对象
        一个WEB应用下有多个Servlet程序
        所有的servlet程序都共享同一个ServletContext对象
    
        demo1存入内容   ServletContext  demo2中取出来
    
        * 作用:
            * 获取WEB应用全局初始化参数
                * 在web.xml中配置
                    <context-param>
                        <param-name>encoding</param-name>
                        <param-value>GBK</param-value>
                    </context-param>
    
                String getInitParameter(String name)      
                getInitParameterNames()                     
    
            * 实现数据的共享(*****)
                void setAttribute(String name, Object object)   存入数据
                void removeAttribute(String name)               删除数据
                Object getAttribute(String name)                获取数据
    
            * 读取资源文件(*****)
                InputStream getResourceAsStream(String path)    通过文件的地址获取输入流
                String getRealPath(String path)         通过文件的地址获取文件的绝对磁盘路径
    
    * 缺省的servlet(了解)
    * 自己编写的servlet,不要配置/。
    * 在tomcat/conf/web.xml中的配置。
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    

    response对象(响应对象)
    * 想要获取客户端的内容,使用request对象。对客户端做出响应使用response对象。

    * 响应:
        * 响应行
            * 状态码:
                void setStatus(int sc) 设置状态码
    
        * 响应头(key:value的形式,一个key对应一个value,一个key对应多个value)
            * 设置头信息
                void setHeader(String name, String value)  (一个key对应一个value)经常使用的
                    setHeader("aa","bb");
                    setHeader("aa","cc");
                    结果:aa:cc
    
                void setIntHeader(String name, int value)  
                void setDateHeader(String name, long date) 值是毫秒值(int 秒  long 毫秒)
    
                void addHeader(String name, String value)  (一个key对应多个value)
                    addHeader("aa","bb");
                    addHeader("aa","cc");
                    结果:aa:bb,cc
    
                void addIntHeader(String name, int value)  
                void addDateHeader(String name, long date)                      
    
        * 响应体
            ServletOutputStream getOutputStream()   字节输出流
            PrintWriter getWriter()                 字符输出流
    
    
    * 案例
        重定向(登陆页面)
            * 登陆页面重定向
            * 状态码302        应头location
            * 需求:登陆页面,用户名和密码的登陆页面,用户名和密码都是admin,
                    如果有一个不是,重定向到登陆页面,重新登陆。
    
                // 设置302的状态码
                response.setStatus(302);
                // 设置地址
                response.setHeader("location", "/day10/response/login.html");   
    
                // response对象提供了一个方法,可以完成重定向。
                response.sendRedirect("/day10/response/login.html");
    
        页面定时刷新(页面读秒操作)
            * 响应的头  refresh 
            <meta http-equiv="refresh" content="5;url=/day10/response/login.html">
    
        禁用浏览器缓存(三个头信息)
            * 应用:网银。页面的数据是发送变化的。
            Cache-Control : no-cache
            Expires: -1                 值是日期类型(setDateHeader())
            Pragma : no-cache
    
    
        向页面输出中文(乱码问题)
            字节:ServletOutputStream getOutputStream()    字节输出流
                 * 字节的输出中文的乱码
                 *  * 输出哈罗我的是否乱码呢?
                 *      * 不一定乱码。
                 *  * 解决办法
                 *      * 设置浏览器打开文件时所采用的编码
                 *          response.setHeader("Content-Type", "text/html;charset=UTF-8");
                 *      * 获取字符串byte数组时编码和打开文件时编码一致。
                 *          "哈罗我的".getBytes("UTF-8")
    
            PrintWriter getWriter()                 字符输出流
    
                 * 字符输出中文是否乱码呢?
                 *  * 肯定乱码
                 *      response缓冲区的编码,默认值ISO-8859-1
                 *  * 设置response缓冲编码        
                 *      response.setCharacterEncoding("UTF-8");
                 *  * 设置浏览器打开文件所采用的编码
                 *      response.setHeader("Content-Type", "text/html;charset=UTF-8");
                 *  * 简写方式
                 *      response.setContentType("text/html;charset=UTF-8");
    
    
            * 总结:response对象输出中文,产生乱码。
                * 字节
                    * 解决方案
                        * 设置浏览器打开文件时采用的编码
                            response.setHeader("Content-Type", "text/html;charset=UTF-8");
                        * 获取字符串的byte数组采用的编码
                            "哈罗我的".getBytes("UTF-8")
                * 字符
                    * 解决方法
                        * 设置浏览器打开文件时采用的编码
                            response.setHeader("Content-Type", "text/html;charset=UTF-8");
                        * 设置response缓冲区的编码
                            response.setCharacterEncoding("UTF-8");
    
                    * 简写的方式(等于上面的两句)
                        * response.setContentType("text/html;charset=UTF-8");
    
        实现文件下载(初级的下载)
            * 超链接,浏览器可以解析直接打开。
            * 弹出下载窗口。
                * 前提条件:先准备一个文件。
                * 读取文件,读入到输入流中。
                * 通过response读出到浏览器端。
                * 设置头信息 
                    * Content-Disposition   attachment;filename=文件名称
                    * 以附件的形式打开。
    
        实现验证码(*****)
    

    request对象(请求对象)

    ServletRequest
        |
    HttpServletRequest  
    
    
    获取客户机信息
        getRemoteAddr(*****)    获取IP地址
        getMethod() (*****)             获取请求方式
        getContextPath()(*****)     获取虚拟路径
    
    获取请求头信息
        String getHeader(String name)       
        long getDateHeader(String name)     
        int getIntHeader(String name)       
    
        * 请求头
            referer             记住当前网页的来源
            User-Agent          判断浏览器
            if-modified-since   控制缓存
    
    获取请求参数(*****)
        String getParameter(String name) (*****) 
        String[] getParameterValues(String name)(*****)  
    
        Map getParameterMap()(*****) 
    
        Enumeration getParameterNames()(用的比较少)  
    
        乱码问题解决:
            * POST请求乱码 :request.setCharacterEncoding("utf-8"); 
    
            * GET请求乱码
                解决方案一:修改tomcat/conf/server.xml 
                    <Connector port="80" protocol="HTTP/1.1" 
                               connectionTimeout="20000" 
                               redirectPort="8443" URIEncoding="utf-8"/>
                * 必须有修改tomcat服务器配置文件权限
    
                解决方案二:逆向编解码
                username = URLEncoder.encode(username, "ISO8859-1");
                username = URLDecoder.decode(username, "utf-8");
    
                解决方案三:简写的方式(推荐使用)
                username = new String(username.getBytes("ISO-8859-1"),"utf-8");
    
            * request获取中文数据乱码(总结:)
                * post提交
                    * 设置request缓冲区的编码
                        request.setCharacterEncoding("utf-8"); 
                * get提交
                    * String构造方法
                        username = new String(username.getBytes("ISO-8859-1"),"utf-8");
    
    
    利用请求域传递对象(request域对象)
    重定向和转发的区别(转发)(*****)
    
        * 域对象
            ServletContext:服务器一启动,为每个web应用创建一个ServletContext对象,所有servlet实例共享对象。 
            request:一次请求的范围。
    
        * setAttribute("","");
        * getAttribute("");
        * removeAttribute("");
    
    
        * getRequestDispatcher(String path) ,返回是RequestDispatcher:对象
        * RequestDispatcher:
            * forward(ServletRequest request, ServletResponse response)(经常使用) 转发的方法
            * include(ServletRequest request, ServletResponse response)(了解) 包含
    
    • JSP、EL表达式的入门(要用)

      • Servlet/JSP 是两种动态的WEB资源的两种技术。
      • 使用Servlet生成HTML的页面
        response.getWriter(“”);
        response.getWriter(“”);
      • JSP简介

        • java server pages java服务器端的网页们,是在服务器端执行。
        • JSP不仅可以写HTML+java代码+JSP自己的代码
        • JSP的运行原理(*):JSP – 翻译成Servlet – 编译 – 访问
      • JSP的脚本元素(JSP的页面可以编写java代码)

        <%! %> :定义类、定义变量、定义方法(不常用) 成员变量。
        <%= %> :输出语句(输出到页面,不能有分号)
        <% %> :定义变量、语句

      • EL快速入门

        • 获取域对象中的内容 request.setAttribute(“xx”,”yy”);
          ${xx}
    • 两个会话的技术

      • 会话概念:打开浏览器,可以访问WEB资源,多次访问WEB资源,关闭浏览器,整个过程一次会话。

      • 购买商品
        用户点击超链接通过一个servlet购买了一个商品,程序应该保存用户购买的商品,
        以便于用户点结帐servlet时,结帐servlet可以得到用户商品为用户结帐。

      • 把商品存入到ServletContext获取request域中呢?

      • cookie和seesion的原理
        看图02
    • cookie

      • 显示上次的访问时间(案例)

        • 第一次访问,输出欢迎,在服务器端,记录当前的时间,把当前的时间通过cookie回写到浏览器。
        • 第二次访问,会带着上次的时间,获取时间,可以把时间显示到页面上,记录当前的时间,再把回写浏览器。
          输出上次的访问时间。
      • Cookie的API

        • cookie的构造方式 Cookie(String name, String value)
        • String getName() 获取cookie的名称
        • String getValue() 获取cookie的值

        • void setMaxAge(int expiry) :设置有效时间

          • 失效cookie setMaxAge(0); 前提条件:设置有效路径(和之前设置过相同)
        • void setPath(String uri) :设置有效路径

          • 默认的有效路径()
            • 配置 /last 默认路径 /day11
            • 配置 /servlet/last 默认路径 /day11/servlet
        • void setDomain(String pattern) :设置有效域名

          • www.sina.com.cn
          • sports.sina.com.cn
          • xxx.sina.com.cn
          • 设置有效域名 setDomain(“.sian.com.cn”);
        • 会话级别的cookie:默认保存到浏览器的内存中。

        • 持久的cookie:把cookie保存到磁盘上。通过setMaxAge(int a)进行设置。
      • 显示用户上次访问过的商品信息(需求)

        • 1.获取请求参数
        • 2.获取cookie数组,通过指定的名称(自己指定)查找cookie
        • 3.如果cookie==null,第一次访问
          • 如果是第一次访问,创建cookie,回写浏览器
        • 4.如果cookie!=null,不是第一次访问
          • 如果不是第一次访问,说明我的cookie中已经存在id
          • 判断,当前的id是否已经存在cookie的中value
          • 如果存在,不用操作
          • 如果不存在,在后面追加(product=1,2)
        • 5.重定向到商品页面
    • session(服务器)

      • cookie基于客户端,不安全,并且大小和个数的限制。
      • session域对象,范围一次会话范围,存个人相关的数据。

        • setAttribute(String name, Object value)
        • Object getAttribute(String name)

        • String getId() 获取seesion的唯一的ID

        • void invalidate() 销毁的seesion
      • 完成简单的购物车

        • 购物车 Map

通过软件可以与该设备进行通信

2.jdbc入门
编写一个jdbc入门代码,完成对数据库操作.
create table user(
id int primary key auto_increment,
username varchar(20) unique not null,
password varchar(20) not null,
email varchar(40) not null
);

    INSERT INTO USER VALUES(NULL,'tom','123','[email protected]');
    INSERT INTO USER VALUES(NULL,'fox','123','[email protected]');    

1.下载驱动
    将驱动jar包复制到lib下.
2.创建一个JdbcDemo1类    
        // 1.注册驱动
        DriverManager.registerDriver(new Driver());

        // 2.获取连接对象
        Connection con = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/day17", "root", "abc");

        // 3.通过连接对象获取操作sql语句Statement
        Statement st = con.createStatement();

        // 4.操作sql语句
        String sql = "select * from user";

        // 操作sql语句(select语句),会得到一个ResultSet结果集
        ResultSet rs = st.executeQuery(sql);

        // 5.遍历结果集
        // boolean flag = rs.next(); // 向下移动,返回值为true,代表有下一条记录.
        // int id = rs.getInt("id");
        // String username=rs.getString("username");
        // System.out.println(id);
        // System.out.println(username);

        while(rs.next()){
            int id=rs.getInt("id");
            String username=rs.getString("username");
            String password=rs.getString("password");
            String email=rs.getString("email");

            System.out.println(id+"  "+username+"  "+password+"  "+email);
        }

        //6.释放资源
        rs.close();
        st.close();

con.close();

jdbc操作详解
1.注册驱动
DriverManager.registDriver(new Driver());

            1.DriverManager类
                它是java.sql包下的一个驱动管理的工具类,可以理解成是一个容器(Vector),可以装入很多数据库驱动

                它的registDriver方法分析
                      public static synchronized void registerDriver(java.sql.Driver driver)
                      参数:java.sql.Driver
                      我们传递的是  com.mysql.jdbc.Driver;

                在com.mysql.jdbc.Driver类中有一段静态代码块:

                    static {
                        try {
                            java.sql.DriverManager.registerDriver(new Driver());
                        } catch (SQLException E) {
                            throw new RuntimeException("Can't register driver!");
                        }
                    }
                上述代码的问题:
                    1.在驱动管理器中会装入两个mysql驱动.
                        解决方案:使用反射
                                Class.forName("com.mysql.jdbc.Driver");

                分析:使用反射的方式来加载驱动有什么好处?
                    1.只加载一次,装入一个驱动对象.
                    2.降低耦合,不依赖于驱动.

            2.可以通过DriverManager来获取连接对象
                Connection con=DriverManager.getConection(String url,String user,String password);
                    url作用:就是用于确定使用哪一个驱动.
                        mysql url:  jdbc:mysql://localhsot:3306/数据库名.
                        oralce url: jdbc:oracle:thin:@localhost:1521:sid

            总结:DriverManager作用:
                1.注册驱动
                2.获取连接Connection
        -----------------------------------------------------------
        关于url
                url格式
                    主协议  子协议   主机 端口  数据库
                    jdbc   :  mysql ://localhost:3306/day17

                mysql的url可以简写:
                    前提:主机是localhost 端口是3306
                    jdbc:mysql:///day17

                了解:在url后面可以带参数
                    useUnicode=true&characterEncoding=UTF-8
    ------------------------------------------------------------------
    2.Connection详解
        java.sql.Connection,它代表的是一个连接对象。简单说,就是我们程序与数据库连接。

        Connection作用:
            1.可以通过Connection获取操作sql的Statement对象。
                Statement createStatement() throws SQLException
                示例:
                Statement st=con.createStatement();

                了解:
                    1.可以获取执行预处理的PreparedStatement对象.
                        PreparedStatement prepareStatement(String sql) throws SQLException
                    2.可以获取执行存储过程的 CallableStatement
                        CallableStatement prepareCall(String sql) throws SQLException
            2.操作事务
                setAutoCommit(boolean flag);开启事务
                rollback();事务回滚
                commit();事务提交

    ------------------------------------------------------------------
    3.Statement详解
        java.sql.Statement用于执行sql语句.
        Statement作用:
            1.执行sql
                DML:insert update delete
                    int executeUpdate(String sql)

                    利用返回值判断非0来确定sql语句是否执行成功。

                DQL:select
                    ResultSet executeQuery(String sql)

                可以通过execute方法来执行任何sql语句.
                    execute(String sql):用于向数据库发送任意sql语句

            2.批处理操作
                addBatch(String sql); 将sql语句添加到批处理
                executeBatch();批量执行
                clearBatch();清空批处理.
    ---------------------------------------------------------------------
    4.ResultSet详解
        java.sql.ResultSet它是用于封装select语句执行后查询的结果。

        常用API
            1.next()方法
                public boolean next();
                    用于判断是否有下一条记录。如果有返回true,并且让游标向下移动一行。
                    如果没有返回false.

            2.可以通过ResultSet提供的getXxx()方法来获取当前游标指向的这条记录中的列数据。
                常用:
                    getInt()
                    getString()
                    getDate()
                    getDouble()
                    参数有两种
                        1.getInt(int columnIndex);
                        2.getInt(String columnName);

                    如果列的类型不知道,可以通过下面的方法来操作
                        getObject(int columnIndex);
                        getObject(String columnName);

    ----------------------------------------------------------------
    5.关闭资源
        Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。

        特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。

为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。

jdbc示例—-CURD
1.查询
1.查询全部
2.条件查询—根据id
2.修改
3.删除
4.添加

关于JdbcUtils抽取:
    只抽取到Connection。

    public static Connection getConnection() throws ClassNotFoundException,
        SQLException {

    Class.forName("com.mysql.jdbc.Driver");

    // 2.获取连接
    Connection con = DriverManager.getConnection("jdbc:mysql:///day17",
            "root", "abc");

    return con;
}

关于上述的抽取JdbcUtils的缺点:
    1.它只能针对于mysql数据库。
    2.每一次调用,都会注册一次驱动.

对于上述问题,对JdbcUtils进行修改:
    1.将关于连接数据库的信息定义到配置文件中。
      读取配置文件进行加载.       

        public class JdbcUtils {

            private static final String DRIVERCLASS;
            private static final String URL;
            private static final String USERNAME;
            private static final String PASSWORD;

            static {
                DRIVERCLASS = ResourceBundle.getBundle("jdbc").getString("driverClass");
                URL = ResourceBundle.getBundle("jdbc").getString("url");
                USERNAME = ResourceBundle.getBundle("jdbc").getString("username");
                PASSWORD = ResourceBundle.getBundle("jdbc").getString("password");
            }

            static {
                try {
                    // 将加载驱动操作,放置在静态代码块中.这样就保证了只加载一次.
                    Class.forName(DRIVERCLASS);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }

            public static Connection getConnection() throws SQLException {

                // 2.获取连接
                Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD);

                return con;
            }

}

滚动结果集:(了解)
    默认得到的ResultSet它只能向下遍历(next()),对于ResultSet它可以设置成是滚动的,可以向上遍历,
    或者直接定位到一个指定的物理行号.

    问题:怎样得到一个滚动结果集?

        Statement st=con.createStatement();
        ResultSet rs=st.executeQuery(sql);
        这是一个默认结果集:只能向下执行,并且只能迭代一次。

         Statement stmt = con.createStatement(
                                  ResultSet.TYPE_SCROLL_INSENSITIVE,
                                  ResultSet.CONCUR_UPDATABLE);
        ResultSet rs = stmt.executeQuery(sql);
        这个就可以创建滚动结果集.
        简单说,就是在创建Statement对象时,不使用createStatement();
        而使用带参数的createStatement(int,int)


        Statement createStatement(int resultSetType,
                      int resultSetConcurrency)
                      throws SQLException

        resultSetType - 结果集类型,它是 ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVE 或 ResultSet.TYPE_SCROLL_SENSITIVE 之一
        resultSetConcurrency - 并发类型;它是 ResultSet.CONCUR_READ_ONLY 或 ResultSet.CONCUR_UPDATABLE 之一 

        第一个参数值  
        ResultSet.TYPE_FORWARD_ONLY    该常量指示光标只能向前移动的 ResultSet 对象的类型。
        ResultSet.TYPE_SCROLL_INSENSITIVE  该常量指示可滚动但通常不受 ResultSet 底层数据更改影响的 ResultSet 对象的类型。
        ResultSet.TYPE_SCROLL_SENSITIVE  该常量指示可滚动并且通常受 ResultSet 底层数据更改影响的 ResultSet 对象的类型。

        第二个参数值
        ResultSet.CONCUR_READ_ONLY    该常量指示不可以更新的 ResultSet 对象的并发模式。
        ResultSet.CONCUR_UPDATABLE    该常量指示可以更新的 ResultSet 对象的并发模式。

        以上五个值,可以有三种搭配方式
            ResultSet.TYPE_FORWARD_ONLY   ResultSet.CONCUR_READ_ONLY   默认
            ResultSet.TYPE_SCROLL_INSENSITIVE   ResultSet.CONCUR_READ_ONLY              
            ResultSet.TYPE_SCROLL_SENSITIVE  ResultSet.CONCUR_UPDATABLE 


        常用API
            next():移动到下一行
            previous():移动到前一行
            absolute(int row):移动到指定行
            beforeFirst():移动resultSet的最前面
            afterLast() :移动到resultSet的最后面

updateRow() :更新行数据

DAO模式
DAO模式(Data Access Object 数据访问对象):在持久层通过DAO将数据源操作完全封装起来,业务层通过操作Java对象,完成对数据源操作
* 业务层无需知道数据源底层实现 ,通过java对象操作数据源

DAO模式结构 :
1、数据源(MySQL数据库)
2、Business Object 业务层代码,调用DAO完成 对数据源操作
3、DataAccessObject 数据访问对象,持久层DAO程序,封装对数据源增删改查,提供方法参数都是Java对象
4、TransferObject 传输对象(值对象) 业务层通过向数据层传递 TO对象,完成对数据源的增删改查

使用dao模式完成登录操作
    1.web层
        login.jsp  LoginServlet  User
    2.service层
        UserService
    3.dao层
        UserDao

-------------------------
sql注入
    由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,
    达到改变SQL运行结果的目的,也可以完成恶意攻击。

    示例:
        在输入用户名时  tom' or '1'='1
        这时就不会验证密码了。
    解决方案:
        PreparedStatement(重点)
        它是一个预处理的Statement,它是java.sql.Statement接口的一个子接口。

    总结PreparedStatement使用:
        1.在sql语句中,使用"?"占位
            String sql="select * from user where username=? and password=?";
        2.得到PreparedStatement对象
            PreparedStatement pst=con.prepareStatement(String sql);
        3.对占位符赋值
            pst.setXxx(int index,Xxx obj);
            例如:
                setInt()
                setString();
            参数index,代表的是"?"的序号.注意:从1开始。

        4.执行sql
            DML:  pst.executeUpdate();
            DQL:   pst.executeQuery();
            注意:这两方法无参数

    关于PreparedStatement优点:
        1.解决sql注入(具有预处理功能)

2.不需要在拼sql语句。

jdbc处理大数据
mysql中有大数据
blob 大二进制
TINYBLOB(255)、BLOB(64kb)、MEDIUMBLOB(16m)和LONGBLOB(4g)
text(clob) 大文本
TINYTEXT(255)、TEXT(64kb)、MEDIUMTEXT(16m)和LONGTEXT(4g)

    对于大数据操作,我们一般只有两种 insert  select
    演示1:
        大二进制操作
        create table myblob(
            id int primary key auto_increment,
            content longblob
        )

        向表中插入数据


        问题1:java.lang.AbstractMethodError: com.mysql.jdbc.PreparedStatement.setBinaryStream(ILjava/io/InputStream;)V
            原因:mysql驱动不支持setBinaryStream(int,InputStream);

        修改成
            pst.setBinaryStream(1, fis,file.length());
            原因:因为mysql驱动不支持setBinaryStream(int,InputStream,long);

        解决:
            mysql驱动支持setBinaryStream(int,InputStream,int);


        注意:如果文件比较大,那么需要在my.ini文件中配置
            max_allowed_packet=64M

    总结:
        存
                pst.setBinaryStream(1, fis, (int) (file.length()));
        取
                InputStream is = rs.getBinaryStream("content");
    --------------------------------------------------------------
    演示:存储大文本
        create table mytext(
            id int primary key auto_increment,
            content longtext
        )
    存储
        File file = new File("D:\\java1110\\workspace\\day17_3\\a.txt");
        FileReader fr = new FileReader(file);
        pst.setCharacterStream(1, fr, (int) (file.length()));
    获取:

Reader r = rs.getCharacterStream(“content”);

jdbc批处理
一次可以执行多条sql语句.

在jdbc中可以执行sql语句的对象有Statement,PreparedStatement,它们都提供批处理.

1.Statement执行批处理
    addBatch(String sql);  将sql语句添加到批处理
    executeBatch(); 执行批处理
    clearBatch();

2.PreparedStatement执行批处理
    addBatch();
    executeBatch();
    clearBatch();

以上两个对象执行批处理区别?
    1.Statement它更适合执行不同sql的批处理。它没有提供预处理功能,性能比较低。        
    2.PreparedStatement它适合执行相同sql的批处理,它提供了预处理功能,性能比较高。

注意;mysql默认情况下,批处理中的预处理功能没有开启,需要开启
    1.在 url下添加参数
        url=jdbc:mysql:///day17?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true
    2.注意驱动版本
        Mysql驱动要使用mysql-connector-java-5.1.13以上

事务:
问题:事务是什么,有什么用?
事务就是一个事情,组成这个事情可能有多个单元,要求这些单元,要么全都成功,要么全都不成功。
在开发中,有事务的存在,可以保证数据完整性。

问题:事务怎样操作

    创建表:
    create table account(
       id int primary key auto_increment,
       name varchar(20),
       money double
    );

    insert into account values(null,'aaa',1000);
    insert into account values(null,'bbb',1000);
    insert into account values(null,'ccc',1000);


    1.mysql下怎样操作

        方式1:
            start transaction  开启事务
            rollback 事务回滚
            commit 事务提交

        方式2:
             show variables like '%commit%'; 可以查看当前autocommit值
                在mysql数据库中它的默认值是"on"代表自动事务.

                自动事务的意义就是:执行任意一条sql语句都会自动提交事务.

                测试:将autocommit的值设置为off
                    1.set autocommit=off 关闭自动事务。
                    2.必须手动commit才可以将事务提交。
                    注意:mysql默认autocommit=on  oracle默认的autocommit=off;

    2.jdbc下怎样操作
        java.sql.Connection接口中有几个方法是用于可以操作事务

            1.setAutocommit(boolean flag);
                如果flag=false;它就相当于start transaction;
            2.rollBack()
                事务回滚。
            3.commit()

事务提交

事务特性(重点) ACID
? 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
? 一致性(Consistency)
事务前后数据的完整性必须保持一致。
? 隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
? 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

-------------------------------------
如果不考虑事务的隔离性,会出现什么问题?
    1.脏读 一个事务读取到另一个事务的未提交数据。
    2.不可重复读
            两次读取的数据不一致(update)
    3.虚读(幻读)
            两次读取的数据一一致(insert)


    4.丢失更新
        两个事务对同一条记录进行操作,后提交的事务,将先提交的事务的修改覆盖了。

-----------------------------------------   
演示以上问题,以及问题的解决方案

    对于以上的问题,我们可以通过设置事务的隔离级别来解决。

    1.事务的隔离级别有哪些?
        1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
        2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
        3 Read committed:可避免脏读情况发生(读已提交)
        4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

    2.怎样设置事务的隔离级别?
        1.mysql中设置 
            1.查看事务隔离级别
                select @@tx_isolation   查询当前事务隔离级别
                mysql中默认的事务隔离级别是  Repeatable read.
                扩展:oracle 中默认的事务隔离级别是  Read committed

            2.mysql中怎样设置事务隔离级别
                set session transaction isolation level 设置事务隔离级别

        2.jdbc中设置
            在jdbc中设置事务隔离级别
            使用java.sql.Connection接口中提供的方法
                void setTransactionIsolation(int level) throws SQLException
                参数level可以取以下值:
                    level - 以下 Connection 常量之一:
                    Connection.TRANSACTION_READ_UNCOMMITTED、
                    Connection.TRANSACTION_READ_COMMITTED、
                    Connection.TRANSACTION_REPEATABLE_READ 
                    Connection.TRANSACTION_SERIALIZABLE。
                    (注意,不能使用 Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。) 


    --------------------------------------------------
    3.演示

        1.脏读
            一个事务读取到另一个事务的为提交数据
            设置A,B事务隔离级别为   Read uncommitted

            set session transaction isolation level  read uncommitted;

            1.在A事务中
                start transaction;
                update account set money=money-500 where name='aaa';
                update account set money=money+500 where name='bbb';

            2.在B事务中
                start transaction;
                select * from account;

            这时,B事务读取时,会发现,钱已经汇完。那么就出现了脏读。

            当A事务提交前,执行rollback,在commit, B事务在查询,就会发现,钱恢复成原样  
            也出现了两次查询结果不一致问题,出现了不可重复读.

        2.解决脏读问题
            将事务的隔离级别设置为 read committed来解决脏读

            设置A,B事务隔离级别为   Read committed

            set session transaction isolation level  read committed;

            1.在A事务中
                start transaction;
                update account set money=money-500 where name='aaa';
                update account set money=money+500 where name='bbb';

            2.在B事务中
                start transaction;
                select * from account;

            这时B事务中,读取信息时,是不能读到A事务未提交的数据的,也就解决了脏读。

            让A事务,提交数据 commit;   

            这时,在查询,这次结果与上一次查询结果又不一样了,还存在不可重复读。

        3.解决不可重复读
            将事务的隔离级别设置为Repeatable read来解决不可重复读。
            设置A,B事务隔离级别为   Repeatable read;
            set session transaction isolation level  Repeatable read;

            1.在A事务中
                    start transaction;
                    update account set money=money-500 where name='aaa';
                    update account set money=money+500 where name='bbb';

            2.在B事务中
                    start transaction;
                    select * from account;  
            当A事务提交后commit;B事务在查询,与上次查询结果一致,解决了不可重复读。

        4.设置事务隔离级别   Serializable ,它可以解决所有问题
            set session transaction isolation level Serializable;

            如果设置成这种隔离级别,那么会出现锁表。也就是说,一个事务在对表进行操作时,
            其它事务操作不了。
    --------------------------------------------------
    总结:
        脏读:一个事务读取到另一个事务为提交数据
        不可重复读:两次读取数据不一致(读提交数据)---update
        虚读:两次读取数据不一致(读提交数据)----insert

        事务隔离级别:
            read uncommitted 什么问题也解决不了.
            read committed 可以解决脏读,其它解决不了.
            Repeatable read 可以解决脏读,可以解决不可重复读,不能解决虚读.
            Serializable 它会锁表,可以解决所有问题.

            安全性:serializable > repeatable read > read committed > read uncommitted 
            性能 :serializable < repeatable read < read committed < read uncommitted 

            结论: 实际开发中,通常不会选择 serializable 和 read uncommitted ,
            mysql默认隔离级别 repeatable read ,oracle默认隔离级别 read committed
丢失更新
    多个事务对同一条记录进行了操作,后提交的事务将先提交的事务操作覆盖了。

    查看图.

    问题:怎样解决丢失更新问题?

        解决丢失更新可以采用两种方式:
            1.悲观锁
                悲观锁 (假设丢失更新一定会发生 ) ----- 利用数据库内部锁机制,管理事务
                    提供的锁机制
                    1.共享锁
                        select * from table lock in share mode(读锁、共享锁)
                    2.排它锁
                            select * from table for update (写锁、排它锁)

                update语句默认添加排它锁 


            2.乐观锁
                乐观锁 (假设丢失更新不会发生)------- 采用程序中添加版本字段解决丢失更新问题

                create table product (
                   id int,
                   name varchar(20),
                   updatetime timestamp
                );

                insert into product values(1,'冰箱',null);
                update product set name='洗衣机' where id = 1;

        解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,

与修改时版本字段不一致,说明别人进行修改过数据 (重改)

连接池
问题:连接池是什么,有什么用?

    连接池:就是创建一个容器,用于装入多个Connection对象,在使用连接对象时,从容器中获取一个Connection,
           使用完成后,在将这个Connection重新装入到容器中。这个容器就是连接池。(DataSource)
           也叫做数据源.

    我们可以通过连接池获取连接对象.
    优点:
        节省创建连接与释放连接 性能消耗 ---- 连接池中连接起到复用的作用 ,提高程序性能         
-----------------------------------------------------------------------------------
自定义连接池
    1.创建一个MyDataSource类,在这个类中创建一个LinkedList<Connection>
    2.在其构造方法中初始化List集合,并向其中装入5个Connection对象。
    3.创建一个public Connection getConnection();从List集合中获取一个连接对象返回.
    4.创建一个  public void readd(Connection) 这个方法是将使用完成后的Connection对象重新装入到List集合中.

代码问题:
    1.连接池的创建是有标准的.
        在javax.sql包下定义了一个接口 DataSource          
        简单说,所有的连接池必须实现javax.sql.DataSource接口,

        我们的自定义连接池必须实现DataSource接口。  

    2.我们操作时,要使用标准,怎样可以让 con.close()它不是销毁,而是将其重新装入到连接池.

        要解决这个问题,其本质就是将Connection中的close()方法的行为改变。

        怎样可以改变一个方法的行为(对方法功能进行增强)
            1.继承
            2.装饰模式
                1.装饰类与被装饰类要实现同一个接口或继承同一个父类
                2.在装饰类中持有一个被装饰类引用
                3.对方法进行功能增强。
            3.动态代理
                可以对行为增强
                Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);

        结论:Connection对象如果是从连接池中获取到的,那么它的close方法的行为已经改变了,不在是销毁,而是重新装入到连接池。
--------------------------------------------------------------------
1.连接池必须实现javax.sql.DataSource接口。
2.要通过连接池获取连接对象  DataSource接口中有一个  getConnection方法.
3.将Connection重新装入到连接池   使用Connection的close()方法。

==================================================================================================
开源连接池
1.dbcp(了解)
dbcp是apache的一个开源连接池。

    要想使用DBCP连接池,要下载jar包
        导入时要导入两个
            commons-dbcp-1.4.jar
            commons-pool-1.5.6.jar

        关于dbcp连接池使用
            1.手动配置(手动编码)
                BasicDataSource bds = new BasicDataSource();

                // 需要设置连接数据库最基本四个条件
                bds.setDriverClassName("com.mysql.jdbc.Driver");
                bds.setUrl("jdbc:mysql:///day18");
                bds.setUsername("root");
                bds.setPassword("abc");

                // 得到一个Connection
                Connection con = bds.getConnection();

            2.自动配置(使用配置文件)
                Properties props = new Properties();
                FileInputStream fis = new FileInputStream("D:\\java1110\\workspace\\day18_2\\src\\dbcp.properties");
                props.load(fis);

                DataSource ds = BasicDataSourceFactory.createDataSource(props);

2.c3p0(必会)

    C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。
    目前使用它的开源项目有Hibernate,Spring等。
    c3p0与dbcp区别

    dbcp没有自动回收空闲连接的功能

    c3p0有自动回收空闲连接功能

    c3p0连接池使用
        1.导包
            c3p0-0.9.1.2.jar

        使用
            1.手动
                ComboPooledDataSource cpds = new ComboPooledDataSource();
                cpds.setDriverClass("com.mysql.jdbc.Driver");
                cpds.setJdbcUrl("jdbc:mysql:///day18");
                cpds.setUser("root");
                cpds.setPassword("abc");

            2.自动(使用配置文件)

                c3p0的配置文件可以是properties也可以是xml.

                c3p0的配置文件如果名称叫做 c3p0.properties or c3p0-config.xml 并且放置在classpath路径下(对于web应用就是classes目录)
                那么c3p0会自动查找。

                注意:我们其时只需要将配置文件放置在src下就可以。

                使用:
                    ComboPooledDataSource cpds = new ComboPooledDataSource();
                    它会在指定的目录下查找指定名称的配置文件,并将其中内容加载。

*tomcat内置连接池管理
tomcat内置连接池使用的是dbcp。

    问题1:tomcat怎样管理连接池?(配置)
        要想将一个dbcp连接池让 tomcat管理,只需要创建一个context.xml配置文件,在配置文件中
        配置相关信息,
        <Context>
          <Resource name="jdbc/EmployeeDB" auth="Container"
                    type="javax.sql.DataSource" username="root" password="abc"
                    driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day18"
                    maxActive="8" maxIdle="4"/>
        </Context>
        问题:context.xml文件位置:
            1.在tomcat/conf/context.xml     这时这个连接池是给整个服务器使用的。
            2.在tomcat/conf/Catalina/localhost 这时这个连接池只给localhost虚拟主机使用。
            3.将context.xml文件放置在web应用的META-INF下

            注意:如果是全局设置,那么我们需要将数据库驱动放置在tomcat/lib目录下


    问题2:怎样从tomcat中获取连接池?
        我们在servlet中获取连接池对象。
        Context context = new InitialContext();
        Context envCtx = (Context)context.lookup("java:comp/env"); 固定路径
        DataSource datasource = (DataSource) envCtx.lookup("jdbc/EmployeeDB"); 

    JNDI----->  JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,
                JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务
                和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是一种命名服务,

在这种服务里,对象不但有名称,还有属性。

2.元数据
问题:元数据是什么,有什么作用?
元数据(metaData) 指数据库中 库、表、列的定义信息

    1.DataBaseMetaData 数据库元数据(了解)
        问题:怎样获取一个DataBaseMetaData?

            Connection接口中定义了一个方法 getMetaData();

        问题:常用API

            String driverName = dmd.getDriverName(); //获取驱动名称
            System.out.println(driverName);

            String userName = dmd.getUserName();//获取用户名
            System.out.println(userName);

            String url = dmd.getURL();//获取url
            System.out.println(url);

            String databaseProductName = dmd.getDatabaseProductName(); //获取数据库名称
            System.out.println(databaseProductName);

            String databaseProductVersion = dmd.getDatabaseProductVersion();//获取数据库版本.
            System.out.println(databaseProductVersion);

            ResultSet getPrimaryKeys(String catalog,
                     String schema,
                     String table)
                     throws SQLException
            获取表中主键相关描述
                每个主键列描述都有以下列:
                TABLE_CAT String => 表类别(可为 null) 
                TABLE_SCHEM String => 表模式(可为 null) 
                TABLE_NAME String => 表名称 
                COLUMN_NAME String => 列名称 
                KEY_SEQ short => 主键中的序列号(值 1 表示主键中的第一列,值 2 表示主键中的第二列)。 
                PK_NAME String => 主键的名称(可为 null)


    2.ParameterMetaData 参数元数据
        参数元数据主要用于获取:sql语句中占位符的相关信息.

        问题:怎样获取ParameterMetaData?
            在PreparedStatement中有一个方法getParameterMetaData可以获取.

        问题:怎样使用?
            int count = pmd.getParameterCount(); // 获取参数 个数
            System.out.println(count);

            String type1 = pmd.getParameterTypeName(1);//获取参数的类型
            System.out.println(type1);

            注意:在获取参数类型时会产生异常
                java.sql.SQLException: Parameter metadata not available for the given statement
            解决方案:
                在url后添加参数
                jdbc:mysql:///day18?generateSimpleParameterMetadata=true
            添加这个参数后,我们在获取,它的结果也是varchar,原因:是mysql驱动的支持问题。   

    3.ResultSetMetaData 结果集元数据

        问题:怎样获取结果集元数据?
            可以通过ResultSet的getMetaData()方法获取.

        问题:怎样使用?
            System.out.println(rsmd.getColumnCount());//获取结果集中列数量

            System.out.println(rsmd.getColumnName(2));//获取结果集中指定列的名称.

System.out.println(rsmd.getColumnTypeName(3));//获取结果集中指定列的类型。

3.dbutils工具
问题:dbutils是什么,有什么作用?
它就是一个简单的jdbc封装工具.
使用dbutils可以简化操作.
要使用dbutils需要导入jar包.

dbutils核心
    1.QueryRunner类
        它是用于执行sql语句的类。
        1.query 用于执行select 
        2.update 用于执行update delete insert
        3.batch 批处理
    2.ResultSetHandler接口
        用于定义结果集的封装              
        它提供九个实现类,可以进行不同的封装。
    3.DbUtils类
        它提供关于关闭资源以及事务rollback,commit操作。
-----------------------------------------------------
Dbutlis详解
    1.QueryRunner
        1.QueryRunner怎样获取
            1.new QueryRunner()
                如果是使用这种构造创建的QueryRunner,它的事务是手动控制.
            2.new QueryRunner(DataSource ds);
                如果是使用这种构造,它的事务是自动事务,简单说,一条sql一个事务。

        2.QueryRunner中的三个核心方法
            query
            update
            batch
            对于上述三个方法,它们提供很多重载。
            如果QueryRunner在创建时,没有传递DataSource参数,那么在使用
            query,update,batch方法时,要传递Connection参数
            如果QueryRunner在创建时,传递了Dataource参数,好么在使用
            query,update,batch方法时,不需要传递Connection参数。

        总结:
            怎样配套使用:
                QueryRunner runner=new QueryRunner();
                runner.query(Connection,sql,ResultSetHandler,Object... param);
                runner.update(Connection,sql,Object...param);
                runner.batch(Connection con,sql,Object[][] objs);

                QueryRunner runner=new QueryRunner(DataSource ds);
                runner.query(sql,ResultSetHandler,Object... param);
                runner.update(sql,Object...param);
                runner.batch(sql,Object[][] objs);

    -----------------------------------------------------------------           
    ResultSetHandler接口  
        用于封装结果集.

============================================================================
模仿QueryRunner
    1.query方法模仿
        public <T> T query(Connection con, String sql, MyResultSetHandler<T> mrs,Object... params) throws SQLException {

                PreparedStatement pst = con.prepareStatement(sql); // 得到一个预处理的Statement.
                // 问题:sql语句中可能存在参数,需要对参数赋值。

                ParameterMetaData pmd = pst.getParameterMetaData();
                // 可以得到有几个参数
                int count = pmd.getParameterCount();
                for (int i = 1; i <= count; i++) {
                    pst.setObject(i, params[i - 1]);
                }

                ResultSet rs = pst.executeQuery(); // 得到了结果集,要将结果集封装成用户想要的对象,但是,工具不可能知道用户需求。

                return mrs.handle(rs);
            }
    2.update方法模仿
        public int update(Connection con, String sql, Object... params) throws SQLException {

            PreparedStatement pst = con.prepareStatement(sql); // 得到一个预处理的Statement.
            // 问题:sql语句中可能存在参数,需要对参数赋值。

            ParameterMetaData pmd = pst.getParameterMetaData();
            // 可以得到有几个参数
            int count = pmd.getParameterCount();
            for (int i = 1; i <= count; i++) {
                pst.setObject(i, params[i - 1]);
            }

            int row = pst.executeUpdate();
            // 关闭资源
            pst.close();
            return row;

}

ResulsetHandler九个实现类

         ArrayHandler, 将结果集中第一条记录封装到Object[],数组中的每一个元素就是记录中的字段值。
         ArrayListHandler, 将结果集中每一条记录封装到Object[],数组中的每一个元素就是记录中的字段值。在将这些数组装入到List集合。

         BeanHandler(重点), 将结果集中第一条记录封装到一个javaBean中。
         BeanListHandler(重点), 将结果集中每一条记录封装到javaBean中,在将javaBean封装到List集合.

         ColumnListHandler, 将结果集中指定列的值封装到List集合.

         MapHandler, 将结果集中第一条记录封装到Map集合中,集合的 key就是字段名称,value就是字段值
         MapListHandler, 将结果集中每一条记录封装到Map集合中,集合的 key就是字段名称,value就是字段值,在将这些Map封装到List集合

         KeyedHandler,在使用指定的列的值做为一个Map集合的key,值为每一条记录的Map集合封装。
         ScalarHandler 进行单值查询 select count(*) from account;

    ---------------------------------------------------------
    扩展:实现BeanHandler
        使用BeanUtils实现
            Object obj = null;

            Map<String, String[]> map = new HashMap<String, String[]>();

            ResultSetMetaData md = rs.getMetaData();
            int count = md.getColumnCount();

            if (rs.next()) {
                try {
                    obj = clazz.newInstance();
                    for (int i = 1; i <= count; i++) {
                        map.put(md.getColumnName(i),
                                new String[] { rs.getString(md.getColumnName(i)) });
                    }
                    BeanUtils.populate(obj, map);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }

            }

            return obj;

使用虚拟主机可以将项目部署成顶级域名

    1.在service.xml文件
        1.端口修改为80
        2. 配置主机
          <Host name="www.customer.com"  appBase="D:\java1110\workspace\day19_2"
                unpackWARs="true" autoDeploy="true">


            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log." suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />

                   <Context path="" docBase="D:\java1110\workspace\day19_2\WebRoot" />
          </Host>
        3.在hosts文件中配置
            127.0.0.1  www.customer.com         

分页查询
问题:什么是分页,为什么使用分页?
分页就是将数据以多页去展示,使用分页可以提高客户的感受。

分页分类:
    1.物理分页
        只从数据库中查询出当前页的数据。
        优点:不占用很多内存
        缺点:效率比较低
    2.逻辑分页
        从数据库中将所有记录查询出业,存储到内存中,要想展示当前页
        数据,直接从内存中获取。
        优点:效率高
        缺点:占用内存比较高

    在java开发领域,我们使用的比较多的是物理分页。
        物理分页的实现:

            1.直接使用jdbc完成
                使用滚动结果集.  优点:跨数据库。缺点:性能低。
            2.使用数据库本身提供的分页操作.
                会使用每一个数据库特定的分页函数,优点:性能高  缺点:不能跨数据库。

                mysql:limit
                sqlservlet:top
                oracle:rownum

    介绍limit使用.
        select * from 表 limit m,n;
        m:代表的是从第几条开始  注意:它是从0开始记录.
        n:代表查询几条记录.

        示例:分页音,每页显示6条,要查询第2页的数据.

            select * from 表  limit  (页码-1)*每页条数,每页条数;

------------------------------------------------------------------------------
分页分析:
    1.页码  默认第一页  
    2.每页条数   人为定义
    3.总条数   select count(*) from 表
    4.总页数    总页数=总条数%每页条数==0?总条数/每页条数:总条数/每页条数+1
                总页数=Math.ceil(总条数*1.0/每页条数);

    5.当前页的数据  List<?>----->select * from 表  limit  (页码-1)*每页条数,每页条数;    

监听器:
问题:什么是监听器?监听器有什么作用?

    概念:
        1.事件  ActionEvent
        2.事件源 JButton
        3.监听器 ActionListener
        4.注册监听 addActionListener();

    监听器就是可以监听某一个事件在执行一个特定操作时,我们可以让其触发一个操作。  

    可以在满足特定条件的情况下执行一段操作。


问题:我们现在学习的是javaweb,那么在javaweb中有什么监听器,有什么作用?

    javaweb中的监听器,主要用于监听javaweb中常用对象(request(HttpServletRequest),session(HttpSession),application(ServletContext))的三种类型操作
        1.对象的创建与销毁
        2.对象的属性变化
        3.session绑定javaBean.

    在javaweb中servlet规范中定义了三种技术  servlet  Listener Filter

    1.监听创建与销毁
        HttpServletRequest
            监听器:ServletRequestListener可以监听request对象的创建与销毁.              
        HttpSession
            监听器:HttpSessionListener可以监听session对象的创建与销毁.
        ServletContext
            监听器:ServletContextListener可以监听application对象的创建与销毁。

    2.监听web对象的属性变化
        HttpServletRequest属性变化
            监听器:ServletRequestAttributeListener监听request对象的属性变化
        HttpSession属性变化
            监听器:HttpSessionAttributeListener 监听session对象的属性变化
        ServletContext属性变化
            监听器:ServletContextAttributeListener监听application对象的属性变化。
------------------------------------------------------------------------------------
演示关于web中监听器怎样使用?

    创建监听器步骤:
        1.创建一个类,去实现指定的监听器接口.
        2.重写接口中方法。
        3.在web.xml文件中配置注册监听。

        演示:
            1.监听application对象的创建与销毁.
                问题:application对象什么时候创建,什么时候销毁的?
                    application对象是服务器启动时创建,
                    服务器关闭时销毁。

            2.监听session对象创建与销毁
                问题:session对象什么时候创建,什么时候销毁?

                    session对象创建:
                        reqeust.getSession();它是用于获取session.
                        是否创建,分以下几种情况:
                            1.请求中如果没有jsessionid,那么就是创建session对象。
                            2.如果请求头中有jsessionid值:
                                1.如果在服务器端,有一个session的id值与其一样,不创建,直接使用。
                                2.如果在服务器端,没有这个session的id值,那么会创建。
                    session销毁:
                        1.默认超时  30分钟
                        2.设置session超时时间
                            setMaxInactiveInterval(int interval) 
                        3.invalidate()手动销毁.
                        4.关闭服务器
            3.监听request对象创建与销毁
                问题:request对象什么时候创建,什么时候销毁?
                    请求发生,request对象创建,响应产生request对象销毁。
        ----------------------------------------------------------------------
        演示监听属性变化
            演示监听session的属性变化

            问题:在监听器中是否可以得到属性值?

                常识:在java的监听机制中,是可以在监听器中获取事件源的。

                我们在开发中,如果有到了事件触发机制,那么一般情况下,都可以使用
                方法的参数(事件对象)来获取想要的信息.


    ----------------------------------------------------------              
思考一个问题:这些监听器在开发中有什么用?
    在主流中应用比较少,但是可以完成一些性能监试操作。

-----------------------------------------------------------------------------
监听器案例:

    功能:扫描session对象在指定时间内没有使用,人为销毁。

    分析:
        1.怎样知识session多长时间没有使用?
            当前时间-最后使用时间(public long getLastAccessedTime())

        2.什么时候开始扫描,扫描多长时间?
            可以使用Timer完成

    完成定时扫描session,如果超时没有使用,销毁案例:

        1.要将所有的session对象得到,保存到集合中。
            1.创建一个监听器 ServletContextListener,它服务器启动时,创建一个集合保存到ServletContext域。
            2.创建一个监听器 HttpSessionListener,当创建一个session时,就从ServletContext域中获取集合,将session对象储存到集合中。

        2.定时扫描

            问题:
                1.session超时,不能只销毁session,还要从集合中移除。

                2.我们的操作,它是多线程的,要考虑集合的同步问题。
                    1.集合需要是线程安全的。
                    2.需要使用迭代器进行遍历。


---------------------------------------------------------------------------------------
session绑定javaBean

    1.HttpSessionBindingListener
        这个监听器,可以让javaBean对象,感知它被绑定到session中或从session中移除。
    2.HttpSessionActivationListener
        这个监听器,可以让javaBean感知,被钝化或活化。

            钝化--->将session中的javaBean保存到文件中.
            活化--->从文件中将javaBean直接获取。

            需要创建一个配置文件context.xml
                这个文件保存到META-INF目录下.
                <Context>
                <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
                <Store className="org.apache.catalina.session.FileStore" directory="it315"/>
                </Manager>
                </Context>          


    这两个监听器特点;
        1.它们是由javaBean实现.
        2.它们不需要在web.xml文件中配置.
javaWeb之过滤器

Fileter介绍
? Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能
? Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截
? Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源(拦截url)进行拦截后,WEB服务器每次在调用web资源之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
? 调用目标资源之前,让一段代码执行
? 是否调用目标资源(即是否让用户访问web资源)。
? web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
? 调用目标资源之后,让一段代码执行
开发Fileter步骤
? Filter开发分为二个步骤:
? 编写java类实现Filter接口,并实现(三个方法)其doFilter方法。
? 在 web.xml 文件中使用和元素对编写的filter类进行注册,并设置它所能拦截的资源。
? Filter链 — FilterChain
? 在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
? web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
? Filter链实验(查看filterChain API文档)
Filter的生命周期
? init(FilterConfig filterConfig)throws ServletException:
? 和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法进行初始化(注:filter对象只会创建一次,init方法也只会执行一次。示例 )
? 开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。(filterConfig对象见下页PPT)
? doFilter(ServletRequest,ServletResponse,FilterChain)
? 每次filter进行拦截都会执行
? 在实际开发中方法中参数request和response通常转换为HttpServletRequest和HttpServletResponse类型进行操作
? destroy():
? 在Web容器卸载 Filter 对象之前被调用。
FilterConfig接口
? 用户在配置filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
? String getFilterName():得到filter的名称。
? String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
? Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
? public ServletContext getServletContext():返回Servlet上下文对象的引用。
注册与映射Filter
? 注册

testFitler
org.test.TestFiter

word_file
/WEB-INF/word.txt


? 用于为过滤器指定一个名字,该元素的内容不能为空。
? 元素用于指定过滤器的完整的限定类名。
? 元素用于为过滤器指定初始化参数,它的子元素指定参数的名字,指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
? 映射Filter

? 元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
? 子元素用于设置filter的注册名称。该值必须是在元素中声明过的过滤器的名字
? 设置 filter 所拦截的请求路径(过滤器关联的URL样式)
? 指定过滤器所拦截的Servlet名称。
? 指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
? 子元素可以设置的值及其意义:
? REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
? INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
? FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
? ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。


testFilter
/index.jsp
REQUEST
FORWARD

Filter示例
示例1全站统一字符编码过滤器
编写jsp 输入用户名,在Servlet中获取用户名,将用户名输出到浏览器上

处理请求post乱码代码
request.setCharacterEncoding(“utf-8”);
设置响应编码集代码
response.setContentType(“text/html;charset=utf-8”);

经常会使用,而过滤器可以在目标资源之前执行,将很多程序中处理乱码公共代码,提取到过滤器中 ,以后程序中不需要处理编码问题了
示例2禁用所有JSP页面缓存
因为动态页面数据,是由程序生成的,所以如果有缓存,就会发生,客户端查看数据不是最新数据情况 ,对于动态程序生成页面,设置浏览器端禁止缓存页面内容

response.setDateHeader(“Expires”,-1);
response.setHeader(“Cache-Control”,”no-cache”);
response.setHeader(“Pragma”,”no-cache”);

将禁用缓存代码,提起到过滤器中,通过url配置,禁用所有JSP页面的缓存
示例3设置图片过期时间
Tomcat缓存策略

对于服务器端经常不变化文件,设置客户端缓存时间,在客户端资源缓存时间到期之前,就不会去访问服务器获取该资源 ——– 比tomcat内置缓存策略更优手段
* 减少服务器请求次数,提升性能

设置静态资源缓存时间,需要设置 Expires 过期时间 ,在客户端资源没有过期之前,不会产生对该资源的请求的
* 设置Expires 通常使用 response.setDateHeader 进行设置 设置毫秒值
示例4自动登录案例(MD5加密)
在访问一个站点,登陆时勾选自动登陆(三个月内不用登陆),操作系统后,关闭浏览器;过几天再次访问该站点时,直接进行登陆后状态

在数据库中创建 user表

create table user (
id int primary key auto_increment,
username varchar(20),
password varchar(40),
role varchar(10)
);

insert into user values(null,’admin’,’123’,’admin’);
insert into user values(null,’aaa’,’123’,’user’);
insert into user values(null,’bbb’,’123’,’user’);

自动登陆 :未登录、存在自动登陆信息、自动登陆信息正确

在用户完成登陆后,勾选自动登陆复选框,服务器端将用户名和密码 以Cookie形式,保存在客户端 。当用户下次访问该站点,AutoLoginFilter 过滤器从Cookie中获取 自动登陆信息
1、判断用户是否已经登陆,如果已经登陆,没有自动登陆的必要
2、判断Cookie中是否含有自动登陆信息 ,如果没有,无法完成自动登陆
3、使用cookie用户名和密码 完成自动登陆

如果将用户密码保存在cookie文件中,非常不安全的 ,通常情况下密码需要加密后才能保存到客户端
* 使用md5算法对密码进行加密
* md5 加密算法是一个单向加密算法 ,支持明文—密文 不支持密文解密

MySQL数据库中提供md5 函数,可以完成md5 加密
mysql> select md5(‘123’);
+———————————-+
| md5(‘123’) |
+———————————-+
| 202cb962ac59075b964b07152d234b70 |
+———————————-+
加密后结果是32位数字 16进制表示

Java中提供类 MessageDigest 完成MD5加密

将数据表中所有密码 变为密文 update user set password = md5(password) ;
在登陆逻辑中,对密码进行md5 加密
在AutoLoginFilter 因为从Cookie中获得就是加密后密码,所以登陆时无需再次加密
示例5 URL级别的权限控制

认证:who are you ? 用户身份的识别 ———— 登陆功能
权限:以认证为基础 what can you do ? 您能做什么? 必须先登陆,才有身份,有了身份,才能确定可以执行哪些操作
示例6通用get和post乱码过滤器

Decorator模式
1、包装类需要和被包装对象 实现相同接口,或者继承相同父类
2、包装类需要持有 被包装对象的引用
在包装类中定义成员变量,通过包装类构造方法,传入被包装对象
3、在包装类中,可以控制原来那些方法需要加强
不需要加强 ,调用被包装对象的方法
需要加强,编写增强代码逻辑

ServletRequestWrapper 和 HttpServletRequestWrapper 提供对request对象进行包装的方法,但是默认情况下每个方法都是调用原来request对象的方法,也就是说包装类并没有对request进行增强

在这两个包装类基础上,继承HttpServletRequestWrapper ,覆盖需要增强的方法即可
系统中存在很多资源,将需要进行权限控制的资源,放入特殊路径中,编写过滤器管理访问特殊路径的请求,如果没有相应身份和权限,控制无法访问

在Filter中,对request对象进行包装,增强获得参数的方法
getParameter
getParameterValues
getParameterMap

ServletResponseWrapper 和 HttpServletResponseWrapper 提供了对response 对象包装,继承 HttpServletResponseWrapper ,覆盖需要增强response的方法

文件上传与下载
上传
文件上传概述
? 实现web开发中的文件上传功能,需完成如下二步操作:
? 在web页面中添加上传输入项
? 在servlet中读取上传文件的数据,并保存到服务器硬盘中。
? 如何在web页面中添加上传输入项?
? 标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
? 1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
? 2、必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
? 3、表单的提交方式要是post
? 如何在Servlet中读取文件上传数据,并保存到本地硬盘中?
? Request对象提供了一个getInputStream方法,通过这个方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,在servlet端编程直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作,示例。
? 为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。
? 使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。
文件上传步骤
? 实现步骤
1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象。
4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件
? True 为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
? False 为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据。
FileUpload上传操作核心API
1、DiskFileItemFactory 磁盘文件项工厂类
public DiskFileItemFactory(int sizeThreshold, java.io.File repository) 构造工厂时,指定内存缓冲区大小和临时文件存放位置

public void setSizeThreshold(int sizeThreshold) 设置内存缓冲区大小,默认10K

public void setRepository(java.io.File repository)设置临时文件存放位置,默认System.getProperty(“java.io.tmpdir”).

内存缓冲区: 上传文件时,上传文件的内容优先保存在内存缓冲区中,当上传文件大小超过缓冲区大小,就会在服务器端产生临时文件
临时文件存放位置: 保存超过了内存缓冲区大小上传文件而产生临时文件
* 产生临时文件可以通过 FileItem的delete方法删除

2、ServletFileUpload 文件上传核心类
static boolean isMultipartContent(javax.servlet.http.HttpServletRequest request) 判断request的编码方式是否为multipart/form-data

java.util.List parseRequest(javax.servlet.http.HttpServletRequest request) 解析request,将请求体每个部分封装FileItem对象,返回List

void setFileSizeMax(long fileSizeMax) 设置单个文件上传大小
void setSizeMax(long sizeMax) 设置总文件上传大小

void setHeaderEncoding(java.lang.String encoding) 设置编码集 解决上传文件名乱码 *

3、FileItem 表示文件上传表单中 每个数据部分
boolean isFormField() 判断该数据项是否为文件上传项,true 不是文件上传 false 是文件上传
if(fileItem.isFormField()){
// 不是上传项
java.lang.String getFieldName() 获得普通表单项name属性
java.lang.String getString() / java.lang.String getString(java.lang.String encoding) 获得普通表单项value属性 传入编码集用来解决输入value乱码
}else{
// 是上传项
java.lang.String getName() 获得上传文件名 (注意IE6存在路径)
java.io.InputStream getInputStream() 获得上传文件内容输入流
// 上传文件
void delete() 删除临时文件(删除时,必须要管理输入输出流)
}

注意事项:因为文件上传表单采用编码方式multipart/form-data 与传统url编码不同,所有getParameter 方法不能使用 setCharacterEncoding 无法解决输入项乱码问题

JavaScript的多文件上传表单
? 技巧:
? 每次动态增加一个文件上传输入框,都把它和删除按纽放置在一个单独的div中,并对删除按纽的onclick事件进行响应,使之删除删除按纽所在的div。
? 如:
this.parentNode.parentNode.removeChild(this.parentNode);

上传文件存在的问题
? 上传文件后,在服务器端保存位置
第一类存放位置:直接存放WebRoot目录下 和 除WEB-INF META-INF的其它子目录下 例如: WebRoot/upload
* 客户端可以直接在浏览器上通过url访问位置(资料无需通过权限控制,而可以直接访问) —- 对上传资源安全性要求不高、或者资源需要用户直接可见
* 例如:购物商城商品图片

第二类存放位置:放入WEB-INF及其子目录 或者 不受tomcat服务器管理目录 例如: WebRoot/WEB-INF/upload 、c:\ 、d:\abc
* 客户端无法通过URL直接访问,必须由服务器内部程序才能读取 (安全性较高,可以很容易添加权限控制)
* 例如:会员制在线视频

? 上传文件在同一个目录重名问题
如果文件重名,后上传文件就会覆盖先上传文件

文件名 UUID
filename = UUID.randomUUID().toString() + “_” + filename;

? 为了防止同一个目录下方上传文件数量过多 —- 必须采用目录分离算法
1) 按照上传时间进行目录分离 (周、月 )
2) 按照上传用户进行目录分离 —– 为每个用户建立单独目录
3) 按照固定数量进行目录分离 —— 假设每个目录只能存放3000个文件 ,每当一个目录存满3000个文件后,创建一个新的目录
4) 按照唯一文件名的hashcode 进行目录分离
public static String generateRandomDir(String uuidFileName) {
// 获得唯一文件名的hashcode
int hashcode = uuidFileName.hashCode();
// 获得一级目录
int d1 = hashcode & 0xf;
// 获得二级目录
int d2 = (hashcode >>> 4) & 0xf;

    return "/" + d2 + "/" + d1;// 共有256目录l
}

? 乱码问题
普通编写项 value属性乱码 ————- fileItem.getString(编码集);
上传文件项 文件名乱码 ——— fileupload.setHeaderEncoding(编码集);

下载
常见文件下载有两种方式
1、超链接直接指向下载资源
如果文件格式浏览器识别,将直接打开文件,显示在浏览器上, 如果文件格式浏览器不识别,将弹出下载窗口
对于浏览器识别格式的文件,通过另存为进行下载

客户端访问服务器静态资源文件时,静态资源文件是通过 缺省Servlet返回的,在tomcat配置文件conf/web.xml 找到 — org.apache.catalina.servlets.DefaultServlet

2、编写服务器程序,读取服务器端文件,完成下载
必须设置两个头信息 ,来自MIME协议 Content-Type Content-Disposition

response.setContentType(getServletContext().getMimeType(filename));
response.setHeader(“Content-Disposition”, “attachment;filename=” + filename); // 以附件形式打开,不管格式浏览器是否识别
处理IE浏览器与Firefox浏览器乱码问题
if (agent.contains(“MSIE”)) {
// IE浏览器
filename = URLEncoder.encode(filename, “utf-8”);
filename = filename.replace(“+”, ” “);
} else if (agent.contains(“Firefox”)) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = “=?utf-8?B?”
+ base64Encoder.encode(filename.getBytes(“utf-8”))
+ “?=”;
} else if (agent.contains(“Chrome”)) {
// google浏览器
filename = URLEncoder.encode(filename, “utf-8”);
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, “utf-8”);
国际化
问题:什么是国际化,国际化作用?
软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对不同地区和国家的访问,
提供相应的、符合来访者阅读习惯的页面或数据。
国际化又称为 i18n:internationalization

    对于程序中固定使用的文本元素,例如菜单栏、导航条等中使用的文本元素、或错误提示信息,状态信息等,
    需要根据来访者的地区和国家,选择不同语言的文本为之服务。
    对于程序动态产生的数据,例如(日期,货币等),软件应能根据当前所在的国家或地区的文化习惯进行显示。

    可以提供更友好的访问习惯。

问题:怎样实现国际化?
    针对于不同的国家与地区要显示的信息,都配置到配置文件中,
    根据当前访问者的国家或语言来从不同的配置文件中获取信息,
    展示在页面上。

问题:关于配置文件?
    所谓的配置文件就是一组properties文件,它们叫做资源包。

    相关的概念:
        对于软件中的菜单栏、导航条、错误提示信息,状态信息等这些固定不变的文本信息,可以把它们写在一个properties文件中,
        并根据不同的国家编写不同的properties文件。这一组properties文件称之为一个资源包。

        ResourceBundler,它是用于从资源包中获取数据的。

        关于资源文件(properties)命名:
            基名_语言_国家.properties

            message_zh_CN.properties
            message_en_US.properteis
----------------------------------------------------------
编码演示  properties文件操作以及通过ResourceBundler来获取资源包中信息.

    1.资源包文件一般都放置在classpath下(对于myeclipse就是src下)

    2.关于ResourceBundle使用
        创建:
            ResourceBundle bundle = ResourceBundle.getBundle("message");        
            ResourceBundle bundle = ResourceBundle.getBundle("message",Locale.US);
        获取:
            bundle.getString(String name);

        扩展:关于properties文件中中文问题处理?
            在jdk中有一个命令native2ascii.exe。

            1.进行一次翻译
                native2ascii 回车
                中文  回车

            2.批量翻译
                native2ascii  源文件路径   目录文件路径
                例如: native2ascii d:/a.txt  d:/a.properties

-------------------------------------------------------------------
国际化的登录页面
    1.创建登录页面
    2.创建配置文件
    3.在登录页面上根据不同的国家获取ResourceBundle
    4.在页面上需要国际化的位置,通过ResourceBundle.getString()来获取信息.

    问题:在页面上使用了jsp脚本.解决方案:使用标签 。
        在jstl标签库中提供了国际化标签.


------------------------------------------------------------------------
关于日期国际化
    DateFormat类.
    作用:
        1.可以将一个Date对象格式化成指定效果的String     format方法
        2.可以将一个String解析成Date对象     parse方法

    1.DateFormat对象创建
        DateFormat df1 = DateFormat.getDateInstance(); // 只有年月日
        DateFormat df2 = DateFormat.getTimeInstance(); // 只有小时分钟秒
        DateFormat df3 = DateFormat.getDateTimeInstance();// 两个都有

        DateFormat df1 = DateFormat.getDateInstance(DateFormat.FULL); // 只有年月日
        DateFormat df2 = DateFormat.getTimeInstance(DateFormat.MEDIUM); // 只有小时分钟秒
        DateFormat df3 = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.SHORT);// 两个都有

        DateFormat df1 = DateFormat.getDateInstance(DateFormat.FULL,Locale.US); // 只有年月日
        DateFormat df2 = DateFormat.getTimeInstance(DateFormat.MEDIUM,Locale.US); // 只有小时分钟秒
        DateFormat df3 = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.SHORT,Locale.US);// 两个都有

关于货币国际化
    NumberFormat类
        1.对数值进行格式化
            NumberFormat nf = NumberFormat.getIntegerInstance();
        2.对数值进行百分比
            NumberFormat nf = NumberFormat.getPercentInstance(Locale.FRANCE);
        3.对数值进行以货币显示
            NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
MessageFormat
    动态文件格式化.

    MessageForamt可以对一个模板中的信息进行动态赋值.

    1.MessageFormat使用
        MessageForamt.format(String pattern,Object... params);

    2.说明一下关于动态文本中的占位符?
        例如:{0} is required 

        1.注意占位符只能使用{0}---{9}之间的数值.
        2.关于占们符的格式
            {argumentIndex}: 0-9 之间的数字,表示要格式化对象数据在参数数组中的索引号
            {argumentIndex,formatType}: 参数的格式化类型
            {argumentIndex,formatType,FormatStyle}: 格式化的样式,它的值必须是与格式化类型相匹配的合法模式、或表示合法模式的字符串。

            formatType可以取的值有:number date time
            formatStyle可以取的值有
                number类型可以取:integer currency  percent 
                date类型可以取的:short medium  full long
                time类型可以取的:short medium  full long

1.注解
问题:什么是注解,它有什么作用?
@xxx就是一个注解。
注释:它是用于描述当前代码功能,是给程序员使用的。
注解:它是描述程序如果运行,是给编译器,解释器,jvm使用。

jdk中自带三个注解:
    1.@Override
        是给编译器使用,用于描述当前的方法是一个重写的方法。

        注意:在jdk1.5与jdk1.6中有区别
            jdk1.5中@Override它只能描述继承中的重写.
            jdk1.6中@Override它不仅能描述继承中的重写,还可以描述实现中的重写.

    2.@Deprecated
        它是用于描述方法过时。

        问题:方法什么时候过时?
            1.有新的版本的方法替换旧版本方法。
            2.在旧的版本中存在安全隐患的方法
    3.@SuppressWarnings
        去除程序中的警告信息      
        unused 变量未使用
        deprecation 使用了不赞成使用的类或方法时的警告
        unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。
        fallthrough 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。
        path 在类路径、源文件路径等中有不存在的路径时的警告。?
        serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告。?
        finally 任何 finally 子句不能正常完成时的警告。
        all 关于以上所有情况的警告。

-------------------------------------------------------
关于定义注解

    1.定义注解
        @interface 名称  就定义了一个注解,要想使用  在类,方法,属性上直接  @名称.

        问题:@interface 名称,是声明了一个注解,它的本质是什么?

        @interface MyAnnotation{}
        它的本质就是

        import java.lang.annotation.Annotation;

        interface MyAnnotation extends Annotation
        {
        }
        注解的本质就是一个接口,它继承了Annotation接口。
        所的的注解都实现了这个接口,但是,不能手动实现。

        注解是jdk1.5的新特性.
    ------------------------------------------------------  
    2.注解中的成员

        接口中的成员:
            属性:public static final
            方法: public abstract

        注解成员:
            1.可以有属性
                注解中可以有属性,但是基本不使用。
            2.可以有方法

            在开如,一般使用注解时,只研究它的方法,我们一般管它叫做注解中的属性.

        1.关于注解中的属性的类型问题.
            它的类型只能是以下几种:
                1.基本类型
                    整型:byte short int long
                    浮点:float double
                    字符:char
                    逻辑:boolean
                2.String
                3.Class
                4.enum
                5.Annotation
                6.以上类型的一维数组。

        2.关于注解中有属性,使用的问题
            如果一个注解中有属性,并且属性没有默认值,那么我们在使用注解时,必须给注解的属性赋值.

            关于属性赋值方式:

                1.默认值问题
                    String st() default "abc";

                2.如果是单值

                    注解(属性名称=值)
                    例如:@MyAnnotation3(i=1)
                3.如果是数组
                    1.如果只赋一个值
                        注解(属性名称=值)
                        例如:@MyAnnotation3(i=1)
                    2.如果要赋多个值
                        注解(属性名称={值1,值2,...})
                        例如:@MyAnnotation3(i={1,2,3})

                4.关于属性名称value问题
                    可以省略属性名称
                        例如 @MyAnnotation3("hello");

                    如果value属性是一个数组:
                        @MyAnnotation3({"a","b"})

                    如果注解中有value属性,还有其它属性:
                        那么value属性名称不能在省略.
    ------------------------------------------------------------------                  
        3.元注解
            修饰注解的注解
            1.@Retention
                作用:是指定注解给谁使用.

                它的属性值只能是以下三个
                    RetentionPolicy.SOURCE  给编译器使用  使用后抛弃
                    RetentionPolicy.CLASS   给解析器使用。当jvm加载完成后,就抛弃.                       
                    RetentionPolicy.RUNTIME jvm加载完成后,还存在。开发人员可以通过反射来获取注解相关信息.                   

            2.@Target
                作用:就是定义注解在什么位置使用
            3.@Documented
                作用:是通过javadoc生成的文档中是否抽取注解描述.
            4.@Inherited
                作用:是描述当前注解是否具有继承性

            想要开发,有功能的注解,对于程序员,一定会使用的元注解是:
                @Retention
                @Target
    ------------------------------------------------------------------------
    注解案例---银行最大转账金额:
        这个案例的目的:
            1.怎样通过反射来操作注解
            2.注解可以替换配置文件。

        代码实现:
            1.将银行最大转账金额,定义在配置文件中,使用时,直接从配置文件中读取.

            2.使用注解来替换配置文件。
                1.定义一个注解
                    @Retention(RetentionPolicy.RUNTIME)
                    @Target(ElementType.METHOD)
                    public @interface BankInfo {

                        int maxMoney();
                    }

                2.通过反射来获取注解信息                       
                    1.获取当前方法的Method对象。
                        1.得到Class对象
                            1.类名.class
                            2.对象.getClass()
                            3.Class.forName(String className);
                        2.得到Method对象
                            Class.getDeclaredMethod(String methodName,Class...paramClass);
                    2.在Method类中有一个 getAnnotation(Class annotationClass),可以获取一个注解对象.

                    3.通过注解对象来调用其属性.


            -----------------------------------------------
            注解可以替换配置文件,替换的是什么?
                配置文件的出现,它的主要目的就是解耦合。但是随着现在开发程序越来越庞大,配置文件的缺点
                就出现了,配置文件内容越来越庞大,就不利于我们开发与阅读.                   
                这时就出现了注解,因为注解可以直接写在代码上,并且,通过注解也可以解耦合。

----------------------------------------------------------------------------------------------  
注解示例2--jdbc连接

=====================================================================================================================
2.servlet3.0特性(了解)

在servlet3.0中可以使用注解来替代我们配置文件.
简单说:在servlet3.0中可以没有web.xml文件。

servlet3.0
servlet2.5
问题:怎样知道我们当前使用的是哪个版本?
    在web.xml文件中有一个属性version=""它就可以标识当前是哪个版本.

版本对应关系

servlet2.5    javaee5.0  tomcat 5.x tomcat6   jdk1.5
servlet3.0    javaee6.0  tomcat7.0            jdk1.6
------------------------------------------------------
关于servlet3.0特性:
    1.使用注解来替换配置文件
        @WebServlet("/hello") 用于配置servlet
        @WebFilter("/*")      用于配置Filter
        @WebListener          用于配置Listener

        关于这些注解细节:
            以@WebServlet("/hello") 为例

            注意:属性urlpatterns与values它们都是描述访问当前servlet的路径,但它们不能一起出现,只能使用一个.

            <servlet>
                <servlet-name></servlet-name>   String name() default "";
                <servllet-class></servlet-class>
                <init-param>       WebInitParam[] initParams() default {};
                    <param-name>
                    <param-value>
                </init-param>
                <load-on-startup>     int loadOnStartup() default -1;
            </servlet>

            <servlet-mapping>
                <servlet-name></servlet-name>
                <url-pattern></url-pattern>     String[] urlPatterns() default {};   String[] value() default {};
            </servlet-mapping>


            在servlet中怎样获取初始化参数
                ServletConfig对象获取

            在web.xml文件中的属性 metadata-complete,可以取值为true,false,
            如果为false,代表servlet3.0中的注解可以使用,如果为true,代表不可以使用注解。



    2.servlet3.0中的文件上传

        浏览器端:
            1.method=post
            2.encType="multipart/form-data"
            3.使用<input type="file" name="f">

        服务器端:
            servlet3.0完成。

            1.要在servlet上添加注解@MultipartConfig  
                表示Servlet接收multipart/form-data 请求
            2.在servlet中要想得到上传信息,通过request对象获取一个Part对象。
                Part part=request.getPart();

                part.write(String filename);

                问题:
                     1.关于上传文件中文名称乱码问题
                        因为上传是post请求,直接使用post乱码解决方案就可以  request.setCharacterEncoding("utf-8");
                     2.关于获取上传文件名称 
                        通过Part获取一个header
                            String cd = part.getHeader("Content-Disposition");
                        在这个header中包含了上传文件名称,直接截取出来就可以。                          
                            String filename = cd.substring(cd.lastIndexOf("\\") + 1,cd.length() - 1);
                     3.如果多文件上传怎样处理?
                            request.getParts();

    3.servlet3.0中异步处理
        本质就是在服务器端开启一个线程,来完成其它的操作。

        1.必须在注解添加一项
            @WebServlet(value = "/reg", asyncSupported = true)
            asyncSupported=true,代表当前servlet支持异步操作.

        2.需要一个异步 上下文对象,通过这个对象,可以获取request,response对象.

            AsyncContext context = req.startAsync();    

            还可以对异步上下文进行监听,在它的监听器方法中有一个onComplete,可以用于判断结束。

=============================================================================================================
3.动态代理

1.代理模式
    代理模式作用:
        屏蔽真实行为的访问,让程序更加安全。
        可以对真实行为的调用进行控制。

    通过一个案例:来说明代理的实现以及代理的作用

        代理模式实现:
            1.代理类与被代理类要实现同一个接口.
            2.在代理类中持有被代理对象.
            3.在代理类中调用被代理的行为。

    AOP:面向方面的编程。
        AOP的底层实现就是通过动态代理来做到的。

2.动态代理
    它就是在代理模式基础上发展的,它不在是对单一的类型进行代理,
    而是可以对任意的一个实现了接口的类的对象做代理。


3.动态代理实现
    有两种方式:
        1.通过jdk中提供的Proxy类来实现
            这种方式要求,被代理类必须实现接口。
            简单说,只能为接口做代理.
        2.通过cglib来实现。
            它不要求,实现接口。

        我们讲第一个.

        代码实现:

            Proxy类中有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);

            参数:
                loader:
                    要求,传递的是被代理类的类加载器ClassLoader.

                    类加载器怎样获取:
                        得到其Class对象。在Class类中提供一个方法  getClassLoader();

                interfaces:
                    要求:得到被代理对象所实现的接口的所有Class对象。
                    怎样获取所有实现接口的Class对象?
                        得到其Class对象,在Class类中提供一个方法  getInterfaces();
                        它返回的是Class[],就代表所实现接口的所有Class对象。

                h:
                    它的类型是InvocationHandler,这是一个接口。
                    InvocationHandler 是代理实例的调用处理程序 实现的接口。


                InvocationHandler接口中有一个方法invoke;
                    // 参数 proxy就是代理对象
                    // 参数method就是调用方法
                    // 参数args就是调用的方法的参数
                    // 返回值,就是真实行为执行后返回的结果,会传递给代理对象调用的方法.
                    public Object invoke(Object proxy, Method method, Object[] args);

.类加载器
问题:什么是类加载器,有什么作用?
类加载器的作用就是将java中的字节码文件(.class文件)转换成Class对象。

当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:
    1.引导类加载器 BootStrap    jre/lib/rt.jar
    2.扩展类加载器  ExtClassLoader   JRE/lib/ext/*.jar
    3.应用类加载器(系统类加载器) AppClassLoader SystemClassLoader   CLASSPATH指定的所有jar或目录

    在java中ClassLoader代表类加载器,所有的类加载器都是ClassLoader的子.

演示类加载器:
    问题:类加载器如果获取?
        在Class类中有一个方法 getClassLoader()它返回的就是一个类加载器.

    1.获取引导类加载器
        ClassLoader cl = String.class.getClassLoader();     
        System.out.println(cl);
        结果是null.
        原因:引导类加载器特殊,它根本就不是java实现。所有在得到引导类回载器是结果就是null.

    2.扩展类加载器
        ClassLoader cl = AccessBridge.class.getClassLoader();       
        System.out.println(cl); //sun.misc.Launcher$ExtClassLoader@9cb0f4

    3.应用类加载器
        ClassLoader cl = this.getClass().getClassLoader();      
        System.out.println(cl); //sun.misc.Launcher$AppClassLoader@164dbd5

----------------------------------------
全盘负责委托机制 
    全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。
    委托机制:先让parent(父)类加载器 寻找,只有在parent找不到的时候才从自己的类路径中去寻找。
    类加载还采用了cache机制:如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。 

-------------------------------------------
自定义类加载器

    创建了一个类  javax.activation.MimeType,在这个类中有一个方法show();
    当jvm加载这个类时,因为在rt.jar包下也存在一个MimeType类,并且包名都一样,
    这时jvm就会使用引导类加载器加载这个类,而我们想得到的其实是应该由应用类加载
    器加载的Class.

    解决方案:
        自定义类加载器.
        1.创建一个类,去继承自ClassLoader
        2.重写findClass方法,在这个方法中通过

defineClass将一个.class文件转换成Class对象.

泛型反射

问题:在BaseDaoImpl类中需要得到当前这个类上的泛型的Class对象,而直接通过T.class这是不对的.
    public class BaseDaoImpl<T> implements BaseDao<T> {

    public T findById(int id) {
        // Session session=HibernateUtils.getSession();

        // session.get(T.class,id);


        return null;
    }
    怎样得到当前这个类上的泛型的Class?

    Type type = this.getClass().getGenericSuperclass(); // 得到当前类上的泛型--父类型

    Type[] params = ((ParameterizedType) type).getActualTypeArguments(); // 得到当前类上所有的泛型类型Class

    clazz = (Class) params[0];                      

你可能感兴趣的:(jsp,servlet,jdbc)