Servlet 是标准的服务器端 Java 应用程序,它扩展了 Web 服务器的功能。
Servlet 运行在一个 Java 环境中,即 Java 虚拟机,或 JVM。servlet 将被载入到一个应用服务器中,例如 WebSphere(tm) Application Server。
由于 Servlet 是由一个用户的请求来调用,Servlet 对应一个 Web 站点单一 URL 的系统响应。一个企业级 Web 站点可能对一个请求就有多个 Servlet,例如注册、购物和支付。
由于 Servlet 运行在服务器环境中,用户永远不会直接看到它们而且 Servlet 不需要图形的用户界面。Servlet 是 Java 组件,可以在系统需要它们时被载入。
使用Servlet生成的动态文本,比CGI脚本更容易,且运行效率更高,是CGI脚本的高效替代品。
Servlet API的最大一个优点是协议的独立性,它不假定网络传输使用的协议、Servlet如何装载以及运行的服务器环境,这些特性使得Servlet API可以方便的嵌入到许多不同种类的服务器中。另外,它还具有可扩展、简单、短小、容易使用的特点。
一个简单的Servlet可以处理使用HTML FORM在HTTP(S)上使用POST传递的数据,例如:一个带有信用卡数据的定单。复杂的Servlet可以用于实现定单输入和处理系统、生产及库存数据库、或者一个复杂的联机支付系统。
一个Servlet可以将请求传递给另一个服务器,这项技术在多个服务器中可以用来平衡装载相同的镜像内容。或者,可以用于将一个逻辑服务划分给几个服务器,按照任务类型或组织的边界路由请求,实现负载均衡。
Java Servlet API是一个标准的Java扩展API。在J2EE中,它已经发展成为Java核心框架的一部分。
JavaSoft提供了一个包,可以用于在其他服务器中嵌入Servlet。
Servlet API已经被大多数基于Java的Web服务器支持,因此,当使用Servlet API时,可以继承Java的许多优点:不但代码不存在内存漏洞和难以发现的指针bug,而且还可以运行在许多不同服务器提供商的平台上。
Servlet采用常见的接受请求和生成响应的编程模型,该模型使用了一系列的分布式系统编程工具箱,这包括从远程过程调用到向Web服务器发出HTTP请求等。
Servlet编程非常简单,主要就是实现一个Servlet接口,例如:
Public class MyServlet extends GenericServlet
{
Public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException
{
……
}
}
Service方法内可以操作request和response参数,这里封装了客户机发送的数据,提供了对参数的访问,并允许Servlet报告各种状态。通常Servlet通过输入流得到大多数参数,并使用输出流发送它们的响应。
ServletInputStream in = request.getInputStream();
ServletOutputStream out = request.getOutputStream();
由于Servlet是一种Java对象,它们由特定的实例数据组成,这意味着正在执行的Servlet是运行在服务器内部的独立的应用程序。
在初始化期间,Servlet访问一些特定的Servlet配置数据,这允许相同Servlet类的不同实例可以用不同的数据来初始化,并当作不同名字的Servlet来管理。在初始化期间提供的数据中,包括了每个实例将在哪里保存它们自己的状态。
Servlet还可以利用ServletContext对象与它们自己的环境进行交互。
Servlet可以使用下面几种模式,然而,不是所有服务器环境都支持以下所有这些模式:
l 基本的模式:以请求/响应协议为核心。
l 服务器可以用过滤链链接多个Servlet。
l Servlet可以支持特定的协议,例如HTTP。
l 在基于HTTP的应用程序中,Servlet是一个基于CGI扩展的完全的替代品。
l 在基于HTTP的应用程序中,Servlet还可以作为HTML的服务器端来包含动态的生成的Web文档。
Servlet装载之后,其生命周期涉及三个主要方法:
u init方法
Servlet通过调用init方法被服务器激活,如果开发者需要,可以执行一些内在的设置,这样,无需每个请求都设置一次。
u service方法
当初始化后,Servlet处理许多请求,为每一个客户请求生成一个service调用,这些请求可能是并行的,这允许Servlet调整多个客户的行为,类的静态属性可以用于在请求中共享数据。
u destroy方法
Servlet对象将一直处理请求,除非服务器调用destroy方法显示的关闭Servlet,Servlet类还是一个合格的垃圾收集器。
与当前任何其他的服务器扩展API不同,Java Servlet提供了强大的安全策略支持。这是因为所有的Java环境都提供了一个安全管理器,可以用于控制是否允许执行某项操作,例如网络或文件的访问。缺省的,通过网络装载的所有Servlet都是不可信的,它不允许执行诸如访问网络服务或本地文件的操作。仅仅在Java Web Server中创建的,或者在本地由服务器管理员控制的…/Servlet目录的Servlet,才是完全可信的,并赋予所有的权限。
Servlet最突出的特性之一就是对于每一个请求不需要象CGI那样单独创建一个新的进程。在大多数环境中,许多Servlet可以并行运行在与服务器相同的进程中,处理许多已初始化的客户请求,这些初始化的开销由许多方法分担,对于该service方法所面对的所有客户请求,它们都有机会共享数据和通讯资源,并充分利用系统缓存。
Servlet部署在容器里,它的生命周期由容器管理。
Servlet的生命周期概括为以下几个阶段:
u 装载Servlet:这项操作一般是动态执行的。有些服务器提供了相应的管理功能,可以在启动的时候就装载Servlet并能够初始化特定的Servlet。
u 创建一个Servlet实例。
u 调用Servlet的init()方法。
u 服务:如果容器接收到对此Servlet的请求,那么它将调用Servet的service()方法。
u 销毁:实例被销毁,通过调用Servlet的destroy()方法来销毁Servlet。
在几个阶段中,对外提供服务是最重要的阶段,service()方法是我们最关心的方法,因为它才是真正处理业务的方法。
Servlet为客户端提供服务,如下图所示:
客户端 |
Web容器 |
Servlet |
1:请求 |
2:init() |
3:service() |
5:响应 |
6:destroy() |
4:返回结果 |
在进行具体Servlet编程之前,这里,先举一个最简单的Servlet程序,熟悉一下Servlet的结构。
package com.iss.test;
import java.io.*
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorldServlet extends HttpServlet
{
//Initialize global variables
public void init()
throws ServletException
{
}
//Process the HTTP Get request
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html;charset=gb2312");
PrintWriter out = response.getWriter();
out.println(“”);
out.println(“”);
out.println(“
out.println(“”);
out.println(“”);
out.println(“Hello, World!”);
out.println(“”);
out.println(“”);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
doGet(request, response);
}
//Clean up resources
public void destroy()
{
}
}
使用任何一种编译工具,编译为class文件。比如jdk自带的javac命令:
Javac HelloWorld.java
使用任何一种打包工具,打成可发布的jar包或web组件包。
将打包好的程序发布到任何支持Servlet容器的服务器上去。
这个servlet对应的部署描述符如下:(位于WEB-INF下的web.xml中)
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd”
version=” 2.4” >
HelloWorldServlet
从客户端访问服务器上的应用,比如通过浏览器访问,服务器给客户端返回“Hello,world!”的信息。
一个抽象类,可以从该类派生出一个子类来实现一个HttpServlet,接受来自Web站点的请求,并将处理后的响应结果发回Web站点。在HttpServlet的子类中,必须至少重载下表中所列的其中一种方法。
HttpServlet子类通常重载的方法
方法名 |
用途 |
doGet |
如果Servlet支持HTTP GET请求,用于HTTP GET请求 |
doPost |
如果Servlet支持HTTP POST请求,用于HTTP POST请求 |
doPut |
如果Servlet支持HTTP PUT请求,用于HTTP PUT请求 |
doDelete |
如果Servlet支持HTTP DELETE请求,用于HTTP DELETE请求 |
init和destroy |
如果需要管理Servlet生命周期内所持有资源,可以重载这两个方法 |
通常,不重载service方法,对于上表中的每一种HTTP请求,service方法通过分派它们到相应的Handle线程(doXXX方法)来处理这些标准的HTTP请求。
通常也不重载doOptions和doTrace方法,service方法通过分派它们到doTrace和doOptions来支持HTTP1.1 TRACE和OPTIONS。
Servlet通常运行在多线程的服务器中,因此,所编写的Servlet代码必须能够处理并行请求和对数据资源的同步访问。
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
用于获取服务器信息,并将其作为响应返回给客户端。当经由web浏览器,或者通过HTML、Jsp直接访问Servlet的URL时,一般使用Get调用。
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
用于客户端把数据传送到服务器端。使用它的好处是可以隐藏发送给服务器的任何数据。适合于发送大量的数据。
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
Put的调用和Post相似,它允许客户端把真正的文件存放在服务器上,而不仅仅是传送数据。
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
它的调用和Post相似,它允许客户端删除服务器端的文件或者web页面。
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
由容器调用以使此Servlet能够处理Trace请求。这个方法主要用于测试,它是不可以覆盖的方法。
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
它用于处理客户端的Head调用,并且返回一个response。当客户端只需要知道响应的Header时,它就发出一个Header请求。
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
它用于处理客户端的Options调用,通过这个调用,客户端可以获得此Servlet支持的方法。
该接口扩展了javax.servlet.servletRequest接口,用于提供更多的功能给HttpServlet。Servlet引擎实现该接口,创建HttpServletRequest对象,将来自客户浏览器的信息传递给HttpServlet的service方法。
public long getDateHeader(String name)
得到一个特定请求的header的、描述Date对象的长整形值。Header是大小写敏感的。如果请求中没有该name的header,则方法返回-1,如果方法不能转换日期,则抛IllegalArgumentException异常。
public String getHeader(String name)
得到有关请求header的值,为一个字符串,如果指定名字的header没有随请求一起发送,该方法返回null,name是大小写敏感的。
public Cookie[] getCookies()
得到一个Cookie对象数组,含有所有发送请求的浏览器的Cookie,如果浏览器没有发送任何Cookie,则返回null。
public Enumeration getHeaderNames()
得到请求中的所有header名。
public String getRequestURI()
得到HTTP请求的第一行中从协议名到查询字符串之间的URL部分。
public HttpSession getSession(Boolean create)
public HttpSession getSession()
得到与请求相联系的当前HttpSession,如果需要,则为请求创建一个新的任务。
该接口扩展了OutputStream类,因此,可以使用OutputStream类中声明的方法,例如,getOutputStream()方法。
public void addCookie(Cookie cookie)
增加指定的cookie到响应。
public void setHeader(String name, String value)
使用给定的名字和值设置响应头。
该接口定义了一个Servlet配置对象,Servlet引擎可以用来传递信息给Servlet以便初始化Servlet。
配置信息含有初始化参数,这些参数以“名字/值”的形式成对出现,描述关于服务器的Servlet信息。
public ServletContext getServletContext()
得到服务器传递给Servlet的一个ServletContext对象。
public String getInitParameter(String name)
得到一个含有参数name指定的初始化参数的值,如果参数不存在则为null。
public Enumeration getInitParameterNames()
得到Servlet的初始化参数的名字,返回值是一个Enumeration对象。
该接口定义了一套Servlet与Servlet引擎通信的方法,例如:得到MIME类型的文件、确定运行在服务器上的其他Servlet或写Servlet日志文件。
Servlet引擎通过返回一个ServetContext对象来与Servlet交流,Servlet使用ServletConfig接口的getServletContext方法得到ServletContext对象。
public ServletContext getContext(String uripath)
得到服务器上特定URL的一个ServletContext对象,该方法允许Servlet访问特定URL上的资源,并得到来自该URL的RequestDispatcher对象。
public String getMimeType(String file)
得到指定文件的MIME类型,如果不知道MIMI类型则为null。MIMI类型由Servlet引擎的配置决定,MIME类通常为“text/html”和“image/gif”。
public String getRealPath(String path)
得到参数path中对应于虚拟路径的真实路径。虚拟路径含有一个Servlet名,后跟Servlet文件名。
public String getServerInfo()
得到正在允许的Servlet引擎的名字和版本。
public Object getAttribute(String name)
得到Servlet引擎的参数name所指定的属性。
public Enumeration getAttributeNames()
得到一个含有属性名的Enumeration对象,这些属性在Servlet上下文中有效。
public void setAttribute(String name, Object object)
设置Servlet上下文中属性的名字,如果指定的名字name已使用,该方法将覆盖老的名字,并将名字捆绑到新的属性中。
public void removeAttribute(String name)
从Servlet上下文中移走给定名字name的属性名。
该类是一个抽象类,它提供了从客户机请求读取二进制数据的一个输入流,对于一些协议,例如HTTP POST和PUT,可以使用ServletInputStream对象读取和发送数据。
public int readLine(byte[] b, int off, int len) throws IOException
读输入流,一次一行。如果读到输入流的尾部,则返回-1。
该类是一个抽象类,它提供了一个输入流,用于向客户机发送数据。
public void print(String s) throws IOException
public void print(boolean b) throws IOException
public void print(char c) throws IOException
public void print(int i) throws IOException
public void print(long l) throws IOException
public void print(float f) throws IOException
public void print(double d) throws IOException
public void println() throws IOException
public void println(String s) throws IOException
public void println(boolean b) throws IOException
public void println(char c) throws IOException
public void println(int i) throws IOException
public void println(long l) throws IOException
public void println(float f) throws IOException
public void println(double d) throws IOException
配置Servet时,首先必须指定Servlet的名字、Servlet的类(如果是JSP,必须指定JSP文件的位置)。另外,可以选择性的给Servlet增加一定的描述,并且指定它在部署时显示的名字,部署时显示的Icon。
上述HelloWorldServlet是这样配置的:
如果要配置的Servlet是一个JSP文件,那么可以这样指定:
一个Servlet的初始化参数可以如下指定:
……
在这个配置里,指定parameter1的参数值为1000。
启动装入优先级通过
这种配置可以保证HelloWorldServlet1在HelloWorldServlet2之前被载入,HelloWorldServlet3则可以在服务器启动后的任何时候载入。
可以给一个Servlet做多个映射,这样,我们可以通过不同的方式来访问这个Servlet。
一个典型的配置如下所示:
通过这些配置,我们可以使用不同的方式来访问这个Servlet。
一个Servlet的安全设置如下所示:
Web应用中的过滤器截取从客户端进来的请求,并做出处理的答复。可以说过滤器是外部进入网站的第一道关,在这个关卡里,可以验证客户是否来自可信的网络,可以对客户提交的数据进行重新编码,可以从系统里获得配置信息,过滤掉客户的某些不应当出现的词汇,验证客户是否已经登陆,验证客户端的浏览器是否支持当前的应用,记录系统日志,等等。
过滤器可以部署多个,这些过滤器组成了一个过滤链,每个过滤器只执行某个特定的操作或者检查,这样请求在达到被访问的目标之前,需要经过这个过滤链,如果由于安全问题不能访问目标资源,那么过滤器就可以把客户端的请求拦截。
开发一个Filter,必须直接或间接实现Filter接口。Filter接口定义了以下的方法:
u init():用于初始化Filter,获得FilterConfig对象。
u destroy():销毁这个Filter。
u doFilter():进行过滤处理。
配置Filter分为两步:一是声明Filter,二是使用Filter。
一个Filter的配置示例如下:
从上面可以看出,一个Filter可以有多个Filter Mapping。一个web组件可以有多个过滤器,这些过滤器按照配置中出现的先后组成一个过滤链。每个到达的请求,经过这些过滤链中的过滤器逐个过滤,如果不通过,请求就被拦截。
Cookie是一组短小的、服务器发送到浏览器的文本信息,当浏览器再次访问相同的网站或域时,该文本信息会原样的传回,服务器根据客户机传回的信息,可以向访问者提供许多方便。比如:识别用户、避免输入用户名和密码、定制一个站点、定向广告等等。
将Cookie发送到客户端,Servlet可以使用相应的名字和值创建一个或多个Cookie:
public Cookie(String name, String value)
构造器的两个参数中,不能使用下列字符:
[ ] ( ) = , “ / ? @ : ;
同时,也不能使用空格。
创建Cookie后,可以使用response.addCookie(cookie)将Cookie插入到相应的头中,在客户端,对于得到请求,可以使用response.getCookies(cookies)从响应头中提起Cookie,得到的是一个Cookie数组。
将Cookie放进响应头可以使用HttpServletResponse类的方法:
public void addCookie(Cookie cookie);
例如:
Cookie userCookie = new Cookie(“user”,”uid 1234” );
response.addCookie(userCookie);
读取来自客户端的Cookie可以使用HttpServletRequest类的方法:
public Cookie[] getCookies();
HTTP是一种“无状态”的协议,这会造成许多问题。为了解决“状态”的维护,通常有四种解决方案:
Ø cookie
Ø URL重写
Ø 隐藏的form域
Ø session
HttpServletRequest有两个方法可以得到Session对象:
public HttpSession getSession(Boolean create);
public HttpSession getSession();
得到与request对象相联系的HttpSession,如果request对象中没有session对象且参数create为true,则创建一个新的。
public Enumeration getAttributeName();
public Object getAttribute(String name);
public void setAttribute(String name, Object value);
JDBC封装了与底层数据库的通信细节,提供了与数据库相关的类和接口,为数据库开发人员提供了一种面向应用的开发平台。
数据库是用于存储和处理数据的工具,数据库是构成了许多公司的重要基础。当前,由于数据库产品缤纷复杂,一个公司里经常出现同时使用多种数据库的现象。使用Java进行数据库开发时通过JDBC技术,可以一致性地访问不同的数据库,不用分别为不同平台的不同数据库编写各自不同的应用程序。
JDBC(Java Data Base Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。同时,JDBC也是个商标名。
JDBC向应用程序开发者提供了独立于数据库的、统一的API,这个API提供了编写的标准,并考虑了所有不同应用程序设计的标准,其关键是一组由驱动程序实现的Java接口。驱动程序负责标准的JDBC调用,当应用程序被移植到不同的平台或数据库系统,应用程序不变,改变的是驱动程序,驱动程序扮演了多层数据库设计中的中间层的角色。如下图所示:
应用程序 |
JDBC |
Oracle 驱动程序 |
Sybase 驱动程序 |
DB2 驱动程序 |
Informix 驱动程序 |
…… 驱动程序 |
Java具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特征,是编写数据库应用程序的杰出语言,所需要的只是Java应用程序与各种不同数据库之间进行对话的方法,而JDBC正是作为此种用途的机制。
随着越来越多的使用Java编程语言,对从Java中便捷的访问数据库的要求也在日益增加。对于服务器端的数据库应用,JDBC往往与RMI、CORBA、JSP或Servlet,EJB等技术混合使用,用于实现多层结构的应用系统。
数据库厂商一般会提供一组API访问数据库。流行的数据库如Oracle、SQL Server、Sybase和Informix都为客户提供了专用的API。有四种类型的数据库驱动程序,它们分别是:
u JDBC-ODBC桥
u 部分Java、部分本机驱动程序
u 中间数据访问服务器
u 纯Java驱动程序
以下分别介绍。
JDBC-ODBC桥是一个JDBC驱动程序,它通过将JDBC操作转换为ODBC操作来实现JDBC操作。对ODBC,它像是通常的应用程序,桥为所有对ODBC可用的数据库实现JDBC。它作为sun.jdbc.odbc包实现,其中包含一个用来访问ODBC的本地库。桥是由Intersolv和JavaSoft联合开发的。由于ODBC被广泛地使用,该桥地优点是让JDBC能够访问几乎所有地数据库。
通过odbc子协议,使用URL打开JDBC连接即可使用桥。建立连接前,必须将桥驱动程序类sun.jdbd.odbc.JdbcOdbcDriver添加到名为jdbc.drivers的java.lang.System属性中,或用Java类加载器将其显示的加载。可以用如下方式显示加载:
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);
加载时,ODBC驱动程序将创建自己的实例,同时在JDBC驱动程序管理器中进行注册。
桥驱动程序使用odbc子协议。该子协议的URL为以下的形式:
jdbc:odbc:
JDBC-ODBC桥在JDBC API和ODBC API之间提供了一个桥梁,这个桥把标准的JDBC调用翻译成对应的ODBC调用,然后通过ODBC库把它们发送到ODBC数据源,如图所示:
JDBC API |
ODBC API |
JDBC-ODBC桥 |
ODBC层 |
数据源 |
Java应用程序 |
这种方式有一个明显的缺点就是效率相对低下,所以不推荐使用这种桥驱动程序,但它可以减少开发人员进行企业开发的麻烦。
这种驱动使用Java实现与数据库厂商专有API的混和形式来提供数据访问。它比前一种方式要快。JDBC驱动将标准的JDBC调用转变为对数据库API的本地调用,该类型的驱动程序是本地部分Java技术性能的本机API驱动程序,如下图所示:
JDBC API |
厂商专有API |
JDBC驱动程序 |
数据源 |
Java应用程序 |
在这种方式里,驱动程序和厂商专有的API必须在每个运行Java应用程序的客户端安装。
现在大多数的数据库厂商都在其数据库产品中提供驱动程序,这种使用方式比前一种有效。
这种方式使用一个中间数据访问服务器,通过这种服务器,它可以把Java客户端连接到多个数据库服务器上,如下图所示:
JDBC API |
JDBC驱动程序 |
本机驱动程序 |
数据源 |
Java应用程序 |
JDBC驱动程序 |
这种方式不需要客户端的数据库驱动,而是使用网络-服务器中间层来访问一个数据库。该类型的驱动程序是网络协议完全Java技术性能的驱动程序,它为Java应用程序提供了一种进行JDBC调用的机制。
使用该类型的驱动程序是平台无关的,并且不需要客户端的安装和管理,因此很适合做Internet的应用。
这种方式使用厂商专有的网络协议把JDBC API调用转换成直接的网络调用,这种方式的本质是使用套接字(Socket)进行编程。纯Java驱动运行在客户端,并且直接访问数据库,因此运行这个模式要使用一个两层的体系,如下图所示:
JDBC API |
JDBC驱动程序 |
数据源 |
Java应用程序 |
该类型的驱动程序是本地协议完全Java技术性能的驱动程序,同时它的使用也比较简单,客户端不需要安装任何中间件或者运行库。现在大部分厂商都提供第四类驱动程序的支持。
Ø 与数据库建立连接
Ø 发送SQL语句
Ø 处理结果
JDBC URL提供了一种标识数据库的方法,可以使相应的驱动程序能识别该数据库并与之建立连接。JDBC的作用是提供某些约定,驱动程序编程员在构造它们的JDBC URL时应该遵循这些约定。
u 由于JDBC URL要与各种不同的驱动程序一起使用,因此,这些约定应非常灵活。首先,它们应允许不同的驱动程序使用不同的方案来命名数据库。
u JDBC URL应允许驱动程序编程员将一切所需的信息编入其中。
u JDBC URL应允许某种程度的间接性。即:JDBC URL可指向逻辑主机或数据库名,而这种逻辑主机或数据库名将由网络命名系统动态的转换为实际的名称。
JDBC URL的标准语法如下所示。它由三部分组成,各部分之间用冒号分割。
jdbd:<子协议>:<子名称>
JDBC URL的三个部分可分解如下:
Ø jdbc:协议,JDBC URL中的协议总是jdbc。
Ø <子协议>:驱动程序名或数据库连接机制的名称。
Ø <子名称>:标识数据库的方法。子名称可以依不同的子协议而变化,使用子名称的目的是为定位数据库提供足够的信息。如果数据库是通过Internet来访问的,则在JDBC URL中应将网络地址作为子名称的一部分包括进去,且必须遵循如下所示的标准URL命名约定:
//主机名:端口/子协议
对于不同的数据库,厂商提供的驱动程序和连接的URL都不同,几个主要的数据库厂商与其对应的驱动程序和连接的URL如下所示:
数据库驱动程序和URL
数据库名 |
驱动程序 |
URL |
MS SQL Server 2000 |
com.microsoft.jdbc.sqlserver.SQLServerDriver |
Jdbc:Microsoft:sqlserver://[ip]:[port];user=[user];password=[password] |
JDBC-ODBC |
sun.jdbc.odbc.JdbcOdbcDriver |
jdbc:odbc:[odbcsource] |
Oracle oci8 |
orcle.jdbc.driver.OracleDriver |
jdbc.oracle:oci8:@[sid] |
Oracle thin Driver |
oracle.jdbc.driver.OracleDriver |
jdbd:oracle:thin:@[ip]:[port]:[sid] |
Cloudscape |
com.cloudscape.core.JDBCDriver |
jdbc:cloudscape:database |
MySQL |
org.gjt.mm.mysql.Driver |
jdbc:mysql:/ip/database,user,password |
子协议odbc是一种特殊情况。它是为指定ODBC风格的数据资源的URL而保留的,并允许在子名称后面指定任意多个属性值。
odbc子协议的完整语法为:
jdbc:odbc:<数据资源名称>[;<属性名>=<属性值>]*
例如:
jdbc:odbc:test
jdbc:odbc:test;UID=uid;PWD=pwd
事务就是将一些SQL语句作为一个整体来执行,要么所有语句全部完成,要么一条语句都不执行。当调用方法commit或rollback时,当前事务即告结束,另一个事务谁即开始。
缺省情况下,新连接处于自动提交模式,也就是说,当执行完语句后,将自动对那个语句调用commit方法。这种情况下,由于每个语句都是被单独提交的,因此,一个事务只由一个语句组成。如果禁用自动提交模式,事务将要等到commit或rollback方法被显示调用时才结束,因此,它将包括上一次调用commit或rollback方法以来所有执行过的语句。对于第二种情况,事务中的所有语句将作为组来提交或还原。
方法commit使SQL语句对数据库所做的任何更改成为永久性的,它还将释放事务持有的全部锁,而方法rollback将放弃那些更改。
每个数据库驱动程序必须实现Driver接口。我们在编程中要连接数据库,必须先装载特定厂商提供的数据库驱动程序(Driver),驱动程序的装载方法如下:
Class.forName(“
对于简单的应用程序,仅需要使用该类的方法getConnection,该方法将建立与数据库的连接。JDBC允许用户调用DriverManager类的方法getDriver、getDrivers和registerDriver及Driver类的方法connect,但在多数情况下,最好让DriverManager类管理建立连接的细节。
DriverManager类是JDBC的管理层,作用于用户和驱动程序之间,用于管理JDBC驱动程序。它跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接,另外,DriverManager类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。
作为初始化的一部分,DriverManager将试图装载系统属性“jdbc.drivers”所指的驱动程序类,这可以让用户定制在它们的应用程序中使用的JDBC驱动程序。例如,在Windows的HOME目录的/.hotjava/properties文件中,可以指定:
Jdbc.drivers=<驱动程序>
另外,程序还可以显示的装载JDBC驱动程序。例如:下述代码用于装载my.sql.Driver
Class.forName(“my.sql.Driver”);
在DriverManager中有一个非常重要的方法,就是getConnection(parameter),通过这个方法可以获得一个连接。
public static synchronized Connection getConnection(String url)
尝试建立一个和给定的URL的数据库连接,调用此方法时,DriverManager将在已注册的驱动中选择恰当的驱动来建立连接。
public static synchronized Connection getConnection(String url, Properties info)
与上类似,不过提供了一些属性,这些属性连接特定的数据库需要,至少包含user和password属性。
public static synchronized Connection getConnection(String url, String user,String password)
连接到指定URL的数据库,使用用户名为user,密码为password。
Connection对象代表与数据库的连接,也就是在已经加载的Driver和数据库之间建立连接。
连接过程包括所执行的SQL语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。
public void close()
关闭到数据库的连接,在使用完连接之后必须关闭,否则连接会保持一段比较长的时间,直到超时。
public void commit()
提交对数据库的更改,使更改生效。这个方法只有调用了setAutoCommit(false)方法后才有效,否则对数据库的更改会自动提交到数据库。
public Statement createStatement()
throws SQLException
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException
public Statement createStatement(int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
创建一个Statement,Statement用于执行SQL语句。
public PreparedStatement prepareStatement(String sql)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int autoGeneratedKeys)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int[] columnIndexes)
throws SQLException
public PreparedStatement prepareStatement(String sql,
String[] columnNames)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int resultSetType,
int resultSetConcurrency)
throws SQLException
public PreparedStatement prepareStatement(String sql,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
public PreparedStatement prepareStatement(String sql,
String[] columnNames)
throws SQLException
使用指定的SQL语句创建一个预处理语句,sql参数中往往包含一个或者多个“?”占位符。
一个Statement对象仅能有一个ResultSet对象。当完成对表的操作之后,必须重新得到另一个Statement对象,才能进行其他表的操作。
Statement对象可以用于执行一个静态的SQL语句,并得到SQL语句执行后的结果。
public boolean execute(String sql)
throws SQLException
运行语句,返回是否有结果集。
public ResultSet executeQuery(String sql)
throws SQLException
运行查询语句,返回ResultSet对象。
public int executeUpdate(String sql)
throws SQLException
运行更新操作,返回更新的行数。
PreparedStatement对象可以代表一个预编译的SQL语句,它从Statement接口继承而来,并与之有如下两方面的不同:
u PreparedStatement实例包含已编译的SQL语句。
u 包含于PreparedStatement对象中的SQL语句可具有一个或多个IN参数。IN参数的值在SQL语句创建时未被指定。相反的,该语句为每个IN参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX方法来提供。
由于PreparedStatement对象已经预编译过,所以其执行速度要快于Statement对象。因此,需要多次执行的SQL语句经常创建为PreparedStatement对象,以提供效率。
作为Statement的子类,PreparedStatement继承了Statement的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代IN参数占位符的值。
另外,Statement接口中的三种方法execute、executeQuery和executeUpdate已经被更改,使之不再需要参数。
所有的setXXX方法的原型如下,其中XXX是与该参数相应的类型。例如,如果参数具有Java类型long,则使用的方法就是setLong。setXXX方法的第一个参数parameterIndex为要设置的参数的序数位置,第二个参数是设置给该参数的值。
public void setXXX(int parameterIndex, XXX x)
throws SQLException
设定指定位置的参数值。
public boolean execute()
throws SQLException
运行语句,返回是否有结果集。
public ResultSet executeQuery()
throws SQLException
运行查询语句,返回ResultSet对象。
public int executeUpdate()
throws SQLException
运行更新操作,返回更新的行数。
ResultSet提供了对数据库中表的访问,一个ResultSet对象通常由执行一个Statement而生成。
ResultSet对象维护了一个光标指针,用来描述当前记录的位置,最初,光标指向第一个记录的位置之前,next方法将光标移到到下一个记录,其方法的原型如下。
boolean next() throws SQLException
getXXX方法得到当前记录的字段的值,可以使用字段名或其索引得到自动的值,一般来说,使用字段的索引效率更高,字段的编号从1开始,1为当前记录的第一个字段,2为第二个字段,以此类推。
在ResultSet接口中,还有一系列的updateXXX方法,可以用来更新当前记录的字段值。
public XXX getXXX(int parameterIndex)
throws SQLException
public XXX getXXX(String columnName)
throws SQLException
获取指定位置或者指定列的查询结果值。
Ø 输入java.sql包
在程序的开头,必须加入下面的代码:
Import java.sql.*;
Ø 声明变量
在代码中,一般要声明三个相关的变量。Stmt用于SELECT语句,pstmt用于UPDATE语句,rs用于SELECT的结果集。
Statement stmt;
PreparedStatement pstmt;
ResultSet rs;
Ø 加载jdbc驱动程序
Class.forName(“
Ø 定义JDBC URL
String url = “jdbc:odbc:oracle:thin@ 10.10.10 .1:db 9” ;
Ø 连接数据库
Connection conn = DriverManager.getConnection(url);
Ø 进行相应的数据操作
根据需要创建Statement或PreparedStatement实例,执行SELECT操作或UPDATE操作。
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");
Ø 关闭数据库连接
conn.close();
Vector data = new Vector();
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;
//注册驱动器
try
{
//加载驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver");
//得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//执行sql语句
stmt = conn.createStatement();
//返回数据集合
rs = stmt.executeQuery("select * from book where bookid=‘ 0007’ ");
while (rs.next())
{
DataObject dataObject = new DataObject();
dataObject.setBookId(rs.getString("bookid"));
dataObject.setBookName(rs.getString("bookname"));
dataObject.setBookNumber(rs.getString("booknumber"));
data.addElement(dataObject);
}
}
catch (SQLException e1)
{
e1.printStackTrace();
}
catch (ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//关闭联接
finally
{
try
{
rs.close();
stmt.close();
conn.close();
}
catch (SQLException e2)
{
e2.printStackTrace();
}
}
String sql;
Connection conn = null;
PreparedStatement ps=null;
//注册驱动器
try
{
//加载驱动程序
Class.forName("oracle.jdbc.driver.OracleDriver");
// 得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
// 执行sql语句
sql="insert into book ( bookid,bookname,booknumber) values (?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "0021");
ps.setString(2, "数据库概论");
ps.setInt(3, 11);
ps.executeUpdate();
}
catch (SQLException e1)
{
e1.printStackTrace();
}
catch (ClassNotFoundException e)
{
System.out.println("Driver not found");
}
// 关闭联接
finally
{
try
{
ps.close();
conn.close();
}
catch (SQLException e2)
{
e2.printStackTrace();
}
}
int state;
Statement stmt = null;
Connection conn = null;
//注册驱动器
try
{
//加载驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver");
//得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//执行sql语句
stmt = conn.createStatement();
//返回数据集合
state=stmt.executeUpdate("update book set booknumber=9 where bookname='java编程思想'");
if(state>0)
{
System.out.println("更新成功");
}
else
{
System.out.println("更新失败");
}
}
catch(SQLException e1)
{
e1.printStackTrace();
}
catch(ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//关闭联接
finally
{
try
{
stmt.close();
conn.close();
}
catch(SQLException e2)
{
e2.printStackTrace();
}
}
boolean state=false;
Statement stmt = null;
Connection conn = null;
//注册驱动器
try
{
//加载驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver");
//得到数据库联接
conn= DriverManager.getConnection("jdbc:oracle:thin:@192.168.110.52:1521:edu","study","study");
//执行sql语句
stmt = conn.createStatement();
//返回数据集合
state=stmt.execute("delete from book where bookid='0007'");
if(state)
{
System.out.println("删除成功");
}
else
{
System.out.println("删除失败");
}
}
catch (SQLException e1)
{
e1.printStackTrace();
}
catch(ClassNotFoundException e)
{
System.out.println("Driver not found");
}
//关闭联接
finally
{
try
{
stmt.close();
conn.close();
}
catch (SQLException e2)
{
e2.printStackTrace();
}
}
在实际应用开发中,特别是在Web应用系统中,如果JSP、Servlet或EJB使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等过程,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁的发生这种数据库操作,系统的性能必然会急剧的下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器中,基本都提供了这项技术,无需自己编程。
数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个vector对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已建立的数据库连接,可以克服上述缺点,极大的节省了系统资源和时间。
数据库连接池的主要操作如下:
Ø 建立数据库连接池对象
Ø 按照事先指定的参数创建初始数量的数据库连接
Ø 对于一个数据库访问请求,直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接,且连接数没有达到最大,创建一个新的数据库连接
Ø 存取数据库
关闭数据库,释放所有数据库连接。
JSP由Sun Microsystems 公司倡导、许多公司参与一起建立的一 种动态网页技术标准,其在动态网页的建设中有其强大而特别的功能。Java Server Pages技术可以让web开发人员和设计人员非常容易的创建和维护动态网页,特别是目前的商业系统
编写一个简单的HTML文件——test.html,在这个HTML中包含一个表单,我们希望能够接收用户在这个表单中填入的字符串,并把它打印出来。
Test.html 的代码:
your string:
|
Test.jsp的代码:
<%@ page contentType="text/html; charset=gb2312" %>
<% String strMyString = (String)request.getParameter(“myString”); out.println(strMyStirng); %>
|
[注:在此我们不做任何有效性验证]
过程描述:
1、用户在test.htm中填写字符串,如“Hello , world!!!”,并点击“submit”按钮
2、浏览器将用户提交的请求(其中包含用户填写的数据信息和目标jsp)发送到Servlet容器
3、如果该jsp没有被编译过的话,Servlet容器会编译它;若jsp已被编译完成,则Servlet容器会调用这个jsp执行
4、执行的结果,通过response反映
具体流程见下图:
点击“submit”提交请求
显示服务器产生的响应 |
编译、执行JSP,生成动态内容 |
发送请求 |
发送响应 |
JSP技术是由Servlet技术发展起来的,自从有了JSP后,在java服务器端编程中普遍采用的就是JSP技术。因为JSP在编写表示页面时远远比Servlet简单,而且不需手工编译——由Servlet容器自动编译。
而Servlet目前更多的是作为控制器来使用。
JSP构建在Servlet上,拥有Servlet所有强大的功能。
Ø 将内容的生成和显示进行分离
在服务器端,JSP引擎解释JSP标识和小脚本,生成所请求的内容,并且将结果以HTML(或者XML)页面的形式发送回浏览器。
Ø 强调可重用的组件
绝大多数JSP页面依赖于可重用的,跨平台的组件(JavaBeans或者Enterprise JavaBeansTM组件)来执行应用程序所要求的更为复杂的处理。
Ø 采用标识简化页面开发
Web页面开发人员不会都是熟悉脚本语言的编程人员。Java Server Page技术封装了许多功 能,这些功能是在易用的、与JSP相关的XML标识中进行动态内容生成所需要的。
与CGI
• 后台实现逻辑是基于Java Component的,具有跨平台的特点。
• 将应用逻辑与页面表现分离,使得应用逻辑能够最大程度得到复用,从而提高开发效率。
• 运行比CGI方式高,尤其对于数据库访问时,提供了连接池缓冲机制,使运行所需资源最小。
• 安全,由于后台是完全基于Java技术的,安全性由Java的安全机制予以保障
• 内置支持XML,使用XML从而使页面具有更强的表现力和减少编程工作量。
首先,我们来看一个典型的JSP文件:
<%@ page contentType="text/html; charset=gb2312" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ page import=“com.iss.scm.bizlogic.outbound.OutboundBiz” %> 这是一个典型的JSP,它包含了JSP中常用的元素 --> <%! String getDate() { return new java.util.Date().toString(); } %>
<% out.println(getDate()); %>
|
从上面的JSP文件中,可以总结出五类元素:
Ø 注释
Ø 模版元素
Ø 脚本元素
Ø 指令元素
Ø 动作元素
下面,我们分别介绍这些元素:
1、注释
JSP中有多种注释,有JSP自带的注释规范,也有HTML/XML的注释规范。
u HTML/XML注释
在客户端显示一个注释。
JSP语法:
例子:
描述:这种注释和HTML中很像,唯一不同之处就是,可以在这个注释中用表达式。
u 隐藏注释
写在JSP程序中,但不发给客户。
JSP语法:<%-- comment --%>
例子:
<%-- 下面是使用表达式的例子 --%>
<%= getDate() %>
描述:用隐藏注释标记的字符会在JSP编译时被忽略。JSP编译器不会对<%--和--%>之间的语句进行编译,它不会显示在客户的浏览器中,也不会在源代码中看到。
u Scriptlets中的注释
Scriptlets中包含的是java代码,所以java中的注释规范在Scriptlets中也能使用。
语法:// 或 /* … */
例子:
/ *
* 这是一个注释
*/
2、模版元素
模版元素是指JSP的静态HTML或XML内容,通常由美工或网页工程师来负责完成。模版元素是网页的框架,它影响页面的结构和美观程度。在JSP编译时,这些模版元素会被编译到Servlet里面。比如被编译时,会用以下代码替代:
out.write(“”);
3、脚本元素
脚本元素主要包括:
u 声明(Declaration)
声明是在JSP程序中声明合法的变量和方法,如:
<%! String getDate()
{
return new java.util.Date().toLocaleString();
}
int iCount = -1;
%>
u 表达式(Expression)
表达式就是位于<%=和%>之间的代码,如:
<%! String strBookName = “Design Pattern” %>
… …
<%= strBookName %>
或
<%= getDate() %>
u Scriptlets
Scriptlets位于<%和%>之间,它们是合法的java代码,如:
<% String[] strEmp= {‘aaa’,’bbb’,’ccc’}; %> <% for(int i = 0;i < 3;i++) { %> <% } %>
|
4、指令元素
JSP中有三种指令元素,它们是:
u 页面(page)指令
语法:
<%@ page
[language=”java”]
[import=”{package.class|package.*},…”]
[contentType=”TYPE;charset=CHARSET”]
[session=”True | False”]
[buffer=”none | 8kb | sizekb”]
[autoFlush=”True | False”]
[isThreadSafe=”True | false”]
[info=”text”]
[errorPage=”relativeURL”]
[isErrorPage=”true | false”]
[extends=”package.class”]
[isELIgnored=”true | false”]
[pageEncoding=”peinfo”]
%>
例子:<%@ page contentType="text/html; charset=gb2312" %>
u include指令
例子:<%@ include file=“/slof/outbound/outbound.jsp” %>
u taglib指令
例子:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
…
id="sendStockNO" name="outboundForm" property= "outboundEntity.sendStockNO"/>
5、动作元素
动作可能影响当前的输出流或者用来创建、使用、改变对象。Jsp规范包含标准的动作类型,新的动作类型由taglib指令定义。动作遵循XML语法标准。
动作元素在请求处理阶段起作用。
语法:
或者
…
JSP规范定义了一系列的标准动作,它们用jsp作为前缀,这些动作元素有:
下面我们介绍几个典型的动作元素
n
jsp:param操作被用来以“名-值”对的形式为其他标签提供附加信息。
使用方式:
n
jsp:include操作允许在请求时间内在现成的jsp页面里面包含静态或者动态资源。被包含的对象只有对JspWriter对象的访问权,并且它不能设置头或者Cookie。如果页面输出是缓冲的,那么缓冲区的刷新要优于包含的刷新。另外,此指令在运行上效率比<%@ page include %>指令的效率低。
使用方式:
n
jsp:forward操作是运行将请求转发到另一个JSP、Servlet或者静态资源文件。请求被转向到的资源必须位于同JSP发送请求相同的上下文环境之中。每当遇到此操作时,就停止执行当前的JSP,转而执行被转发的资源。
使用方式:
n
该标签用来在JSP页面中创建一个bean实例,并指定它的名字以及作用范围。它保证对象在标签指定的范围内可以使用。
使用方式:
其中,id是一个大小写相关的名字,用来表示这个实例;scope表示此实例可以使用的范围;typeSpec可以是以下四者之一:
class = “className”
class = “className” type = “typeName”
beanName = “beanName” type = “typeName”
type = “typeName”
scope解释:
page
能够在JSP文件以及此文件中的所有静态包含文件中使用Bean,直到页面执行完毕向客户端发回响应或转到另一个文件为止。
request
在请求的范围内有效
session
在会话范围内有效,所有的JSP都可以访问
application
在应用服务器启动时创建,所有的JSP都可以访问
n
此操作和useBean一起协作,用来设置Bean的属性。该标签将会使用Bean给定的setter方法,为Bean中的属性赋值。
使用方法:
其中,name的值为在useBean标签中设置的id;propertyDetails可以是下面几个中的一个:
property=”*”
property=”propertyName” param=”parameterName”
property=”propertyName”
property=”propertyName” value=”propertyValue”
n
此操作和useBean一起协作,用来获取Bean的属性的值。该标签将会使用Bean给定的getter方法。
使用方法:
n
jsp:plugin用来产生客户端浏览器的特别标签,一般用它来插入Applet或者JavaBean。当JSP文件被编译把结果发送到浏览器时,
使用方法:
type=”bean | applet”
code=”classFileName”
codebase=”classFileDirectoryName”
[name=”instanceName”]
[archive=”URIToArchive,…”]
[align=”bottom | top | middle | left | right”]
[height=”displayPixels”]
[width=”displayPixels”]
[hspace=”leftRightPixels”]
[vspace=”topBottomPixels”]
[nspluginurl=”URLToPlugin”]
[iepluginurl=”URLToPlugin”]
[
[
]
[
>
其中,type指示将被执行的插件对象的类型;code指示java类文件的名称;codebase指示运行java类的目录或指向这个目录的路径;name指示Bean或Applet的实例的名称;archive指示以逗号分隔的路径名列表;
n
用于java插件不能启动时,显示给用户的一段文字。
使用方式:
…
…
out对象被封装成javax.servlet.jsp.JspWriter接口。主要用来向客户端输出数据。
request对象代表请求对象,它被封装成HttpServletRequest接口。
通过getParameter方法可得到request参数,通过GET、POST、HEAD等方法可以得到request的类型,通过Cookies、Referer可得到请求的http头。
来自客户端的请求经Servlet容器处理后,由request对象进行封装。它作为jspService()方法的一个参数由容器传递给jsp页面。
response被包装成HttpServletResponse接口。它封装了JSP产生的响应,然后被发送到客户端以响应客户的请求。由容器生成,作为jspService()方法的参数被传入jsp。
pageContext对象被封装成javax.servlet.jsp.PageContext接口,它为jsp页面包装成页面的上下文,管理对属于jsp中特殊可见部分中已命名对象的访问。它的创建和初始化都是由容器来完成的,jsp页面里面可以直接使用pageContext对象的句柄。
session对象用来保存每个用户信息,以便跟踪每个用户的操作状态。session信息保存在容器里,session的ID保存在客户机的Cookie中。一般情况下,用户首次登陆系统时容器会给此用户分配一个唯一标识的session id,这个id用于区分其他的用户,当用户退出系统时,这个session就会自动消失。
Session被封装成HttpSession接口。
application对象为多个应用程序保存信息,对于一个容器而言,每个用户都共同使用一个application对象,服务器启动后,就会自动创建application对象,这个对象会一直保持,直到服务器关闭为止。
config对象被封装成javax.servlet.ServletConfig接口,他表示Servlet的配置。当一个Servlet初始化时,容器把某些信息通过此对象传递给这个Servlet。
page对象是java.lang.Object类的一个实例。它指的是jsp实现类的实例,也就是说它是jsp本身,通过这个可以对它进行访问。
exception对象是java.lang.Throwable类的一个实例。它指的是运行时的异常,也就是被调用的错误页面的结果,只有在错误页面才可以使用。
要使用exception对象,必须在page指令指定isErrorPage=“true”,在出错页面中,使用<%= exception.getMessage() %>来获取错误信息。
讨论为什么使用Struts,先让我们来看一下传统单页方法的优缺点:
优点:
只要简单设置应用服务器,然后就可以开始编写代码。不需用核心文件来保持同步,也不需要冗长的库初始配置。由每个单独的 JSP 页面或 servlet 来指定连接。
缺点:
• HTML 和 Java 混合在一起
JSP 文件的编写者必须既是网页设计者,又是 Java 开发者。其结果通常要么是很糟的 Java 代码,要么是难看的网页,有时甚至 Java 代码和网页都很糟。
• Java 和 JavaScript 的不足
随着网页逐渐变大,很容易想到实现一些 JavaScript。当网页中出现 JavaScript 时,这种脚本就可能与 Java 代码产生混淆。
• 强耦合
更改业务逻辑或数据可能牵涉相关的每个网页。
• 内嵌的流程逻辑
要理解应用程序的整个流程,您必须浏览所有网页。试想一下拥有 1000 个页面的错综复杂的逻辑。
• 调试困难
除了很糟的外观之外,HTML 标记、Java 代码和 JavaScript 代码都集中在一个网页中还使调试变得相当困难。
• 页面代码美观
在很大的网页中,编码样式看起来杂乱无章。即使有彩色语法显示,阅读和理解这些代码仍然比较困难。
• 在应用程序的不同部分中重用表示很困难
一定程度上,可以使用
• 公共输入和数据处理任务枯燥且重复
错误处理是普通 JSP 页面的常见问题。而且,必须手工填入表单值及手工检索请求对象中的那些值,这是一件既耗时又枯燥的工作。
简单来说,Design Patten 就是一个常用的方案。 在我们的开发过程中,经常会遇到一些相同或者相近的问题,每次我们都会去寻找一个新的解决方法,为了节省时间提高效率,我们提供一些能够解决这些常见问题的,被证实可行的方案,构成一个统一的资源库。
一个Design Patten描述了一个被证实可行的方案。这些方案非常普通,是有完整定义的最常用的模式。 这些模式可以被重用,有良好的伸缩性。
MVC 通过将问题分为三个类别来帮助解决单一模块方法所遇到的某些问题,这就Model(模型)、View视图)、Controller(控制器) 。
其中:
Ø Model(模型)
模型包含了应用程序的核心,它封装了应用程序的数据结构和事务逻辑,集中体现了应用程序的状态。有时候它仅包含了状态信息,因为它并不了解视窗或控制器的信息(任何时候它对视图或控制器都一无所知 )。
JavaBean及 EJB 很适合扮演这个角色,因为它能够处理绝大部分事务逻辑和数据结构。它能够与数据库或文件系统进行交互,承担维护应用程序数据的责任。
Ø View(视图)
视图实现模块的外观,它是应用程序的外在表现。它可以访问模型的数据, 却不了解模型的情况, 同时它也不了解控制器的情况。当模型发生改变时, 视图会得到通知,它可以访问模型的数据,但不能改变这些数据 。
Ø Controller(控制器)
控制器对用户的输入做出反应并且将模型和视图联系在一起。servlet能够接受客户端的HTTP请求,并且根据需要创建所需的JavaBean或者EJB,然后将产生的变化通知给视窗 。
• 代码分离,显示与逻辑解耦
• 验证处理
• 流程控制
• 更新应用程序的状态
MVC模式的结构
客户机和服务器的无状态连接
– 由于HTTP本身缺乏状态信息,客户端必须对服务器进行再查询才能发现由输入所造成的改变,在这种情况下,控制器不能将应用程序的改变通知视图。
实现视图所用的技术与实现模型或控制器的技术不同。
– 也可以使用Java代码来生成所有的Html,但这样会产生更多问题。
上图表示的是Struts的MVC。
其中
Client Browser是客户发送请求的地方。
Controller Servlet接收来自浏览器的请求,并决定将这个请求发往何处;在Struts中,控制器是以 servlet 实现的一个命令设计模式。
Business Logic更新模型的状态,并帮助控制应用程序的流程。就 Struts 而言,这是通过作为实际业务逻辑“瘦”包装的 Action 类完成的。
Model表示应用程序的状态。业务对象更新应用程序的状态。ActionForm bean 在会话级或请求级表示模型的状态,而不是在持久级。JSP 文件使用 JSP 标记读取来自 ActionForm bean 的信息。
View就是一个 JSP 文件。其中没有流程逻辑,没有业务逻辑,也没有模型信息, 只有标记。标记是使 Struts 有别于其他框架(如 Velocity)的因素之一。
Struts中所使用的组件有:ActionServlet、ActionClass、ActionForm、ActionMapping、ActionForward、ActionError、Struts标记库。
Ø ActionServlet
Struts框架中的控制器;为org.apache.struts.action.ActionServlet,该类扩展了javax.servlet.http.HttpServlet,是Struts框架的核心。
ActionServlet (Command) 创建并使用Action、ActionForm 和 ActionForward。在初始化时读取 struts-config.xml 文件配置。
基本功能:
• 截获用户的Http请求
• 把这个请求映射到相应的Action类,如果这是此类收到的第一个请求, 将初始化实例并缓存
• 创建或发现一个ActionForm bean实例(看配置文件是否定义),然后将请求过程移植到bean.
• 调用Action实例的perform()方法并将ActioForm bean,Action Mapping对象,request和response对象传给它
• perform返回一个ActionForword对象,此对象连接到相应的jsp页面
在web.xml中的配置:
|
Ø ActionClass
Struts框架中封装事务逻辑。
所有Action类都扩展org.apache.struts.action.Action类,并且覆盖类中定义的perform()方法。 Action 类是业务逻辑的一个包装 ,用途是将 HttpServletRequest 转换为业务逻辑。 当事件进展到这一步时,输入表单数据(或 HTML 表单数据)已被从请求流中提取出来并转移到 ActionForm 类中。 ActionForm 类被传递到Actino类,这一切都是自动完成的。扩展 Action 类时要注意简洁。Action 类应该控制应用程序的流程,而不应该控制应用程序的逻辑。通过将业务逻辑放在单独的包或 EJB 中,我们就可以提供更大的灵活性和可重用性。
Action类必须以“线程安全”的方式进行编程,因为控制器会令多个同时发生的请求共享同一个实例,相应的,在设计Action类时就需要注意以下几点:
• 不能使用实例或静态变量存储特定请求的状态信息,它们会在同一个操作中共享跨越请求的全局资源
• 如果要访问的资源(如JavaBean和会话变量)在并行访问时需要进行保护,那么访问就要进行同步
Action在struts-config.xml的配置:
type="org.apache.struts.webapp.example.CautionSaveAction" name="cautionsaveForm" scope="session" input="/S63.jsp">
|
Ø ActionForm
用于封装数据,显示或向ActionClass的方法传送数据。
ActionForm类扩展org.apache.struts.action.ActionForm类,程序开发人员创建的bean能够包含额外的属性。 框架假设用户在应用程序中为每个表单都创建了一个ActionForm bean,维护 Web 应用程序的会话状态。如果使用动态ActionForm类,则只需在struts-config.xml中进行相应的配置,框架可自动生成ActionForm bean。典型的ActionFrom bean只有属性的设置与读取方法(getXXX),而没有实现事务逻辑的方法。只有简单的输入检查逻辑,使用的目的是为了存储用户在相关表单中输入的最新数据,以便可以将同一网页进行再生,同时提供一组错误信息,这样就可以让用户修改不正确的输入数据
Struts 框架将对ActionForm执行以下操作 :
• 检查 UserActionForm 是否存在;如果不存在,它将创建该类的一个实例。
• 将使用 HttpServletRequest 中相应的域设置 ActionForm 的状态。没有太多讨厌的 request.getParameter() 调用。
• Struts 框架在将 ActionForm传递给业务包装 Action 之前将更新它的状态。
• 在将它传递给 Action 类之前,Struts 还会对 ActionForm调用 validation() 方法进行表单状态验证,但不提倡这种做法。
• 可在会话级维护 ActionForm
ActionForm在struts-config.xml中的配置:
|
Ø ActionMapping
帮助控制器将请求映射到操作。
struts-config.xml 配置信息被转换为一组 ActionMapping,而后者又被放入 ActionMappings 容器中。 ActionMapping对象帮助进行框架内部的流程控制,它们可将特定请求URI映射到特定Action类,并且将Action类与ActionForm bean相关联。ActionServlet (Command) 通过 perform() 方法将 ActionMapping 传递给 Action 类。这样就使 Action 可访问用于控制流程的信息。Action将使用ActionMapping的findForward()方法,此方法返回一个指定名称的ActionForward,这样Action就完成了本地转发。
Ø ActionForward
用来指示操作转移的对象。
Ø ActionError
用来存储和回收错误。
ActionError 是 Struts 保持错误列表的方式,封装了单个错误消息。 ActionError类从不独立进行错误处理,它们总是被存储在ActionErrors对象中,View 可以使用标记访问这些类 。 如下所示:
Ø Strtus标记库
用于View层显示,减轻表示层的开发工作。
JSP视图组件所使用的 struts 标记库由四类标记组成:
• Bean标记:用来在JSP页中管理bean
• Logic标记:用来在JSP页中控制流程
• HTML标记:用来生成HTML标记,在表单中显示数据,使用会话ID对URL进行编程
• 模板标记:使用动态模板构造普通格式的页
下面,分别为大家介绍这四类标记。
1.Bean标记
• 创建和复制bean的标记
• 脚本变量定义标记
• 显示Bean属性
• 消息标记和国际化
java.util数据包中定义的Locale和ResourceBundle
2.Logic标记
• 实体常数比较标记
• 属性存在标记
• 匹配标记
–
–
• 重复标记
class=ItemBody height=20 width="10%">
• 转发和重定向标记
转发标记
重定向标记
3.HTML标记
• 显示表单元素和输入控件
表单标记
表单内容…
其他还有按钮和取消标记、 复位和提交标记 、文本和文本区标记 、检查框和复选框标记 、文件标记 、单选钮标记 、隐藏标记 、密码标记 、选择标记等等,可查http://apache.jakarta.com网站。
• 显示错误信息的标记
–
• 其他HTML标记
–
–
–
–
4.模板标记
• 动态模板是模块化WEB页布局设计的强大手段
• 插入标记
• 放置标记
• 获得标记
Struts的工作流程见下图
1、应用服务启动时读取struts-config.xml配置文件
2、根据strus-config.xml中的配置项生成Action Mappings;Mapping中的信息包括:path、action、form bean、forwards。
3、当web浏览器端发过来请求时,由充当Controller的ActionServlet获取请求信息
4、ActionServlet根据获取的request查找path,决定使用哪个form bean和哪个action;在此,我们假设使用form bean2和action2
5、ActionServlet判断是否存在form bean实例和action实例,若存在则重用,若不存在则创建
6、ActionServlet将控制权转交给Action,由Action来调用业务逻辑层的服务;form bean作为参数传入到action中,由action从中获取数据并传送到业务逻辑层
7、Action从业务逻辑层获取服务之后,返回ActionForward,控制权重新回到ActionServlet手中
8、ActionServlet根据ActionForward中的内容,查找Mappings,确定下一步显示哪个.jsp页面
9、应用服务器容器编译jsp文件,当遇到需要读取数据的tag时,调用form bean2的getter方法
10、 编译jsp为html并发送给浏览器
– 在web.xml中声明ActionServlet,并且将它配置成启动时进行加载
– 指定ActionServlet的启动配置文件如:struts-config.xml
– 指定ActionServlet的启动模板配置文件(如果需要的话)如:tiles-defs.xml
– 配置Http请求所要映射的servlet名称
– 配置所有系统及用户自定义的标签库
– 全局转发
– ActionMapping类
– ActionForm bean
– JDBC数据源
– 指定资源文件的位置
– 配置需要的Plug in
如:
添加校验模块,并指定其配置文件validator.xml、validator-rules.xml的位置
Struts.jar
tiles.jar
jakarta-regexp-1.2.jar
commons-validator.jar
commons-beanutils.jar
commons-collections.jar
commons-dbcp.jar
commons-digester.jar
commons-logging.jar
commons-pool.jar
commons-services.jar
在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦、浪费时间的。Hibernate 是一个面向Java 环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL 的关系模型结构中去。
Hibernate 不仅仅管理Java 类到数据库表的映射,还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL 和JDBC 处理数据的时间。Hibernate 的目标是对于开发者通常的数据持久化相关的编程任务,解放其中的95%。
对Hibernate 非常高层的概览:
这幅图展示了Hibernate 使用数据库和配置文件数据来为应用程序提供持久化服务(和持久化的对象)。
Hibernate 是比较复杂的,提供了好几种不同的运行方式。我们展示一下两种极端情况。
应用程序自己提供JDBC 连接,并且自行管理事务。这种方式使用了Hibernate API 的一个最小子集。
对于应用程序来说,所有的底层JDBC/JTA API 都被抽象了,Hibernate 会替你照管所有的细节。
n SessionFactory (net.sf.hibernate.SessionFactory)
对编译过的映射文件的一个线程安全的,不可变的缓存快照。它是Session 的工厂,是ConnectionProvider 的客户。可能持有事务之间重用的数据的缓存。
n 会话Session (net.sf.hibernate.Session)
单线程,生命期短促的对象,代表应用程序和持久化层之间的一次对话。封装了一个JDBC 连接,也是Transaction 的工厂。持有持久化对象的缓存。
n 持久化对象(Persistent Object)及其集合(Collection)
生命期短促的单线程的对象,包含了持久化状态和商业功能。它们可能是普通的JavaBeans,唯一特别的是他们现在从属于且仅从属于一个Session。
n 临时对象(Transient Object)及其集合(Collection)
目前没有从属于一个Session 的持久化类的实例。他们可能是刚刚被程序实例化,还没有来得及被持久化,或者是被一个已经关闭的Session 所实例化的。
n 事务,Transaction (net.sf.hibernate.Transaction)
(可选) 单线程,生命期短促的对象,应用程序用它来表示一批工作的原子操作。是底层的JDBC,JTA 或者CORBA 事务的抽象。一个Session 可能跨越多个Transaction事务。
n ConnectionProvider(net.sf.hibernate.connection.ConnectionProvider)
(可选)JDBC 连接的工厂和池。从底层的Datasource 或者 DriverManager 抽象而来。对应用程序不可见。
n TransactionFactory (net.sf.hibernate.TransactionFactory)
(可选)事务实例的工厂。对应用程序不可见。
正如其名,Configuration 类负责管理Hibernate 的配置信息。Hibernate 运行时需要获取一些底层实现的基本信息,其中几个关键属性包括:
Ø 数据库URL
Ø 数据库用户
Ø 数据库用户密码
Ø 数据库JDBC驱动类
Ø 数据库dialect,用于对特定数据库提供支持,其中包含了针对特定数据库特性的实现,如Hibernate数据类型到特定数据库数据类型的映射等。
使用Hibernate 必须首先提供这些基础信息以完成初始化工作,为后继操作做好准备。这些属性在hibernate配置文件(hibernate.cfg.xml hibernate.properties)中加以设定(参见后面“Hibernate配置”中的示例配置文件内容)。
当我们调用:
Configuration config = new Configuration().configure();
时,Hibernate会自动在当前的CLASSPATH 中搜寻hibernate.cfg.xml文件并将其读取到内存中作为后继操作的基础配置。Configuration 类一般只有在获取SessionFactory时需要涉及,当获取SessionFactory 之后,由于配置信息已经由Hibernate 维护并绑定在返回的SessionFactory之上,因此一般情况下无需再对其进行操作。
我们也可以指定配置文件名,如果不希望使用默认的hibernate.cfg.xml文件作为配置文件的话:
File file = new File("c://sample//myhibernate.xml");
Configuration config = new Configuration().configure(file);
SessionFactory 负责创建Session 实例。我们可以通过Configuation 实例构建SessionFactory:
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Configuration实例config会根据当前的配置信息,构造SessionFactory实例并返回。SessionFactory 一旦构造完毕,即被赋予特定的配置信息。也就是说,之后config 的任何变更将不会影响到已经创建的SessionFactory 实例(sessionFactory)。如果需要使用基于改动后的config 实例的SessionFactory,需要从config 重新构建一个SessionFactory实例。
Session是持久层操作的基础,相当于JDBC中的Connection。
Session实例通过SessionFactory实例构建:
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
之后我们就可以调用Session所提供的save、find、flush等方法完成持久层操作:
Ø Find
String hql= " from TUser where name='Erica'";
List userList = session.find(hql);
Ø Save
TUser user = new TUser();
user.setName("Emma");
session.save(user);
session.flush();
最后调用Session.flush方法强制数据库同步,这里即强制Hibernate将user实例立即同步到数据库中。如果在事务中则不需要flush方法,在事务提交的时候,hibernate自动会执行flush方法,另外当Session关闭时,也会自动执行flush方法。
Transaction 是对实际事务实现的一个抽象,这些实现包括JDBC的事务、JTA 中的UserTransaction、甚至可以是CORBA 事务。为开发者提过一个统一事务的操作界面,使得项目可以在不同的环境和容器之间方便地移值。
使用方法:
Transaction tx = session.beginTransaction();
······
tx.commit();
当执行事务提交方法时,Hibernate会发现持久数据的变化,并根据这些变化同步更新到数据库中。必须注意的是,在一个事务之中不允许出现另外一个事务,也即:事务不运行嵌套。
一个典型的hibernate.cfg.xml配置文件如下:
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
—- SessionFactory 配置 -->
—- 数据库URL -->
jdbc:mysql://localhost/sample
—- 数据库JDBC驱动 -->
org.gjt.mm.mysql.Driver
—- 数据库用户名 -->
User
—- 数据库用户密码 -->
Mypass
net.sf.hibernate.dialect.MySQLDialect
—- 是否将运行期生成的SQL输出到日志以供调试 -->
True
—- 是否使用数据库外连接 -->
True
—- 事务管理类型,这里我们使用JDBC Transaction -->
net.sf.hibernate.transaction.JDBCTransactionFactory
—映射文件配置,注意配置文件名必须包含其相对于根的全路径 -->
一个典型的hibernate.properties配置文件如下:
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class org.gjt.mm.mysql.Driver
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql:///sample
hibernate.connection.username user
hibernate.connection.password mypass
Hibernate中对象和关系数据库之间的映射是用一个XML 文档(XML document)来定义的。这个映射文档被设计为易读的,并且可以手工修改。映射语言是以Java 为中心的,意味着映射是按照持久化类的定义来创建的,而非表的定义。
虽然很多Hibernate 用户选择手工定义XML 映射文档,也有一些工具来生成映射文档,包括XDoclet,Middlegen 和AndroMDA.
一个映射的例子如下所示: