05_JSP/Servlet技术

程序架构:C/S架构(客户端Client/服务器Server)>B/S架构(浏览器Browser/服务器)

B/S架构:请求/响应交互(浏览器<>服务器-应用服务器<>数据库服务器)

统一资源定位符URL(协议/主机/资源位置/参数)-资源描述符URI(Http请求)

    http://localhost:8080(主机端口)/myweb(网站)/test.html(资源)

Web服务器:WebLogic(JavaEE规范)-WebSphere(JavaEE规范)-JBoss(JavaEE规范)-Tomcat(jsp/servlet规范,不支持EJB)

    javaSE规范:支持 IO流,集合,网络编程,线程技术

    javaEE规范:支持13种技术 servlet、jsp、ejb、jdbc

Tomcat安装:window(exe/msi)-linux(rmp)

系统变量配置:JAVA_HOME(jdk路径)->CLASSPATH->Path(jdk\bin目录)

Linux部署Tomcat:

  1. 将Tomcat压缩包解压至/usr/tomcat目录下:unzip tomcat_8.0.zip
  2. 执行startup.sh文件启动tomcat:./startup.sh

root用户执行startup.sh时Permission Denied--chmod +x追加可执行权限

  1. 测试Tomcat:http://localhost:8080--对外开放端口:/sbin/iptables -l INPUT -p tcp --dport 8091-j ACCEPT

Tomcat服务器目录结构(Apache):

/bin:操作指令,startup.bat(window)/sh(Linux) shutdown.bat/sh

/conf:配置文件,server.xml(默认端口8080) context.xml

/lib:存放jar包

/log:日志信息

/tmp:临时文件目录

/webapps:存放发布的web应用(网站)文件,/ROOT默认网站(访问不需要应用名称)

/work:运行目录,放置JSP生成的Servlet

Tomcat启动过程:bin/startup.bat>JAVA_HOME>CLATALINA_HOME环境变量(Tomcat根目录)>/webapps

Tomcat服务器优化(内存|并发连接数|缓存):

a)内存优化:主要是对Tomcat启动参数进行优化,可以在Tomcat启动脚本中修改它的最大内存数等

b) 线程数优化:Tomcat的并发连接参数,主要在Tomcat配置文件中server.xml中配置,比如修改最小空闲连接线程数,用于提高系统处理性能等

c) 优化缓存:打开压缩功能,修改参数,比如压缩的输出内容大小默认为2KB,可以适当的修改

创建Web项目:MyEclipse>new Web Project>ProjectName+JavaEE Version

Eclipse部署Tomcat(手动部署/自动部署):windows>Preferences>Tomcat(关联jdk)

>Deploy Project(部署项目)>Servers(Start/Stop/Config Tomcat)

Web项目调试:设置断点>Tomcat(debug Server)>执行到断点:F5(跳入)/F6/F7(跳回)/F8(跳到下一个断点)

Web开发目录结构:src(servlet)-WebRoot(META-INF、WEB-INF、web.xml、JSP)

Web应用(网站)目录结构:

|- WebRoot:根目录,一个web应用必须有一个根目录

|- 静态资源:html+css+javascript+images+xml

|-WEB-INF:里面的文件不能通过浏览器直接访问,需要在web.xml中配置

|-classes:存放class字节码

|-lib:存放jar包,不能有子目录

|-web.xml: web应用的配置文件

Web应用程序部署:

方式一:Web应用(生成jar包)拷贝至/Webapps目录

<!—web.xml文件中配置默认ROOT首页-->

<welcome-file-list>

<welcome-file>index.html</welcome-file>

</welcome-file-list>

方式二:配置虚拟网站(Tomcat与网站分离)

<!- 修改%tomcat%/conf/server.xml ->

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" >

 <!--配置虚拟网站

    docBase: web应用所在的绝对路径位置

    path: 自定义访问web应用使用的名称

path为空,则不需要名称访问该web应用,而且优先于ROOT应用

-->

    <Context docBase="C:\projects\myweb" path="/访问网站名称"/>

</Host>

方式三:添加配置文件方法配置虚拟网站

<!- %tomcat%/conf/Catalina/localhost目录下添加abc.xml文件 ->

<?xml version="1.0" encoding="utf-8"?>

<!-- 配置虚拟网站:访问web应用名称是xml文件名abc -->

<Context docBase="C:\projects\myweb"/>

站点host(1)>网站(多):静态资源(html+css+javascript)-动态资源(servlet+jsp)

Http协议:请求request-http协议(tcp/ip基础上封装的协议)-响应response

05_JSP/Servlet技术

       请求(请求行|请求头|空行|实体内容)-响应(响应行|响应头|空行|实体内容)

Http请求:浏览器->服务器(请求)--请求一次资源文件,请求一次

GET(提交方式) /project/hello(URI资源描述符)?(GET请求参数) HTTP/1.1 --请求行

--Http版本:HTTP1.0(一次请求)-HTTP1.0(多次请求)

--Http请求方式:GET/POST/HEAD/PUT/CONNECT/TRACE/DELETE

    --最常用请求方式:<form action=”提交地址” method=”get/post”>

    --GET提交参数显示在地址栏 POST在请求实体内容中

--请求头(多个、以键值对形式出现)

Host: localhost:8080   --请求发出的主机和端口

User-Agent: Mozilla/5.0 (Windows NT 6.1;) Firefox/34.0 --浏览器类型

Accept: text/html,application/xhtml+xml,application/xml; --浏览器接受的数据类型

Accept-Charset: ISO-8859-1     --浏览器接受数据编码格式

Accept-Language: zh-cn,en-us;q=0.8,zh;q=0.5,en;q=0.3 --浏览器接受的语言

Accept-Encoding: gzip, deflate  --浏览器接受的数据压缩格式

Referer: http://localhost/index.jsp  --当前请求来源(防止非法链接)

Cookie: name=admin --浏览器保存的cookie数据

If-Modified-Since: Tue, 12 Jul 2016 12:12:16 GMT --浏览器缓存最后修改时间

Connection: keep-alive(保持连接)/close --浏览器和服务器连接状态

Data: Sun, 12 Dec 2016 12:12:12 GMT  --请求发出时间

                                   -- 一个空行

name=admin&password=123(POST参数)      --请求实体内容

Http响应:服务器->浏览器(响应)

HTTP/1.1 200(状态码) OK             --响应行

      --http协议常见的状态码(服务器对于请求处理的结果):

200:表示请求处理完成

302:表示请求需要进一步细化,通常该状态码和location响应头结合使用

404:表示客户端错误,找不到资源

500:表示服务器错误(代码错误)

--响应头(多个)

Location: http://localhost/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://localhost        ---定时刷新或每隔n秒跳转资源

Content-Disposition: attachment; filename=aaa.zip   -- 以下载方式打开资源

Transfer-Encoding: chunked

Set-Cookie:SS=Q0=5Lb_nQ; path=/search    -- 服务器发送给浏览器的cookie数据

Expires: -1              --通知浏览器不使用缓存

Cache-Control: no-cache

Pragma: no-cache

Connection: close/Keep-Alive    --连接状态

Date: Sun, 12 Dec 2016 12:12:16 GMT  --响应发出的时间

                                --一个空行

HelloServlet!Sun Dec 12 12:12:16 CST 2017 --实体内容(用户直接看到的内容)

Servlet

客户端http请求>Web服务器转发>servlet容器>解析urll并根据web.xml找到相对应的servlet,传递request、response对象>处理完业务逻辑后,将信息放入到response并响应到客户端

servlet(实现Servlet接口,继承HttpServlet,交给Tomcat服务器运行)

    Servlet接口>GenericServlet(通用,不基于任何协议)>HttpServlet(基于http)

Servlet开发步骤:编写HelloServlet(继承HttpServlet的java类,重写doget/doPost方法)>>Tomcat运行(web.xml配置)>>url访问

//修改Servlet模板:MyEclipse\Common\plugins\com.genuitec.eclipse.wizard.jar>压缩包方式打开>修改Servlet.java

public class HelloServlet extends HttpServlet {

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

        this.doPost(request, response);

    }

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

//处理代码

}

web.xml:

<!-- 浏览器访问:http://localhost:8080/webproject/Hello -->

<!-- Servlet配置信息 -->

<servlet>

   <description></description>

   <!-- Servlet内部名称 -->

   <display-name>HelloServlet</display-name>

   <servlet-name>HelloServlet</servlet-name>

   <!-- Servlet类的全名:包名+类名 -->

   <servlet-class>com.hfxt.servlet.HelloServlet</servlet-class>

</servlet>

<!-- Servlet映射信息 -->

<servlet-mapping>

<!--Servlet内部名称:与Servlet配置的内容名称保持一致 -->

   <servlet-name>HelloServlet</servlet-name>

<!-- servlet映射路径:外部访问Servlet的路径(名称) -->

<url-pattern>/Hello</url-pattern>

</servlet-mapping>

Servlet编程:

servlet执行过程:Tomcat启动加载web.xml>>浏览器输入url>>截取字符串,得到访问资源/hello>>匹配servlet映射信息url-pattern>>servlet配置信息servlet-calss(字符串com.hfxt.servlet.HelloServlet)>>创建HelloServlet对象,调用方法执行

servlet路径映射:精确匹配(优先):/hello、模糊匹配:/*、/hello/*、*.后缀名

<url-pattern>/Hello</url-pattern>

<url-pattern>/*</url-pattern>

<url-pattern>*.html</url-pattern>

缺省路径(解析web应用下的静态资源):先找动态资源>再找静态资源

<servlet-name>default</servlet-name>

<url-pattern>/</url-pattern>

servlet生命周期:构造方法>初始化>服务>销毁

构造方法:实例化,创建Servlet对象(单实例|多线程)

初始化init():初始化Servlet对象>>HttpServletRequest/Response对象

有参init(ServletConfig)(默认调用)-无参init(GenericServlet,重写实现初始化逻辑)

服务server():>>doGet/doPost()

    销毁destory():重新部署网站或停止服务器调用

tomcat服务器执行的生命周期伪代码:

//字符串:com.hfxt.servlet.HelloServlet

  1. 通过反射,创建HelloServlet对象 --调用1次

    //得到HelloServlet的class(字节码)对象

    Class clz = Class.forName(“com.hfxt.servlet.HelloServlet”);

    //通过class对象调用(无参)构造方法

    Object obj = clz.newInstance;

  1. 通过反射,调用init()方法 --调用1次

//得到init方法对象

Method m = clz.getDeclaraeMethod(“init”,ServletConfig.class);

//调用init方法

m.invoke(obj,config); //

  1. 通过反射,调用server()方法 --调用n次

//得到server方法对象

Method m = clz.getDeclaraeMethod(“service”,HttpServletRequest.class, HttpServletResponse.class);

//调用server方法

m.invoke(obj,request,response);

  1. 通过反射,调用destrory()方法 --调用1次

//得到destrory方法对象

Method m = clz.getDeclaraeMethod(“destrory”,null);

//调用destrory方法

m.invoke(obj,null);

05_JSP/Servlet技术

Servlet线程并发(servlet对象在tomcat中是单实例多线程,多个线程同时操作Servlet的成员变量):对使用到成员变量的代码块加同步锁(同步代码块/同步方法)

static int count = 1; //成员变量,可能被不同用户线程共享到,引发多线程并发问题

/**

         * 1)同步代码块

         * 给使用到共享数据的代码块添加同步锁

         * 注意:同步锁必须多个线程唯一的

         */

        synchronized (ThreadDemo.class) {

            response.getWriter().write("你当前是第"+count+"个访客!"); //A线程刚刚执行完这句代码,被B线程抢去了执行时机

            //睡眠

            /*try {

                Thread.sleep(10000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }*/

            count++; 

        }

        //method(response);

    }

    /**

     * 2 )同步方法

     * @param response

     */

    /*public synchronized static void method(HttpServletResponse response){

        try {

            response.getWriter().write("你当前是第"+count+"个访客!");

        } catch (IOException e1) {

            e1.printStackTrace();

        }   // A线程刚刚执行完这句代码,被B线程抢去了执行时机

        //睡眠

        try {

            Thread.sleep(10000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        count++;

    }*/

Servlet自动加载:tomcat服务器启动的时候创建servlet对象

<servlet>

    <servlet-name>HelloServlet</servlet-name>

    <servlet-class> com.hfxt.servlet.HelloServlet</servlet-class>

<!-- tomcat服务器启动的时候自动创建servlet对象 -->

<!-- 正整数:数值越大,创建对象的优先级越低 -->

    <load-on-startup>1</load-on-startup>

 </servlet>

Servlet重要对象:HttpServletRequest-HttpServletRequest-ServletConfig-ServletContext

HttpServletRequest对象:封装请求信息

HttpServletRequest对象:封装响应信息

ServletConfig对象(servlet上下文对象):封装一个servlet初始化参数信息

ServletContext对象:封装web应用环境信息

HttpServletRequest对象:

//1.tomcat服务器接收到浏览器发送的请求数据

//2.tomcat服务器把http请求信息封装成HttpServletRequest对象

//3.tomcat服务器调用doGet()/doPost()方法,把request对象传入servlet

//4.通过HttpServletRequest对象得到http请求信息

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //service方法是servlet核心服务方法(程序入口)

}

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

        this.doPost(request, response);

}

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

//通过request对象获取数据

请求行:

request.getMethod(): 请求方式

request.getRequestURI():请求资源

request.getRequestURL()

request.getProtocol():协议版本

请求头:

request.getHeader("name"): 根据请求头获取请求值

request.getHeaderNames(): 获取所有请求头名称(return Enumeration)

实体内容:

request.getInputStream(): return InputStream

获取参数方式:Get(参数跟在URI的后面)-Post(参数放在实体内容中)

GET:request.getQueryString();

POST:request.getInputStream();

通用获取参数方式:(不分Post和Get)

request.getParameter(name): 根据参数名获取参数值(获取一个参数值)

request.getParameterValues(name): 根据参数名获取参数值(获取多个参数值)

request.getParameterNames(): 获取所有参数名称

request.getParameterMap(): 获取所有参数

//处理代码(案例):

        User-Agent头:getHeader>User-Agent>获取用户浏览器类型

        referer头(超链接请求头):直接访问下载链接>识别非法请求>跳到定义下载页面>开始下载

}

HttpServletResponse对象:

//1.tomcat服务器提供一个HttpServletResponse对象

//2.通过service方法把response对象传入servlet中

//3.通过response对象修改响应数据

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //service方法

}

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

        this.doPost(request, response);

}

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

响应行:response.setStatus(状态码): 设置状态码

响应头:response.setHeader(name,value): 设置响应头

实体内容:

response.getWriter().writer(): 以字符格式发送实体内容

response.getOutputStream().writer(): 以字节格式发送实体内容

}

编码:请求字符(浏览器)>字节(utf-8)>解码(服务器)>字符(默认iso-8859-1)>>中文乱码

     (服务器)字符>设置编码格式(utf-8)>字节>解码(浏览器)>字符(utf-8)

请求参数(浏览器->服务器):request.setCharcterEncoding("utf-8");

解决get乱码问题(手动解码):

if("GET".equals(request.getMethod())){

name = new String(name.getBytes("iso-8859-1"),"utf-8");

}

解决post乱码问题:request/response.setCharacterEncoding("utf-8")

响应数据(服务器->浏览器):response.setContentType("text/html;charset=utf-8");自适应编码

ServletConfig对象:web.xml中Servlet配置信息>ServletConfig对象

    //一个网站中可能会存在多个ServletConfig对象,一个ServletConfig对象就封装了一个servlet的配置信息

String config.getInitParameter(“name”)     æ ¹æ®å‚数名称获取参数值

Enumeration config.getInitParameterNames() 获取所有参数名称

<servlet>

    <servlet-name>ConfigDemo</servlet-name>

    <servlet-class>com.hfxt.servlet.ConfigDemo</servlet-class>

    <!--  配置servlet参数 -->

<init-param>

<param-name>key</param-name>

<param-value>value</param-value>

</init-param>

  </servlet>

ServletContext对象:web.xml信息(包括全局参数)>ServletContext对象

//一个网站只会创建一个ServletContext对象,代表整个网站的环境信息

    this.getServletConfig().getServletContext();  获取ServletContext对象

//ServletContext对象:启动的时候创建

//ServletConfig对象:调用init方法之前创建的,在ServletContext对象创建之前

public ServletCofig{

ServletContext context;

public ServletConfig(context){

this.context=context;

}

public ServetContxt getServletContext(){

return;

}

ServletConfig config = new ServletConfig(context);

public MyServlet extends HttpSevlet{

publlic init(ServletConfig config){

SevletContext context= config. getServletContext();

}

}

ServletContext对象5大作用:读取web上下文路径、读取全局参数、作为域对象使用、转发页面、读取web资源文件

Servlet三个域对象:ServletContext-HttpServletRequest-HttpSession

//1.获取web的上下文路径(项目在Tomcat中运行的路径)

java.lang.String context.getContextPath()

//2.获取全局参数

<!-- 全局参数配置 -->

  <context-param>

      <param-name>AAA</param-name>

      <param-value>AAA'value</param-value>

  </context-param>

java.lang.String getInitParameter(java.lang.String name)

java.util.Enumeration getInitParameterNames()

//3.域对象(保存数据,获取数据):在不同的资源之间来共享数据

    Servlet1>保存数据(setAttribute)->域对象(容器,Map)->获取数据(getAttribute)>Servlet2

void setAttribute(java.lang.String name, java.lang.Object object) 保存数据

java.lang.Object getAttribute(java.lang.String name) 得到数据

void removeAttribute(java.lang.String name) 清除数据

//4.请求转发

RquestDispatcher getRequestDispatcher(String path).forward(request,response)

//5.读取web项目的资源文件

java.lang.String getRealPath(java.lang.String path)

java.io.InputStream getResourceAsStream(java.lang.String path)

java.net.URL getResource(java.lang.String path)

转发和重定向:

转发(服务器行为):在服务端定位新资源,始终是一次请求; 可以实现请求信息的共享; 客户端的URL不变

request.getRequestDispapatcher(“URL”).forword(request,response);

重定向(浏览器行为):服务端让客户端重新请求资源,两次请求; 不能实现请求信息的共享;客户端的URL变为新请求的URL

response.sendRedirect("url");

05_JSP/Servlet技术

请求重定向(浏览器行为):response.sendRedirect(path)

1)地址栏会发送改变,变成重定向到的地址。

2)可以跳转到项目内的资源,也可以跳转项目外的资源

3)浏览器向服务器发出两次请求,那么就不能使用请求来作为域对象来共享数据。

请求转发(服务器行为):request.getRequestDispapatcher(“URL”).forword

(request,response);

1)地址栏不会改变。

2)只能跳转到项目内的资源,不能跳转到项目外的资源

3)浏览器向服务器发出一次请求,那么可以使用请求作为域对象共享数据。

会话管理:浏览器-会话数据(会话管理)-服务器

    会话管理技术:Cookie(会话数据保存在浏览器)-Session(会话数据保存在服务器)

    Cookie特点:

1)会话数据放在浏览器端

2)数据类型只能string,而且有大小限制的

3)相对数据存放不安全

    Session特点:

1)会话数据放在服务器端(服务器内存),占用服务器资源

2)数据类型任意类型,没有大小限制的

3)相对安全

Cookie技术原理:

1)服务器创建Cookie对象,保存会话数据,把Cookie数据发送给浏览器

response.addCookie(cookie);  (响应头:set-cookie:name=admin)

2)浏览器获取cookie数据,保存在浏览器缓存区,然后在下次访问服务器时携带cookie数据

(请求头:cookie:name=admin)

3)服务器获取浏览器发送的cookie数据

request.getCookies();

Cookie对象:

//1.创建Cookie对象,用于存储会话数据

new Cookie(java.lang.String name, java.lang.String value)

//2.修改Cookie对象

void setPath(java.lang.String uri)

void setMaxAge(int expiry)

void setValue(java.lang.String newValue)

//3.把cookie数据发送给浏览器保存(响应头set-cookie)

response.addCookie(cookie);

//4.浏览器带着cookie访问服务器,服务器接收cookie信息(请求头cookie)

request.getCookies();

Session技术原理:

1)服务器创建Session对象,服务器会给这个session对象分配一个唯一的标记JSESSIONID

2)把JSESSIONID作为Cookie发送给浏览器

3)浏览器得到JSESSIONID保存下来,在下次访问时携带这个JSESSIONID去访问服务器

4)服务器得到JSESSIONID,在服务器内存中搜索是否存在指定JSSESSINOID的session对象

5)如果找到,则返回这个session对象

6)如果找不到,可能直接返回null,或者再创建新的session对象。

HttpSession session = request.getSession();

HttpSession对象:

1)创建HttpSession对象,用于保存会话数据

session = request.getSession()/getSession(true); 创建或获取session对象(没有则创建)

          request.getSession(false); 得到session对象,没有直接返回null

2)修改HttpSession对象

void setMaxInactiveInterval(int interval)  设置session对象的有效时间(秒)

void invalidate()   æ‰‹åŠ¨é”€æ¯session对象

    <!-- 默认情况:等待30分钟空闲时间,session对象才会销毁 -->

    <!-- 设置全局的session对象过期时间(分) -->

    <session-config>

        <session-timeout>1</session-timeout>

    </session-config>

//设置JSESSIONID的时间,不会随着浏览器关闭而丢失

Cookie c = new Cookie("JSESSIONID",session.getId());

c.setMaxAge(1*30*24*60*60);//1个月

response.addCookie(c);

3)保存会话数据(作为域对象)

session.setAttribute("name",Object);  保存数据

session.getAttribute("name");    èŽ·å–æ•°æ®

session.removeAttribute("name"); 删除数据

JSP

JSP(Java Server Page):运行在服务器端的Java服务页面(HTML嵌套java代码)

    JSP本质是Servlet,jsp包含servlet技术

    Jsp的最佳实践:servlet技术(写java代码)-jsp技术(输出html代码)

    JSP运行目录:tomcat的work目录, 存放jsp页面运行过程中产生的临时文件

JSP执行过程:翻译阶段>编译阶段>执行阶段

第一次访问jsp:jsp页面(.jsp)>翻译>java源文件(.class)>编译>class文件(.class)>创建类对象>执行类中的方法

    第n次访问jsp(不修改jsp代码): 直接执行类中的方法

JSP生命周期:翻译(java文件)>翻译(class文件)>构造方法>_jspinit方法>_jspServer方法>_jspDestroy方法

05_JSP/Servlet技术

jsp中文编码:jsp字节文件(默认编码GBK)>翻译(解码pageEncoding=utf-8)>java字符文件>编译(utf-8)>class文件>输出(contentType="text/html;charset=UTF-8")>浏览器(自动切换编码格式)

    pageEncoding>jsp文件会自动变成pageEncoding的编码值>contentType

05_JSP/Servlet技术

JSP基本语法:模板|指令|表达式|脚本|声明(变量/方法)|注释

指令:<%@ 指令 %>  page命令-taglib命令-include命令(动态包含/静态包含)

<%-- page命令:告诉tomcat服务器如何翻译jsp文件 --%>

<%@ page language="java"(翻译jsp文件的语言) import="java.util.*"

pageEncoding="UTF-8" --翻译jsp文件的编码

contentType="text/html;charset=UTF-8" --服务器发送给浏览器的数据类型和内容编码格式

session="true"  -- 是否打开session功能:true打开,false关闭

buffer="8kb"  -- jsp页面的缓存区大小

isELIgnored="false" --是否忽略EL表达式:true忽略,false不忽略。

errorPage="/common/error.jsp"  -- 指定jsp错误处理页面

isErrorPage="true"  --指定当前页面为错误处理页面,可以使用内置对象exception查询错误信息

<!-- 配置全局的错误处理页面 -->

<error-page>

<error-code>404</error-code>

<location>/common/404.html</location>

</error-page>

<error-page>

<error-code>500</error-code>

<location>/common/500.jsp</location>

</error-page>

%>

<%-- taglib命令:导入jsp的标签库 --%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<%-- include命令:导入其他页面 --%>

<%@ include file="/common/page.jsp" %>

<jsp:include page="path"/> --动态包含(相当于追加)

<%@ include file="common.jsp" %> --静态包含(相当于复制粘贴)

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">

<title>JSP</title>

</head>

<body>

<% --JSP Start

模板:jsp页面的html代码

注释:<%-- JSP注释 --%><!-- HTML注释 -->

    --html的注释会被翻译和执行,而jsp的注释不会被翻译和执行

表达式:<%= 变量或表达式 %>

    --原理是使用out.print()方法向浏览器输出内容

脚本:<% java语句 %>      局部变量(方法内)<% type name=value %>

    --脚本原封不动地翻译到java文件的_jspServcice方法中执行

声明:<%! 变量或方法 %>   全局变量(成员变量)<%! type name=value %>

--声明的变量作为成员变量,声明的方法作为成员方法

%>  --JSP End

</body>

</html>

JSP内置对象:jsp直接自动创建或获取常用的开发对象

request-response-config-application-session-exception page-out-pageContext

jsp对象       类型

request       HttpServletRequest    请求对象

response      HttpServletResponse   响应对象

config        ServletConfig          配置对象

application   ServletContext      servlet上下文对象(容纳N次会话)

session       HttpSession           会话对象(容纳N次请求)

exception     Throwable             异常信息对象

page(页面对象this)  Object          jsp文件翻译后的javaç±»

out            JspWriter      输出对象

pageContext   PageContext    jsp上下文对象

out: 相当于带缓存功能的PrintWriter(减少写出次数,提高效率)

    out.println("Hello JSP!");

out.write("Hello JSP!"); <%--向浏览器输出 --%>

    JSP缓存机制:缓存满(buffer="8kb")/jsp页面执行完毕/缓存关闭/手动刷新缓存flush()

05_JSP/Servlet技术

    <%

out.write("123");

       System.out.println("当前缓存区大小:"+out.getBufferSize());

       System.out.println("缓存区剩余大小:"+out.getRemaining());

       //手动刷新

       //out.flush();

       response.getWriter().write("abc");--页面输出顺序:abc 123

%>

pageContext(页面上下文对象):通过pageContext对象获取其他8个内置对象(使用自定义标签);作为域对象使用

//保存到page域

pageContet.setAttribute("name",Object);

//保存到其他域:PAGE_SCOPE、REQUEST_SCOPE、SESSION_SCOPE、APPLICATION_SCOPE

pageContext.setAttribute("name,Object,PageContext.PAGE_SCOPE)

//获取page域

pageContext.getAttribute("name")

//从其他域中获取:PAGE_SCOPE、REQUEST_SCOPE、SESSION_SCOPE、APPLICATION_SCOPE

pageContext.getAttribute("name,PageContext.PAGE_SCOPE)

//自动搜索四个域数据

pageContext.findAttribute("name")

jsp域对象(共享数据、保存数据、获取数据):page-request-session-application

    域对象的方法:setAttribute()、getAttribute()、removeAttribute()

域作用范围:page域<request域<session域<application域 --搜索顺序(小>大)

page域:在同一个jsp页面中数据有效

request域:在同一个请求中数据有效

session域:在同一个会话中数据有效

application域:在同一个网站中数据有效

jsp转发和重定向:

<%

//设置请求的编码方式

    request.setCharacterEncoding("UTF-8");

    //设置响应的编码方式

    response.setCharacterEncoding("UTF-8");

    String username = request.getParameter("username");

    if(username.equals("admin")){//登陆成功

        request.setAttribute("mess", "成功");//属性存取数据

        //String str = (String) request.getAttribute("mess");//取值

        response.sendRedirect("index.jsp");//重定向

    }else{

        request.setAttribute("mess", "失败");

        //session.setAttribute("user", username);

request.getRequestDispatcher("login.jsp").forward(request, response);//转发

    }

%>

EL(取值)和JSTL(操作)

EL表达式(Expression Langage):替代jsp表达式,向浏览器输出域对象中的变量或表达式计算的结果

基本语法:${变量/表达式}  --代替<%= 变量/表达式%>

EL操作符:.(常用)、[]

EL运算符:算数(+-*/)-关系(> < >= <= ==)-逻辑(&& || !)-条件运算符-empty(判空表达式)

EL内置对象:pageContext、pageScope、requestScope、sessionScope、applicatinoScope、param、paramValues、header、headerValues、cookie、initParam

EI访问作用域:page/request/session/application

pageScope/requestScope/sessionScope/applicationScope

    取值的数据类型:普通字符串、普通对象、数组或List集合

<%

    String name="admin";

    pageContext.setAttribute("name",name);

    Student stu = new Student("admin","abc");

    request.setAttribute("stu",stu);

    List<String> list=new ArrayList<String>();

    list.add("aaa");

    request.setAttribute("list", list);

%>

字符串:${ name } <!--从四个域自动搜索:等价于<%=request.findAttribute(“name”)%> -->

普通对象:${ stu.name } <!-- 访问变量 .操作符 等价于调用对象的getXXX方法 -->

${requestScope["student.name"]} <!-- 访问对象属性 []操作符 -->

${requestScope.list[1] } <!-- 访问集合 -->

<!-- EL运算符 -->

${a>b?1:2}

${not empty username} <!-- 返回true -->

JSP标签:替代JSP脚本,完成条件判断,赋值,迭代等功能

动作标签(jsp内置标签)-JSTL标签(标准标签)-自定义标签

动作标签:<jsp:forward/>转发标签、<jsp:param/>参数标签、<jsp:include/>包含标签

    包含标签原理(动态包含):包含与被包含的页面单独翻译成不同的java文件,运行时合并

静态包含:先合并再翻译,不能携带参数 <%@include%>

动态包含:先翻译再合并,携带参数  <jsp:include />

<jsp:forward page="path"/> --跳转页面

<jsp:param name="paramName" value="paramValue" /> --传参

<jsp:include page="/common/header.jsp" /> --动态包含

<jsp:userBean id=”” class=”” scope=”page” /> -- new对象

<jsp:setProperty> --设置指定对象的指定属性

<jsp:getProperty> --取出指定对象的指定属性、

JSTL标签(java standard tag libarary):java标准标签库

标签库分类:核心标签库(c)-国际化标签库(fmt)-EL函数库(fn)-SQL标签库(sql)-XML标签库(x)

<!-- 导入jar包:jstl.jar、standard.jar -->

<!-- 引用库 -->

<!-- uri: 表示需要导入的标签库的uri名称。每个标签库都会有一个tld后缀名的标签声明文件,在tld文件中都有唯一的uri的名称

     prefix: 使用标签库的前缀,通用和tld文件的short-name名称相同 -->

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>

<!-- 核心标签库c -->

保存数据:<c:set></c:set>

获取数据:<c:out value=""></c:out>

单条件判断:<c:if test=""></c:if>

多条件判断:<c:choose></c:choose>、<c:when test=""/>、<c:otherwise />

迭代(循环):<c:forEach />、<c:forTokens items="" delims=""></c:forTokens>

重定向:<c:redirect></c:redirect>

<!-- var:数据的名称  value:保存的数据值  scope:保存到域 -->

    <!-- request作用域存值 =>request.setAttribute("username","admin"); -->

<c:set var="username" value="admin" scope="request"></c:set>

<!-- 获取域对象数据

value: 代表获取域中的某个名称内容。如果数据在域中,必须使用EL语法去获取

default: 默认值。当前需要获取的内容为null,用默认值代替

escapeXml: 默认情况下为true,out标签会把输入的内容进行转义。如果不转义则为false

-->

<c:out var="${name}" default="默认值"></c:out>

<%

    int i = 1;

    pageContext.setAttribute("i", 1);

%>

<c:if test="${i == 1 }">i的值是1</c:if>

<c:choose>

    <c:when test="${i==1 }">

        i值是1

    </c:when>

    <c:otherwise>

        i值未知

    </c:otherwise>

</c:choose>

<!-- 迭代 -->

<!-- begin: 起始位置,从0开始

     end: 结束位置

     step: 增加步长,默认step为 1

     items: 遍历对象(数组|List集合|Map集合),如果是获取域数据,那么使用EL表达式获取

     var: 每个元素名称

     varStatus: 当前状态对象,该对象封装当前元素状态信息

-->

<!-- List集合-->

<%

    User u1 = new User(1,"aaa");

    User u2 = new User(2,"bbb");

    List<User> list = new ArrayList<User>();

    list.add(u1);

    list.add(u2);

    request.setAttribute("list", list);

 %>

<c:forEach var=“u”  items="${requestScope.list }">

    ${u.id } &nbsp; ${u.name }

</c:forEach>

 <!-- Map集合-->

 <!-- forEach标签遍历Map集合时,把每个Map的对象使用Entry封装,Entry封装键对象和值对象

通过getKey()获取键对象,通过getValue()获取值对象 -->

<c:forEach items="${map}" var="entry">

${entry.key } - ${entry.value }

</c:forEach>

<!-- 国际化/格式化标签库fmt -->

<!-- 格式化日期 -->

<fmt:formatDate pattern="yyyy-MM-dd HH:mm:ss" value="<%=new Date() %>" />

<!-- 格式化数字 -->

<fmt:formatNumber pattern=”###,###.00” value="12332232.33438971" />

<!-- 等价于 request.setCharacterEncoding("UTF-8"); -->

<fmt:requestEncoding value="UTF-8" />

<!-- 函数标签库fn:必须写在EL表达式中 -->

<!-- 判断是否以.txt结尾 -->

${fn:endsWith("aaa.txt", ".txt")}

<!-- 截取字符串 -->

${fn:substring("abcdefghijklmn", 6,8)}

自定义标签:

开发步骤:标签处理类(xxTag,继承SimpleTagSupport)+tld文件(WEB-INF/.tld)+使用(taglib)

执行原理:setJspContext>setParent(父标签)>setJspBody(标签体内容)>doTag(调用标签)

<!-- WEB-INF/xx.tld文件 -->

<taglib xmlns="http://java.sun.com/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"

    version="2.1">

  <!-- 标签的版本号 -->

  <tlib-version>1.1</tlib-version>

  <!-- 简单名称,用于使用标签库的前缀 -->

  <short-name>ct</short-name>

  <!-- 是标签库的唯一名称 -->

  <uri>http://ct.hfxt.cn</uri>

  <!-- 定义一个标签 -->

  <tag>

   <!-- 标签名称 -->

     <name>xx</name>

     <!-- 标签处理程序的全名: 包名+类名 -->

     <tag-class>ct.hfxt.cn.tag.xxTag</tag-class>

     <!-- 标签体内容输出格式 -->

     <body-content>scriptless</body-content>

  </tag>

  <tag>

    <!-- 标签名称 -->

     <name>parent</name>

     <!-- 标签处理程序的全名: 包名+类名 -->

     <tag-class>ct.hfxt.cn.tag.ParentTag</tag-class>

     <!-- 标签体内容输出格式 -->

     <body-content>scriptless</body-content>

  </tag>

</taglib>

JDBC和JavaBean操作数据库

JavaBean:遵守以下规范的普通java类

1.必须有一个无参的构造方法

2.把类的属性私有化private(不能直接访问属性)

3.必须提供公开的getter和setter方法(通过getter和setter操作属性)

JavaBean作用:用于封装业务的数据,操作对象的属性(getter或setter方法)

JDBC技术:使用Java程序访问或操作数据库(发送SQL语句)的接口

    登录数据库(ip|port|user|password|连接的数据库)>发送sql语句

    jdbc接口:核心基础包(java.sql.*)-扩展包(javax.sql.*)

05_JSP/Servlet技术

JDBC接口:Driver-Connection-Statement-ResultSet

05_JSP/Servlet技术

|-Driver接口:驱动程序接口

|-Connection connect(): 用于连接数据库的方法

可以使用驱动程序管理类获取连接:DriverManager.getConnection(url,user,pasword);

|-Connection接口:代表和数据库的连接

|- Statement createStatement(): 创建Statement接口的对象。

|- PreparedStatement prepareStatement(String sql): 创建PreparedStatement

|- CallableStatement prepareCall(String sql): 创建CallableStatement接口的对象

|-Statement接口:用于执行静态SQL语句

|- int executeUpdate(String sql): 执行DDL和DML语句(更新sql语句)

|- ResultSet executeQuery(String sql): 执行DQL语句(查询sql语句)

|-PreparedStatement接口:用于执行预编译SQL语句

|- int executeUpdate():执行DDL和DML语句(更新sql语句)

|- ResultSet executeQuery(): 执行DQL语句(查询sql语句)

|-CallableStatement接口:用于执行存储过程SQL语句

|- ResultSet executeQuery(): 存储过程只能执行查询sql

|-ResultSet接口:表示数据库结果集

|- boolean next(): 将光标移至下一行

|- getXXX(): 获取结果集中的每列的值

JDBC操作步骤:获取连接>创建Statement>执行sql(CRUD)>结果集>关闭资源

1)注册驱动:Class.forName(驱动类名称)

2)获取连接对象:Connection conn = DriverManger.getConnection(url,user,password);

