39、数据库连接池与Servlet/JSP开发全解析

数据库连接池与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特定字符进行过滤。例如,必须将 < > " & 替换为 &lt; &gt; &quot; &amp; 。可以使用 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应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值