数据库连接池与Servlet/JSP开发全解析
1. 数据库连接池案例研究
在数据库操作中,连接池是一种优化数据库访问的重要技术。下面通过几个不同的Servlet示例来研究连接池的性能。
1.1 不同连接方式的性能对比
| 条件 | 平均时间 |
|---|---|
| 快速局域网连接数据库,初始10个连接,最大50个连接 (ConnectionPoolServlet) | 1.8秒 |
| 快速局域网连接数据库,复用单个连接 (ConnectionPoolServlet2) | 2.0秒 |
| 快速局域网连接数据库,不使用连接池 (ConnectionPoolServlet3) | 2.8秒 |
从这个表格可以看出,使用连接池能够显著提高数据库访问的性能,尤其是在多连接的情况下。
1.2 ConnectionPoolServlet代码分析
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
public class ConnectionPoolServlet extends HttpServlet {
private ConnectionPool connectionPool;
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String table;
try {
String query =
"SELECT firstname, lastname " +
" FROM employees WHERE salary > 70000";
Connection connection = connectionPool.getConnection();
DBResults results =
DatabaseUtilities.getQueryResults(connection,
query, false);
connectionPool.free(connection);
table = results.toHTMLTable("#FFAD00");
} catch(Exception e) {
table = "Error: " + e;
}
response.setContentType("text/html");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
PrintWriter out = response.getWriter();
String title = "Connection Pool Test";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<CENTER>\n" +
table + "\n" +
"</CENTER>\n</BODY></HTML>");
}
public void init() {
int vendor = DriverUtilities.SYBASE;
String driver = DriverUtilities.getDriver(vendor);
String host = "dbhost2.apl.jhu.edu";
String dbName = "605741";
String url = DriverUtilities.makeURL(host, dbName, vendor);
String username = "hall";
String password = "xxxx";
try {
connectionPool =
new ConnectionPool(driver, url, username, password,
initialConnections(),
maxConnections(),
true);
} catch(SQLException sqle) {
System.err.println("Error making pool: " + sqle);
getServletContext().log("Error making pool: " + sqle);
connectionPool = null;
}
}
public void destroy() {
connectionPool.closeAllConnections();
}
protected int initialConnections() {
return(10);
}
protected int maxConnections() {
return(50);
}
}
- init方法 :在Servlet初始化时创建连接池。
- doGet方法 :处理HTTP GET请求,从连接池获取连接,执行数据库查询,最后释放连接。
- destroy方法 :在Servlet销毁时关闭所有连接。
1.3 ConnectionPoolServlet2和ConnectionPoolServlet3
-
ConnectionPoolServlet2
:是
ConnectionPoolServlet的变体,只使用一个连接,将所有请求排队处理。
package coreservlets;
public class ConnectionPoolServlet2
extends ConnectionPoolServlet {
protected int initialConnections() {
return(1);
}
protected int maxConnections() {
return(1);
}
}
- ConnectionPoolServlet3 :不使用连接池,每次请求都创建新的连接。
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
public class ConnectionPoolServlet3 extends HttpServlet {
private String url, username, password;
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String table;
String query =
"SELECT firstname, lastname " +
" FROM employees WHERE salary > 70000";
try {
Connection connection =
DriverManager.getConnection(url, username, password);
DBResults results =
DatabaseUtilities.getQueryResults(connection,
query, true);
table = results.toHTMLTable("#FFAD00");
} catch(Exception e) {
table = "Exception: " + e;
}
response.setContentType("text/html");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
PrintWriter out = response.getWriter();
String title = "Connection Pool Test (*No* Pooling)";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<CENTER>\n" +
table + "\n" +
"</CENTER>\n</BODY></HTML>");
}
public void init() {
try {
int vendor = DriverUtilities.SYBASE;
String driver = DriverUtilities.getDriver(vendor);
Class.forName(driver);
String host = "dbhost2.apl.jhu.edu";
String dbName = "605741";
url = DriverUtilities.makeURL(host, dbName, vendor);
username = "hall";
password = "xxxx";
} catch(ClassNotFoundException e) {
System.err.println("Error initializing: " + e);
getServletContext().log("Error initializing: " + e);
}
}
}
2. 共享连接池
在实际应用中,多个Servlet可能会访问同一个数据库,这时可以考虑共享连接池。主要有两种方法:使用Servlet上下文和使用单例类。
2.1 使用Servlet上下文共享连接池
Servlet上下文是一个在服务器上所有Servlet(或在支持Web应用的服务器中,在一个Web应用内)共享的对象。可以通过以下步骤实现连接池的共享:
ServletContext context = getServletContext();
ConnectionPool bookPool =
(ConnectionPool)context.getAttribute("book-pool");
if (bookPool == null) {
bookPool = new ConnectionPool(...);
context.setAttribute("book-pool", bookPool);
}
-
步骤说明
:
1. 获取Servlet上下文对象。
2. 尝试从上下文中获取连接池。
3. 如果连接池不存在,则创建一个新的连接池并存储在上下文中。
2.2 使用单例类共享连接池
单例类是一种只允许创建一个实例的类。以下是一个单例类
BookPool
的示例:
public class BookPool extends ConnectionPool {
private static BookPool pool = null;
private BookPool(...) {
super(...);
...
}
public static synchronized BookPool getInstance() {
if (pool == null) {
pool = new BookPool(...);
}
return(pool);
}
}
-
步骤说明
:
1. 定义一个静态变量pool来存储单例实例。
2. 使用私有构造函数确保只能在类内部创建实例。
3. 提供一个静态方法getInstance来获取单例实例,如果实例不存在则创建一个新的实例。
3. Servlet和JSP概述
Servlet和JSP是Java Web开发中常用的技术,它们各有优势。
3.1 Servlet的优点
- 高效:使用线程而不是操作系统进程,一个Servlet副本可持久化。
- 方便:提供了许多高级实用工具。
- 强大:可以与服务器交互,共享数据,使用连接池和持久化。
- 可移植:几乎可以在所有操作系统和服务器上运行。
- 安全:没有shell逃逸和缓冲区溢出问题。
- 低成本:如果服务器不捆绑Servlet支持,可以使用低成本的插件。
3.2 JSP的优点
- 与ASP相比:动态部分使用更好的语言,可移植性强。
- 与PHP相比:动态部分使用更好的语言。
- 与纯Servlet相比:更方便创建HTML。
- 与SSI相比:更灵活和强大。
- 与JavaScript相比:在服务器端运行,语言更丰富。
- 与静态HTML相比:具有动态特性。
4. 免费的Servlet和JSP软件
以下是一些免费的Servlet和JSP开发软件:
- Tomcat: http://jakarta.apache.org/
- JSWDK: http://java.sun.com/products/servlet/download.html
- JRun: http://www.allaire.com/products/jrun/
- ServletExec: http://newatlanta.com/
- LiteWebServer: http://www.gefionsoftware.com/
- Java Web Server: http://www.sun.com/software/jwebserver/try/
5. 文档和编译路径
5.1 文档
- http://java.sun.com/products/jsp/download.html
- http://java.sun.com/products/servlet/2.2/javadoc/
- http://www.java.sun.com/j2ee/j2sdkee/techdocs/api/
5.2 Servlet编译的CLASSPATH条目
-
通常在
install_dir/lib/servlet.jar中的Servlet类。 -
通常在
install_dir/lib/jsp.jar、...jspengine.jar或...jasper.jar中的JSP类。 -
Servlet安装目录的顶级目录(例如
install_dir/webpages/WEB-INF/classes)。
6. 不同服务器的标准目录
| 服务器 | 标准Servlet类位置 | 备用Servlet类位置 | JAR文件位置 |
|---|---|---|---|
| Tomcat 3.0 | install_dir/webpages/WEB-INF/classes | install_dir/classes | install_dir/lib |
| Tomcat 3.1 | install_dir/webapps/ROOT/WEB-INF/classes | install_dir/classes | install_dir/lib |
| JSWDK 1.0.1 | install_dir/webpages/WEB-INF/servlets | install_dir/classes | install_dir/lib |
| Java Web Server 2.0 | install_dir/servlets | install_dir/classes | install_dir/lib |
7. 第一个Servlet
以下是一个简单的Servlet示例
HelloWWW.java
:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWWW extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" +
"<BODY>\n" +
"<H1>Hello WWW</H1>\n" +
"</BODY></HTML>");
}
}
- 安装Servlet :
- 放入第A.1节中所示的Servlet目录。
- 放入与包对应的子目录。
- 调用Servlet :
- http://host/servlet/ServletName
- http://host/servlet/package.ServletName
- 由服务器特定定制定义的任意位置。
8. Servlet生命周期
Servlet的生命周期包括以下几个方法:
-
init
:在Servlet首次加载时执行一次,可用于读取初始化参数。
-
service
:服务器为每个请求在新线程中调用,将请求分派到
doGet
、
doPost
等方法,不要重写此方法。
-
doGet
:处理GET请求,可重写以提供自定义行为。
-
doPost
:处理POST请求,可重写以提供自定义行为,如果希望GET和POST行为相同,可以在此调用
doGet
。
-
doPut
、
doTrace
、
doDelete
等:处理不常见的HTTP请求。
-
destroy
:在服务器删除Servlet实例时调用。
-
getLastModified
:当客户端因缓存副本发送条件GET请求时由服务器调用。
-
SingleThreadModel
:如果实现此接口,服务器将避免并发调用。
9. 处理客户端请求:表单数据
在Servlet中,可以使用
HttpServletRequest
对象来读取表单数据。
9.1 读取参数
-
request.getParameter:返回第一个值。 -
request.getParameterValues:返回所有值的数组。
9.2 示例Servlet
以下是一个读取三个请求参数的Servlet示例
ThreeParams.java
:
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ThreeParams extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Reading Three Request Parameters";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<UL>\n" +
" <LI><B>param1</B>: "
+ request.getParameter("param1") + "\n" +
" <LI><B>param2</B>: "
+ request.getParameter("param2") + "\n" +
" <LI><B>param3</B>: "
+ request.getParameter("param3") + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
}
9.3 示例表单
以下是一个收集三个参数的表单示例
ThreeParamsForm.html
:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Collecting Three Parameters</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<H1 ALIGN="CENTER">Collecting Three Parameters</H1>
<FORM ACTION="/servlet/coreservlets.ThreeParams">
First Parameter: <INPUT TYPE="TEXT" NAME="param1"><BR>
Second Parameter: <INPUT TYPE="TEXT" NAME="param2"><BR>
Third Parameter: <INPUT TYPE="TEXT" NAME="param3"><BR>
<CENTER>
<INPUT TYPE="SUBMIT">
</CENTER>
</FORM>
</BODY>
</HTML>
10. 处理客户端请求:HTTP请求头
HttpServletRequest
对象提供了许多方法来读取HTTP请求头。
10.1 读取请求头的方法
-
getHeader:返回任意头的值,如果头不在请求中则返回null。 -
getHeaders:返回请求中所有出现的头的值(仅Servlet 2.2支持)。 -
getHeaderNames:返回当前请求中所有头的名称。 -
getDateHeader:读取表示日期的头并将其转换为Java日期格式(自1970年以来的毫秒数)。 -
getIntHeader:读取表示整数的头并将其转换为int,如果头不在请求中则返回 -1,对于非整数会抛出NumberFormatException。 -
getCookies:返回Cookie对象的数组,如果没有cookie则数组长度为0。 -
getContentLength:返回Content-Length头的值作为int,如果未知则返回 -1。 -
getContentType:如果请求中存在Content-Type头则返回其值,否则返回null。 -
getAuthType:返回认证类型(”BASIC”、”DIGEST”、”SSL”或null)。 -
getRemoteUser:如果使用了认证则返回用户名,否则返回null。
10.2 其他请求信息
-
getMethod:返回HTTP请求方法(”GET”、”POST”、”HEAD”等)。 -
getRequestURI:返回URL中主机和端口之后的部分。 -
getProtocol:返回HTTP版本(通常是”HTTP/1.0”或”HTTP/1.1”)。
10.3 常见的HTTP 1.1请求头
-
Accept:浏览器可以处理的MIME类型。 -
Accept-Encoding:浏览器可以处理的编码(例如gzip或compress)。 -
Authorization:用于密码保护页面的用户标识,通常不使用HTTP认证,而是使用HTML表单发送用户名/密码,然后由Servlet将信息存储在会话对象中。 -
Connection:在HTTP 1.0中,keep-alive表示浏览器可以处理持久连接;在HTTP 1.1中,持久连接是默认的。Servlet应该使用setContentLength设置Content-Length以支持持久连接。
通过以上内容,我们详细介绍了数据库连接池的使用、Servlet和JSP的开发以及如何处理客户端请求等方面的知识,希望对Java Web开发人员有所帮助。
数据库连接池与Servlet/JSP开发全解析
11. 过滤HTML特定字符
在处理用户输入时,为了防止HTML注入攻击,需要对HTML特定字符进行过滤。例如,必须将
<
、
>
、
"
、
&
替换为
<
、
>
、
"
和
&
。可以使用
ServletUtilities.filter(htmlString)
方法进行此替换。
12. 常见HTTP 1.1请求头的应用场景
下面通过流程图展示常见HTTP 1.1请求头在实际应用中的处理流程:
graph TD
A[客户端发送请求] --> B{是否包含Accept头}
B -- 是 --> C[服务器根据Accept头返回合适的MIME类型响应]
B -- 否 --> D{是否包含Accept-Encoding头}
D -- 是 --> E[服务器根据Accept-Encoding头进行内容编码]
D -- 否 --> F{是否包含Authorization头}
F -- 是 --> G[服务器验证用户身份]
F -- 否 --> H{是否包含Connection头}
H -- 是 --> I[服务器根据Connection头处理连接方式]
H -- 否 --> J[服务器正常处理请求并返回响应]
C --> J
E --> J
G --> J
13. 数据库连接池的优化建议
- 合理设置初始连接数和最大连接数 :根据实际应用的并发访问量和数据库性能,合理调整连接池的初始连接数和最大连接数。例如,如果应用的并发访问量较小,可以适当减小初始连接数和最大连接数,以减少资源占用;如果并发访问量较大,则需要增加连接数。
- 定期清理空闲连接 :长时间空闲的连接会占用数据库资源,可以设置连接池的空闲连接清理机制,定期清理空闲时间超过一定阈值的连接。
- 监控连接池状态 :通过监控连接池的使用情况,如当前连接数、空闲连接数、等待连接的请求数等,及时发现并解决连接池可能出现的问题。
14. Servlet和JSP开发的最佳实践
- 分离业务逻辑和视图 :在Servlet中处理业务逻辑,在JSP中负责页面展示,这样可以提高代码的可维护性和可测试性。
- 使用过滤器 :过滤器可以在请求到达Servlet之前或响应返回客户端之前进行预处理或后处理,如字符编码过滤、权限验证等。
- 合理使用会话管理 :会话管理可以用于跟踪用户的状态,但要注意会话的过期时间和存储的数据量,避免会话数据过多导致内存占用过高。
15. 代码示例总结
以下是本文中涉及的主要代码示例总结:
| 代码示例 | 功能描述 |
| — | — |
|
ConnectionPoolServlet.java
| 使用连接池进行数据库查询的Servlet示例 |
|
ConnectionPoolServlet2.java
| 只使用一个连接的Servlet示例 |
|
ConnectionPoolServlet3.java
| 不使用连接池的Servlet示例 |
|
HelloWWW.java
| 简单的Servlet示例,返回一个HTML页面 |
|
ThreeParams.java
| 读取三个请求参数的Servlet示例 |
|
ThreeParamsForm.html
| 收集三个参数的表单示例 |
16. 总结
本文全面介绍了数据库连接池的使用、Servlet和JSP的开发以及客户端请求的处理等方面的知识。通过对不同连接方式的性能对比,我们了解到连接池在提高数据库访问性能方面的重要性。同时,介绍了共享连接池的两种方法,以及Servlet和JSP的优点和开发过程中的一些实用技巧。
在实际开发中,我们可以根据具体需求选择合适的技术和方法,合理使用连接池、Servlet和JSP,提高应用的性能、可维护性和安全性。希望本文对Java Web开发人员有所帮助,能够在实际项目中灵活运用所学知识,开发出高质量的Web应用。
超级会员免费看
955

被折叠的 条评论
为什么被折叠?