3)创建Statement对象(Statement/PreparedStatement/CallableStament)

conn.createStatement()

conn.preparedStaement(sql);

conn.preparedCall(sql)

4)如果使用PreparedStatement/CallableStament,设置参数stmt.setXXX(参数位置,参数值)

5)执行sql(发送参数)

DDL+DML:stmt.executeUpdate()  

DQL:ResultSet rs = stmt.executeQuery()

6)处理结果集:rs.next()、rs.getXXX()  列索引和列名称

7)关闭资源:rs.close();stmt.close();conn.close();

导入当前要连接的数据库相应的jar包

oracle 10g(ojdbc14.jar)/oracle 11g(ojdbc6.jar)/mySQL(mysql-connecyor)

//MySQL JDBC

private static String url = "jdbc:mysql://localhost:3306/d2017";

//连接数据库的字符串:jdbc协议+数据库协议+主机地址+端口+连接的数据库

private static String user = "root";//用户名

private static String password = "root";//密码

//1.驱动程序(只注册一次)

//Driver driver = new com.mysql.jdbc.Driver(); //多次注册

Class.forName("com.mysql.jdbc.Driver");//反射获取

//2.获取连接(使用驱动管理类)

Connection conn = DriverManager.getConnection(url, user, password);

//Statement

//3)准备sql:静态sql

//String sql = "DELETE FROM student WHERE id=2";

//4)创建Statement对象(CRUD)

//Statement stmt = conn.createStatement();

//5)执行sql

int count = stmt.executeUpdate(sql);

//6)关闭资源

stmt.close();

conn.close();

//PreparedStatement

//3p)准备sql:预编译SQL(?参数的占位符)

String sql = "select * from student where id=?";

//4p)创建PreparedStatement(CRUD):预编译sql语句(检查sql语法和权限,利用sql缓存功能)

PreparedStatement stmt = conn.prepareStatement(sql);

//5p)给参数赋值:参数的位置从1开始

stmt.setInt(1,16);

//6p)执行sql

rs = stmt.executeQuery();

//7p)遍历结果集

while(rs.next()){

int id = rs.getInt("id");

String name = rs.getString("name");

System.out.println(id+"\t"+name);

}

//8p)关闭资源

JdbcUtil.close(rs,stmt,conn);

//CallableStatement

//3c) 准备sql:带参数的存储过程SQL

String sql = "CALL pro_testByStu2(?,?)";

//4c)预编译

stmt = conn.prepareCall(sql);

//5c)参数赋值

stmt.setInt(1, 4);//设置输入参数

stmt.registerOutParameter(2, java.sql.Types.VARCHAR);//设置输出参数(sqlType)

//6c)发送参数,执行sql

stmt.executeQuery();

//7c)从输出参数中查看结果

/**

* ResultSet的getXX()方法为了获取列的值

* CallableStatement的getXXX()为了获取输出参数的值

*/

String name = stmt.getString(2);

System.out.println(name);

//8c)关闭资源

JdbcUtil.close( stmt, conn);

//Oracle JDBC

public int selectCount(){

    int cnt = 0;

    Connection conn = null;

    Statement stmt = null;

    ResultSet rs = null;

    try {

        //1.加载driver驱动

        Class.forName("oracle.jdbc.OracleDriver");

        //2.通过DriverManager获得Connection对象

        conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:ORCL"

,"t38", "123");

        //3.编写SQL命令

        String sql = "SELECT COUNT(1) c FROM userinfo";

        //4.Connection + String创建Statement对象

        stmt = conn.createStatement();

        //5.根据SQL命令调用相应的方法执行

        /* executeQuery(): 查询,返回“结果集”

         * executeUpdate(): 增删改,返回“受影响行数” */

        rs = stmt.executeQuery(sql);

        //6.如果是查询,则解析“结果集”ResultSet

            //结果集:1行用if;N行用while

        if(rs.next()){//rs.next()执行一次说明fetch一行记录

        /*

         * getXxxx(int) 根据字段的位置获取数据,位置从1开始

         * getXxxx(String) 根据字段的名称获取数据,以查询结果集的名称为准

        */

            cnt = rs.getInt("c");

        }

        } catch (SQLException e) {

            e.printStackTrace();

        } finally {

//7.关闭数据库连接资源(顺序:ResultSet, Statement, Connection)

                if(rs != null){

                    rs.close();

                }

                if(stmt != null){

                    stmt.close();

                }

                if(conn != null){

                    conn.close();

                }

        return cnt;

}

//配置文件jdbc.properties:key=value,键和值String形式

//Oracle

jdbc.driverClassName=oracle.jdbc.OracleDriver

jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl

jdbc.username=t38

jdbc.password=1234

//MySQL

url=jdbc:mysql://localhost:3306/d2017

user=root

password=root

driverClass=com.mysql.jdbc.Driver

//JdbcUtil工具类:MySQL JDBC封装

public class JdbcUtil {

    private static String url = null; //url

    private static String user = null; //user

    private static String password = null; //password

    private static String driverClass = null; //驱动程序类

