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

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

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);

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(参数放在实体内容中)

GETrequest.getQueryString();

POSTrequest.getInputStream();

通用获取参数方式:(不分PostGet

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");

请求重定向(浏览器行为):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方法

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

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

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()

    <%

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.jarstandard.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>

JDBCJavaBean操作数据库

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.*)

JDBC接口:Driver-Connection-Statement-ResultSet

|-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)从输出参数中查看结果

/**

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

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

*/

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行用ifN行用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->关闭连接
    连接池要素:初始化连接数-最大连接数-最大等待时间

    //自定义连接池

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数据)

分页查询三层结构:

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接口):在请求资源(静态资源或动态资源)或响应资源,执行过滤任务

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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值