    static{   //注册驱动程序:静态代码

        try {

            /**

             * 读取jdbc.properties文件

             */

            //1)创建Properties对象

            Properties prop = new Properties();

            //构造输入流

            /**

             *web项目:当前目录指向%tomcat%/bin目录,在web项目不能使用相对路径

             *web项目加载配置文件:ServletContext.getRealPath()/getResourceAsStream()

             */

            //1)获取类的对象

            Class clazz = JdbcUtil.class;

            //2) 使用类路径的读取方法去读取文件

            /**

             *  类路径:查询类的目录/路径

             *  java项目下:类路径的根目录,指向项目的bin目录

             *  web项目下:类路径的根目录,指向项目的WEB-INF/classes目录

             */

            InputStream in = clazz.getResourceAsStream("/jdbc.properties");

            //构造输入流

            //InputStream in = new FileInputStream("./src/jdbc.properties");

            //2)加载文件

            prop.load(in);

            //3)读取文件内容

            url = prop.getProperty("url");

            user = prop.getProperty("user");

            password = prop.getProperty("password");

            driverClass = prop.getProperty("driverClass");

            Class.forName(driverClass);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    /**

     * 获取连接方法

     */

    public static Connection getConnection(){

        try {

            Connection conn = DriverManager.getConnection(url, user, password);

            return conn;

        } catch (SQLException e) {

            e.printStackTrace();

            throw new RuntimeException(e);

        }

    }

    /**

     * 释放资源的方法 stmt+conn

     */

    public static void close(Statement stmt,Connection conn){

        if(stmt!=null){

            try {

                stmt.close();

            } catch (SQLException e) {

                e.printStackTrace();

                throw new RuntimeException(e);

            }

        }

       

        if(conn!=null){

            try {

                conn.close();

            } catch (SQLException e) {

                e.printStackTrace();

                throw new RuntimeException(e);

            }

        }

    }

    /**

     * 释放资源的方法 rs+stmt+conn

     */

    public static void close(ResultSet rs,Statement stmt,Connection conn){

        if(rs!=null){

            try {

                rs.close();

            } catch (SQLException e) {

                e.printStackTrace();

                throw new RuntimeException(e);

            }

        }

        if(stmt!=null){

            try {

                stmt.close();

            } catch (SQLException e) {

                e.printStackTrace();

                throw new RuntimeException(e);

            }

        }

        if(conn!=null){

            try {

                conn.close();

            } catch (SQLException e) {

                e.printStackTrace();

                throw new RuntimeException(e);

            }

        }

    }

}

JDBC批处理:

Statement批处理:

void addBatch(String sql)  添加sql到缓存区(暂时不发送)

int[] executeBatch() 执行批处理命令。 发送所有缓存区的sql

void clearBatch()  清空sql缓存区

PreparedStatement批处理:

void addBatch() 添加参数到缓存区

int[] executeBatch() 执行批处理命令。 发送所有缓存区的sql

void clearBatch()  清空sql缓存区

JDBC处理大数据文件:

mysql:

字符串(varchar/char 65535)>大文本数据(tinytext/longtext/text)

字节bit>大字节文件:tinyblob(255b)/blob(64kb)/MEDIUMBLOB(16M)/longblob(4GB)

oracle:

字符串(varchar/char 65535)>大文本数据(clob)

字节(bit)>大字节文件(blob)

数据库事务:

mysql数据库事务

开启事务: set autocommit=0;

提交事务: commit;

回滚事务: rollback;

jdbc控制事务

开启事务: Connection.setAutoCommit(false);

提交事务: Connection.commit();

回滚事务: Connection.rollback();

连接池:提高Connection对象的利用率,提高执行sql的效率; 最大连接

jdbc步骤:获取连接->得到Statement->发送sql->关闭连接
    连接池要素:初始化连接数-最大连接数-最大等待时间

05_JSP/Servlet技术

    //自定义连接池

private static LinkedList<Connection> pool = new LinkedList<Connection>();

    private int initCount = 5; //连接池的初始化连接数

    private int maxCount = 10; //连接池的最大连接数

    private int currentCount = 0; //用于记录当前连接数量

连接池工具:DBCP(BasicDataSource连接池对象)-C3P0(hibernate内置默认)

    DataSource接口:统一连接池获取连接的方法

// DBCP

//1)创建dbcp连接池对象

BasicDataSource bds = new BasicDataSource();

//2)设置连接参数

bds.setUrl(url);

bds.setUsername(user);

bds.setPassword(password);

bds.setDriverClassName(driverClass);

//3)设置连接池参数

bds.setInitialSize(5);//初始化连接

bds.setMaxActive(10);//最大连接数

bds.setMaxWait(3000);//当超过最大连接数时,最大等待时间为3秒

//4)获取连接

try {

//从连接池中获取连接

for(int i=1;i<=11;i++){

Connection conn = bds.getConnection(); //注意: 这里返回的Connection对象,不是原来的Connection,而是代理后的Connection对象

System.out.println(conn);

//使用连接池,记住释放连接

if(i==3){

conn.close();//把连接对象放回连接池中的。连接池中最大能够保存最大连接数的连接对象

}

}

} catch (SQLException e) {

e.printStackTrace();

}

// C3P0

//1)创建连接池对象

ComboPooledDataSource cds = new ComboPooledDataSource();

//2)设置连接参数

cds.setJdbcUrl(url);

cds.setUser(user);

cds.setPassword(password);

cds.setDriverClass(driverClass);

//3)设置连接池相关的参数

cds.setInitialPoolSize(5);//初始化连接数

cds.setMaxPoolSize(10);//最大连接数

cds.setCheckoutTimeout(3000);//最大等待时间

cds.setMinPoolSize(3); //最小连接数

//4)获取连接

for(int i=1;i<=11;i++){

Connection conn = cds.getConnection();

System.out.println(conn);

//关闭第3个

if(i==3){

conn.close();//本质是把连接对象放回连接池中

}

}

BeanUtils:方便开发者操作javabean对象, 拷贝javabean对象

    commons-beanutils.jar核心包+commons-logging.jar辅助包

//1.从一个javabean拷贝到另一个javabean对象(所有属性)

    BeanUtils.copyProperties(拷贝到的对象,原来的对象);

//2.拷贝一个javabean对象的属性

BeanUtils.copyProperty(拷贝到的对象, "拷贝的属性", "拷贝的值");

//3.从一个map集合中拷贝到javabean对象中

    BeanUtils.copyProperties(javabean对象, map);

元数据:编写更通用的jdbc代码

数据库元数据DatabaseMetaData-参数元数据ParameterMetaData-结果集的元数据ResultSetMetaData

1.连接数据库:数据库的元数据对象DatabaseMetaData

      DatabaseMetaData dbmd = conn.getMetaData();

2.预编译statement执行sql:预编译的sql参数使用参数元数据ParameterMetaData

      ParameterMetaData pmd = stmt.getParameterMetaData();

3.执行查询sql,返回结果集ResultSet rs:使用结果集的元数据ResultSetMetaData

ResultSetMetaData rsmd = rs.getMetaData();

DBUtils: jdbc代码的封装

    QeuryRunnerç±»: update()更新、query()查询

    ReusltHandler接口(把结果集封装成不同的对象):BeanHandler、BeanListHandler、ScalarHandler

导包: DBCP (commons-dbcp.jar核心包+commons-pool.jar辅助包)、C3P0(c3p0-0.9.1.2.jar)

//jdbcUtil:连接池版本的jdbcUtil

public class JdbcUtil {

    //一个数据库只需要一个连接池对象

    private static DataSource ds  = new ComboPooledDataSource();

    /**

     * 获取连接池对象的方法

     */

    public static DataSource getDataSource(){

        return ds;

    }

    /**

     * 获取连接对象的方法

     */

    public static Connection getConnectio(){

        try {

            return ds.getConnection();

        } catch (SQLException e) {

            e.printStackTrace();

            throw new RuntimeException(e);

        }

    }

}

//SQLUtil

/**

*  ResultSetHandler接口: 用于把结果集封装成不同类型的对象

*  ArrayHandlerç±»: 把结果集的第一行结果封装成对象数组

*  ArrayListHandler类: 把结果集的每一行结果封装成对象数组,把每个对象数组放入List集合中

*  BeanHandler类: 把结果集的第一行,封装成javabean对象(常用)

*  BeanListHandler类: 把结果集的每行封装成javabean,把每个javabean放入List集合中(常用)

*  ScalarHandler类:查询聚合函数(例如:count(*))  (常用)

*/

public class SQLUtil {

    /**

     * 通用的更新方法

     */

    public static void update(String sql,Object[] values){

        try {

            ComboPooledDataSource ds = new ComboPooledDataSource();

            Connection conn = ds.getConnection();

            PreparedStatement stmt = conn.prepareStatement(sql);

            ParameterMetaData pmd = stmt.getParameterMetaData();

            //得到sql中有几个参数

            int count = pmd.getParameterCount();

            //可以把参数值放入数组中

            //新的赋值方式

            for(int i=1;i<=count;i++){

                //setObject可以设置任何类型参数

                stmt.setObject(i, values[i-1]);

            }

            stmt.executeUpdate();

        } catch (SQLException e) {

            e.printStackTrace();

        }

    }

    /**

     * 通用的查询方法

     */

    public static <T> List<T> query(String sql,Object[] values,Class<T> clazz){

        try {

            ComboPooledDataSource ds = new ComboPooledDataSource();

            Connection conn = ds.getConnection();

            PreparedStatement stmt = conn.prepareStatement(sql);

            ParameterMetaData pmd = stmt.getParameterMetaData();

            //得到参数数量

            int paramCount = pmd.getParameterCount();

            //如果是null,则不赋值了

            if(values!=null){

                //参数赋值

                for(int i=1;i<=paramCount;i++){

                    stmt.setObject(i, values[i-1]);

                }

            }

            ResultSet rs = stmt.executeQuery();

            //得到结果集的元数据

            ResultSetMetaData rsmd = rs.getMetaData();

            //得到列数量

            int columnCount = rsmd.getColumnCount();

            List list = new ArrayList();

            while(rs.next()){//行

                //每一行数据就是一个对象

                //构造对象

                Object obj = clazz.newInstance();

                //遍历每列

                for(int i=1;i<=columnCount;i++){

                    //得到表的列值

                    Object value = rs.getObject(i);

                    //通过结果集元数据可以得到字段名称

                    String columnName = rsmd.getColumnName(i);

                    //表中的每列的值就封装到对象的每个属性中

                    //约定一个前提:表中的每个字段名称和javabean对象中属性名称保持一致

                    //把值拷贝到javabean对象中

                    BeanUtils.copyProperty(obj, columnName, value);

                }

                list.add(obj);

            }

            return list;

        } catch (Exception e) {

            e.printStackTrace();

            throw new RuntimeException(e);

        }

    }

}

JNDI数据源

使用JNDI数据源的步骤:

  1. 在Tomcat的context.xml配置文件中添加数据源配置:

<Resource name="mssql" type="javax.sql.DataSource" auth="Container"    driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"

    url="jdbc:sqlserver://localhost:1433;DataBaseName=petdb"

    username="admin" password="123"

    maxActive="10" maxIdle="2" maxWait="10000"

/>

  1. 给Tomct添加数据库驱动jar包:在tomcat的lib目录下添加数据库驱动jar包
  2. 在web项目的核心配置文件web.xml中去关联要使用的JNDI数据源

<resource-ref>

<description>news DataSource</description>

<res-ref-name>mssql</res-ref-name>

<res-type>javax.sql.DataSource</res-type>

<res-auth>Container</res-auth>

</resource-ref>

  1. 在代码中获得JNDI数据源中的某个数据库连接对象

Context cxt = new InitialContext();

DataSource ds = cxt.lookup("java:comp/env/mssql");//前缀java:comp/env/

Connection conn = ds.getConnection();

分页查询&条件查询

分页查询:分页对象PageBean>Servlet(转发数据)>jsp页面(展示PageBean数据)

05_JSP/Servlet技术

分页查询三层结构:

javabean对象:当前页的数据、首页、上一页、下一页、末页/总页数、当前页码、总记录数、每页显示记录数

dao层:查询当前页的数据|总记录数

service层;封装分页javabean对象(首页|上一页|下一页|末页/总页数)

web层:当前页码|每页显示记录数

条件查询:根据不同的条件拼凑条件查询的sql语句

条件查询sql语句:

select * from 表 where 1=1  (恒成立)

if 条件1

and 字段1 like 内容 (模糊查询)

if 条件2

and 字符2 like 内容

分页+条件查询:分页的基础上,修改这两条语句

查询当前页数据的sql语句:

select * from 表 where 1=1

if 条件1

and 字段1 like 内容

if 条件2

and 字符2 like 内容

limit 起始行,每页查询行数;

查询总记录数的sql语句:

select count(*) from 表 where 1=1

if 条件1

and 字段1 like 内容

if 条件2

and 字符2 like 内容

//PageBean

public class PageBean {

    List<Employee> data; // 当前页数据

    int firstPage; // 首页

    int prePage; // 上一页

    int nextPage;// 下一页

    int totalPage;// 末页/总页数

    int currentPage;// 当前页

    int totalCount;// 总记录数

    int pageSize;// 每页显示记录数

    public List<Employee> getData() {

        return data;

    }

    public void setData(List<Employee> data) {

        this.data = data;

    }

    /**

     * 首页

     */

    public int getFirstPage() {

        return 1;

    }

    public void setFirstPage(int firstPage) {

        this.firstPage = firstPage;

    }

    /**

     * 上一页 算法:如果当前页是首页,则为1,否则为(当前页-1)

     */

    public int getPrePage() {

        return this.getCurrentPage()==this.getFirstPage()?1:this.getCurrentPage()-1;

    }

    public void setPrePage(int prePage) {

        this.prePage = prePage;

    }

    /**

     * 下一页  算法: 如果当前页是末页,则为末页,否则为(当前页+1)

     */

    public int getNextPage() {

        return this.getCurrentPage()==this.getTotalPage()

                ? this.getTotalPage()

                : this.getCurrentPage()+1;

    }

    public void setNextPage(int nextPage) {

        this.nextPage = nextPage;

    }

    /**

     * 总页数  算法:如果总记录数%每页显示记录数能够整除,则为(总记录数/每页显示记录数),否则  (总记录数/每页显示记录数+1)

     */

    public int getTotalPage() {

        return this.getTotalCount()%this.getPageSize()==0 ? this.getTotalCount()/this.getPageSize(): this.getTotalCount()/this.getPageSize()+1;

    }

    public void setTotalPage(int totalPage) {

        this.totalPage = totalPage;

    }

    public int getCurrentPage() {

        return currentPage;

    }

    public void setCurrentPage(int currentPage) {

        this.currentPage = currentPage;

    }

    public int getTotalCount() {

        return totalCount;

    }

    public void setTotalCount(int totalCount) {

        this.totalCount = totalCount;

    }

    public int getPageSize() {

        return pageSize;

    }

    public void setPageSize(int pageSize) {

        this.pageSize = pageSize;

    }

}

//Dao:根据条件拼装sql

public int queryData(currentpage,pageSize,query){

QueryRunner qr = new QueryRunner(JdbcUtil.getDataSource());

StringBuffer sql = new StringBuffer("select count(*) from table where 1=1 ");

if(query!=null){

if(query.getName()!=null && !query.getName().trim().equals("")){

sql.append(" and name like '%"+query.getName()+"%'");

}

if(query.getId()!=0){

sql.append(" and deptId="+query.getId()+"");

}

}

Long count = (Long)qr.query(sql.toString(), new ScalarHandler());

return count.intValue();

}

//ListServlet

/****  1)接收用户输入的参数:currentPage参数   ****/

String currentPage = request.getParameter("currentPage");

if (currentPage == null || currentPage.equals("")) {

// 如果用户第一次访问,没有传递currentPage参数,则当前页为1

currentPage = "1";

}

//接收用户输入的每页显示记录数

String pageSize = request.getParameter("pageSize");

//如果没有传递这个pageSize参数,则为默认值5

if(pageSize==null || pageSize.equals("")){

pageSize = "5";

}

/**** 2)调用业务逻辑方法,获取结果  ****/

//1)封装PageBean对象

Service service = new Service();

PageBean pageBean = service.queryPageBean(Integer.parseInt(currentPage)

,Integer.parseInt(pageSize));

/***** 3)把结果转发到jsp页面显示   ****/

//2)把PageBean对象放入域对象中

request.setAttribute("pageBean", pageBean);

//3)转发到jsp页面显示数据

request.getRequestDispatcher("/List.jsp").forward(request, response);

//List.jsp

<html>

  <head>

    <title>分页&条件查询数据</title> 

  </head>

  <body>

    <table align="center" width="600px">

        <tr>

            <td>

            查询条件:<input type="text" value="${param['condition'] }" />

            <input type="submit" value="搜索"/>

            </td>

        </tr>

    </table>

    <table border="1" align="center" width="800px">

     <tr>

        <th><br>编号</th>

        <th>姓名</th>

     </tr>

     <c:forEach items="${pageBean.data}" var="emp">

     <tr>

        <td>${obj.id }</td>

        <td>${obj.name }</td>

     </tr>

     </c:forEach>

     <tr>

        <td align="center" colspan="6">

        <%--

            1)如果当前页是首页,那么不能点击“首页”和“上一页”,否则可以点击

            2) 如果当前页是末页,那么不能点击“下一页”和“末页”,否则可以点击

         --%>

         <c:choose>

            <c:when test="${pageBean.currentPage==pageBean.firstPage}">

                首页&nbsp;

                上一页

            </c:when>

            <c:otherwise>

                <a href="${pageContext.request.contextPath }/ListServlet?currentPage=${pageBean.firstPage }&pageSize=${pageBean.pageSize}">首页</a>&nbsp;

                <a href="${pageContext.request.contextPath }/ListServlet?currentPage=${pageBean.prePage}&pageSize=${pageBean.pageSize}">上一页</a>&nbsp;

            </c:otherwise>

         </c:choose>

          <c:choose>

            <c:when test="${pageBean.currentPage==pageBean.totalPage}">

                下一页&nbsp;

                末页

            </c:when>

            <c:otherwise>

                <a href="${pageContext.request.contextPath }/ListServlet?currentPage=${pageBean.nextPage }&pageSize=${pageBean.pageSize}">下一页</a>&nbsp;

            <a href="${pageContext.request.contextPath }/ListServlet?currentPage=${pageBean.totalPage }&pageSize=${pageBean.pageSize}">末页</a>&nbsp;

            </c:otherwise>

         </c:choose>

            当前为第${pageBean.currentPage }页/共${pageBean.totalPage }页&nbsp;

            共${pageBean.totalCount }条数据&nbsp;

            每页显示 <input type="text" size="2" id="pageSize" value="${pageBean.pageSize }" onblur="changePageSize()"/> 条&nbsp;

            跳转到第<input type="text" id="curPage" size="2" onblur="changePage()"/>页

        </td>

     </tr>

    </table>

  </body>

<script type="text/javascript">

    /*改变每页显示记录数*/

    function changePageSize(){

        //1.得到用户输入

        var pageSize = document.getElementById("pageSize").value;

        //判断规则:只能输入1-2个的数字

        var reg = /^[1-9][0-9]?$/;

        if(!reg.test(pageSize)){

            alert("只能输入1-2位的数字");

            return;

        }

        //2.请求ListServlet,同时发送参数(pageSize)

        var url = "${pageContext.request.contextPath}/ListServlet?pageSize="+pageSize;

        window.location.href=url;

    }

    /*跳转页面*/

    function changePage(){

        var curPage = document.getElementById("curPage").value;

        var reg = /^[1-9][0-9]?$/;

        if(!reg.test(curPage)){

            alert("只能输入1-2位的数字");

            return;

        }

        /*如果输入的页码大于等于总页数*/

        var totalPage = "${pageBean.totalPage}";

        if(curPage>totalPage){

            alert("已经超过总页数");

            return;

        }

        var url = "${pageContext.request.contextPath}/ListServlet?currentPage="+curPage+"&pageSize=${pageBean.pageSize}";

        window.location.href=url;

    }

  </script>

</html>

过滤器和监听器

Servlet三大组件:servlet-过滤器Filter-监听器Listener

(servlet)Servlet接口:javax.servlet.Servlet

(过滤器)Filter接口:javax.servlet.Filter

(监听器)Listener接口:javax.servlet.*

过滤器(Filter接口):在请求资源(静态资源或动态资源)或响应资源,执行过滤任务

05_JSP/Servlet技术

Filter生命周期:构造方法-init方法-doFilter方法-destory方法

init方法:初始化方法。

参数FilterConfig对象,用于读取Filter配置的init-param参数。

doFilter方法: 执行过滤任务。每次访问目标资源时被执行。

参数一: ServletRequest,HttpServletRequest的父类,可以强制转换为HttpServletREquest

参数二:ServletResponse,HttpServletResponse的父类,可以强制转换为HttpServletResponse

参数三:FilterChain,是过滤器链对象。当一个资源同时被多个过滤器所过滤,那么就形成一个过滤器链。调用FilterChain的doFilter(request,response)方法,执行过滤器链的下一个过滤器,如果没有过滤器则调用目标资源。

destory方法:销毁Filter对象的时候调用。

装饰者模式:当需要对某些类的方法进行增强,那么可以对该类进行装饰

1.编写一个装饰者类,继承被装饰者类(被装饰者类是非final)

2.声明一个成员变量,用于接收被装饰者类对象

3.在装饰者类的构造方法中,接收被装饰者类对象

4.覆盖需要增强的方法

解决参数中文乱码问题:MyHttpRequest装饰者类,对HttpServletRequest的getParameter方法进行增强

getParameterValues("name") / getParameterMap();

网页内容压缩问题:MyHttpResponse装饰者类,对HttpServletResponse的getWriter方法进行增强

让getWriter()方法返回带缓存功能的PrintWriter对象,这样PrintWriter的write方法写入的内容就直接写入到PinrtWriter中的缓存对象中。然后可以通过缓存对象取出内容。

ByteArrayOutputStream buf = new ByteArrayOutputStream()

GZIPOutputStream gzip = new GZIPOutputStream(buf);

gzip.write(内容);

gzip.finish();  //内容写入到ByteArrayOutputStream中

byte[] result = buf.toByteArray(); //从ByteArrayOutputStream中得到压缩后的内容

登录权限的过滤:SecurityFilter过滤器中,进行登录权限的判断,这个过滤器要过滤哪些需要权限才能访问的资源。(注意: 不要过滤登录页面,登录的servlet)

if(session==null){

没有权限的处理

}else{

String user = (String)session.getAttribute("user");

if(user==null){

没有权限的处理

}

}

//登录成功后,应该要放行资源

chain.doFilter(request,response);

Filter代码实现:

  1. 创建实现Filter接口的类
  2. 重写doFilter()方法:放过/调用下一个过滤器或下一个资源、
  3. web.xml配置过滤器(映射配置):

<!-- 配置一个过滤器 -->

<!-- 过滤器配置 -->

<filter>

<!-- 内部名称 -->

<filter-name>FirstFilter</filter-name>

<filter-class>gz.itcast.a_filter.FirstFilter</filter-class>

</filter>

<!-- 过滤器映射配置 -->

<filter-mapping>

<!-- 内部名称,和上面的名称保持一致 -->

<filter-name>FirstFilter</filter-name>

<!--过滤器的url-pattern是过滤的路径,而不是访问过滤器的路径  -->

<url-pattern>/target</url-pattern>

</filter-mapping>

<!-- 参数编码过滤器 -->

<filter>

<filter-name>EncodingFilter</filter-name>

<filter-class>com.hfxt.filter.EncodingFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>EncodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<!-- 网页内容压缩过滤器 -->

<filter>

<filter-name>GZIPFilter</filter-name>

<filter-class>gz.itcast.d_cases.GZIPFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>GZIPFilter</filter-name>

<url-pattern>/content</url-pattern>

</filter-mapping>

监听器:监听必须交给tomcat服务器运行

web的事件编程:

事件源:ServletContext对象 ServletRequest对象  HttpSession对象 (域对象)

事件: ServletContextEvent ServletRequestEvent,xxx(创建或销毁对象,对象的属性修改)

事件监听器(接口):ServletContextListener ServletRequestListerner

ServletContext对象:

ServletContextListener:创建和销毁

ServletContextAttributeListener:属性增加,修改,删除

ServletRequest对象:

ServletRequestListener:创建和销毁

ServletRequestAttributeListener:属性增加,修改,删除

HttpSession对象:

HttpSessionListener:创建和销毁

HttpSessionAttributeListener:属性增加,修改,删除

  1. 创建用户类实现HttpSessionBindingListener接口
  2. 重写valueBound()和valueUnbound()方法,实现用户数量的统计
  3. 在web.xml配置监听器

    <listener>

        <!--监听器所属类的完全限定名-->

        <listener-class>com.hfxt.entity.UserListener</listener-class>

    </listener>

文件上传和下载

上传:

三个条件:

a)以post方式提交的表单

b)表单内必须有<file/> 组件

c)把表单的enctype属性设置为multipart/form-data

使用apache组织的上传工具

DiskFileItemFactory : 设置上传环境。缓存大小,缓存目录

核心类:ServletFileUpload: 用于解析上传的文件

List<FileItem> parserRequest(request);  解析上传文件,返回文件对象集合

FileItem对象:代表一个上传后的文件(名称,大小,类型,内容)

FileItem.isFormFiled():  判断该文件是否是普通表单组件。

FileItem.getString(): 获取普通表单组件的内容

1)设置限制文件大小:

ServletFileUpload.setFileSizeMax() : 设置单个文件

ServletFileUpload.setSizeMax() 设置总文件

2)设置文件名编码

ServletFileUpload.setHeaderEncoding("utf-8);

3)设置上传文件的进度监听器

ServletFileUpload.setProgressListener(监听器的实现类);

下载:

使用servlet进行文件下载:

设置响应头,通知浏览器以下载方式打开文件

response.setHeader("content-disposition,"attachment;filename=xxx.jpg");

1)读取到服务器文件

InputStream in  = xxxxxxxx

2)把文件流输出到浏览器作为响应的实体内容

OutputStream out = response.getOutputStream();

byte[] buf = new byte[1024];

int len = 0;

while(  (len=in.read(buf))!=-1   ){

out.wirte(buf,0,len);

}

第三方控件

第三方组件:第三方组织提供的组件

CKEditor:网页中实现所见即所得的编辑器

*在页面中引入ckeditor.js:

<script type=”text/javascript” src=”cjeditor/ckeditor.js”>

</script>

*在页面加入textarea,使之成为ckeditor

  <textarea id=”newscontent” name=”newscontent” class=”ckeditor”>

  </textarea>

*通过config.js配置CKEditor

Web开发模式:模型1(jsp+javabean)-模型2(java+Servlet+javabean)

MVC:Mdoel模型(javabean)-View视图(jstl+el)-Cotroller控制器(servlet)

    项目开发结构:dao层(CURD)-service层(业务逻辑)-web层(servlet+jsp)

05_JSP/Servlet技术