简体中文 繁體中文 English Deutsch 한국 사람 بالعربية TÜRKÇE português คนไทย Français Japanese

站内搜索

搜索
AI 风月

活动公告

03-01 22:34
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

掌握Servlet核心技术从Hello World开始构建你的第一个Java Web应用

3万

主题

586

科技点

3万

积分

白金月票

碾压王

积分
32701

立华奏

发表于 2025-9-2 00:30:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. Servlet技术概述

Servlet(Server Applet)是Java EE规范中用于扩展Web服务器功能的组件,它运行在服务器端,用于处理客户端请求并生成响应。作为Java Web开发的核心技术,Servlet提供了一种基于组件的、平台无关的方法来构建Web应用程序。

Servlet的主要优势包括:

• 高性能:Servlet在初始化后常驻内存,能够快速响应请求
• 可扩展性:可以轻松地与其他Java技术(如JDBC、JNDI等)集成
• 安全性:受益于Java的安全特性
• 平台无关性:一次编写,到处运行

2. 开发环境准备

在开始Servlet开发之前,我们需要准备以下开发环境:

2.1 安装JDK

确保你的系统上安装了Java Development Kit (JDK) 8或更高版本。你可以通过以下命令检查Java版本:
  1. java -version
复制代码

2.2 安装IDE

推荐使用以下IDE之一进行Servlet开发:

• Eclipse IDE for Enterprise Java Developers
• IntelliJ IDEA Ultimate Edition
• Apache NetBeans

2.3 安装Web服务器

Servlet需要一个Web容器(也称为Servlet容器)来运行。最常用的选择是Apache Tomcat,你可以从官方网站下载并安装。

2.4 配置Maven

Maven是一个项目管理工具,可以帮助我们管理项目依赖。确保你的IDE中已配置Maven。

3. 创建第一个Servlet “Hello World”项目

让我们通过创建一个简单的”Hello World”Servlet项目来开始我们的学习之旅。

3.1 创建Maven项目

在IDE中创建一个新的Maven项目,并选择maven-archetype-webapp模板。

3.2 配置pom.xml

在pom.xml文件中添加Servlet API依赖:
  1. <dependencies>
  2.     <!-- Servlet API -->
  3.     <dependency>
  4.         <groupId>javax.servlet</groupId>
  5.         <artifactId>javax.servlet-api</artifactId>
  6.         <version>4.0.1</version>
  7.         <scope>provided</scope>
  8.     </dependency>
  9. </dependencies>
  10. <build>
  11.     <plugins>
  12.         <plugin>
  13.             <groupId>org.apache.maven.plugins</groupId>
  14.             <artifactId>maven-compiler-plugin</artifactId>
  15.             <version>3.8.1</version>
  16.             <configuration>
  17.                 <source>1.8</source>
  18.                 <target>1.8</target>
  19.             </configuration>
  20.         </plugin>
  21.     </plugins>
  22. </build>
复制代码

3.3 创建HelloWorld Servlet

在src/main/java目录下创建一个包,例如com.example.servlet,然后在该包下创建HelloWorldServlet.java文件:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public class HelloWorldServlet extends HttpServlet {
  9.    
  10.     @Override
  11.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  12.             throws ServletException, IOException {
  13.         
  14.         // 设置响应内容类型
  15.         response.setContentType("text/html");
  16.         
  17.         // 获取PrintWriter对象
  18.         PrintWriter out = response.getWriter();
  19.         
  20.         // 编写HTML响应
  21.         out.println("<html>");
  22.         out.println("<head>");
  23.         out.println("<title>Hello World Servlet</title>");
  24.         out.println("</head>");
  25.         out.println("<body>");
  26.         out.println("<h1>Hello World!</h1>");
  27.         out.println("<p>This is my first Servlet application.</p>");
  28.         out.println("</body>");
  29.         out.println("</html>");
  30.     }
  31. }
复制代码

3.4 配置web.xml

在src/main/webapp/WEB-INF目录下创建或编辑web.xml文件,添加Servlet映射:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  5.          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  6.          version="4.0">
  7.    
  8.     <servlet>
  9.         <servlet-name>HelloWorldServlet</servlet-name>
  10.         <servlet-class>com.example.servlet.HelloWorldServlet</servlet-class>
  11.     </servlet>
  12.    
  13.     <servlet-mapping>
  14.         <servlet-name>HelloWorldServlet</servlet-name>
  15.         <url-pattern>/hello</url-pattern>
  16.     </servlet-mapping>
  17.    
  18. </web-app>
复制代码

3.5 部署和运行

将项目部署到Tomcat服务器并启动。然后,在浏览器中访问以下URL:
  1. http://localhost:8080/your-webapp-context/hello
复制代码

(注意:your-webapp-context是你的Web应用程序的上下文路径,通常是项目名称)

你将看到浏览器显示”Hello World!“消息,这标志着你的第一个Servlet应用程序成功运行!

4. Servlet生命周期详解

Servlet生命周期指的是Servlet从创建到销毁的整个过程。理解Servlet生命周期对于开发高效、可靠的Web应用程序至关重要。

Servlet生命周期主要包括三个阶段:

4.1 初始化阶段

当Servlet第一次被请求时,或者服务器启动时(如果配置了load-on-startup),Servlet容器会创建Servlet实例并调用其init()方法进行初始化。
  1. @Override
  2. public void init() throws ServletException {
  3.     // 初始化代码
  4.     System.out.println("Servlet is being initialized");
  5. }
复制代码

init()方法只会在Servlet生命周期中被调用一次,可以在这里执行一些一次性的初始化操作,如加载配置文件、建立数据库连接等。

4.2 处理请求阶段

初始化完成后,Servlet可以处理客户端请求。对于每个请求,Servlet容器会调用Servlet的service()方法:
  1. @Override
  2. protected void service(HttpServletRequest request, HttpServletResponse response)
  3.         throws ServletException, IOException {
  4.     // 处理请求的代码
  5. }
复制代码

在HttpServlet中,service()方法会根据请求类型(GET、POST、PUT等)调用相应的doGet()、doPost()、doPut()等方法。我们通常只需要重写这些方法,而不是直接重写service()方法。

4.3 销毁阶段

当Servlet容器需要卸载Servlet时(如服务器关闭或应用被移除),会调用Servlet的destroy()方法:
  1. @Override
  2. public void destroy() {
  3.     // 清理代码
  4.     System.out.println("Servlet is being destroyed");
  5. }
复制代码

destroy()方法也只会在Servlet生命周期中被调用一次,可以在这里执行一些清理操作,如关闭数据库连接、释放资源等。

4.4 完整的生命周期示例

下面是一个完整的Servlet生命周期示例:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public class LifeCycleServlet extends HttpServlet {
  9.    
  10.     private int visitCount = 0;
  11.    
  12.     @Override
  13.     public void init() throws ServletException {
  14.         super.init();
  15.         System.out.println("LifeCycleServlet: 初始化");
  16.         visitCount = 0;
  17.     }
  18.    
  19.     @Override
  20.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  21.             throws ServletException, IOException {
  22.         
  23.         response.setContentType("text/html");
  24.         PrintWriter out = response.getWriter();
  25.         
  26.         visitCount++;
  27.         
  28.         out.println("<html>");
  29.         out.println("<head>");
  30.         out.println("<title>Servlet生命周期示例</title>");
  31.         out.println("</head>");
  32.         out.println("<body>");
  33.         out.println("<h1>Servlet生命周期示例</h1>");
  34.         out.println("<p>这是第 " + visitCount + " 次访问此Servlet</p>");
  35.         out.println("</body>");
  36.         out.println("</html>");
  37.         
  38.         System.out.println("LifeCycleServlet: 处理GET请求,访问次数: " + visitCount);
  39.     }
  40.    
  41.     @Override
  42.     public void destroy() {
  43.         System.out.println("LifeCycleServlet: 销毁,总访问次数: " + visitCount);
  44.         super.destroy();
  45.     }
  46. }
复制代码

5. Servlet API核心接口和类

Servlet API提供了一系列接口和类,用于开发Servlet应用程序。了解这些核心接口和类对于掌握Servlet技术至关重要。

5.1 核心接口

javax.servlet.Servlet接口是所有Servlet必须直接或间接实现的核心接口。它定义了以下方法:
  1. public interface Servlet {
  2.     void init(ServletConfig config) throws ServletException;
  3.     ServletConfig getServletConfig();
  4.     void service(ServletRequest request, ServletResponse response)
  5.         throws ServletException, IOException;
  6.     String getServletInfo();
  7.     void destroy();
  8. }
复制代码

javax.servlet.ServletConfig接口用于获取Servlet的配置信息:
  1. public interface ServletConfig {
  2.     String getServletName();
  3.     ServletContext getServletContext();
  4.     String getInitParameter(String name);
  5.     Enumeration<String> getInitParameterNames();
  6. }
复制代码

javax.servlet.ServletContext接口表示Servlet应用程序的环境:
  1. public interface ServletContext {
  2.     Object getAttribute(String name);
  3.     Enumeration<String> getAttributeNames();
  4.     void setAttribute(String name, Object object);
  5.     void removeAttribute(String name);
  6.     String getInitParameter(String name);
  7.     Enumeration<String> getInitParameterNames();
  8.     String getServletContextName();
  9.     String getContextPath();
  10.     // 还有许多其他方法...
  11. }
复制代码

javax.servlet.ServletRequest接口封装了客户端请求的信息:
  1. public interface ServletRequest {
  2.     Object getAttribute(String name);
  3.     Enumeration<String> getAttributeNames();
  4.     String getCharacterEncoding();
  5.     void setCharacterEncoding(String env)
  6.         throws UnsupportedEncodingException;
  7.     int getContentLength();
  8.     String getContentType();
  9.     ServletInputStream getInputStream() throws IOException;
  10.     String getParameter(String name);
  11.     Enumeration<String> getParameterNames();
  12.     String[] getParameterValues(String name);
  13.     Map<String, String[]> getParameterMap();
  14.     String getProtocol();
  15.     String getScheme();
  16.     String getServerName();
  17.     int getServerPort();
  18.     BufferedReader getReader() throws IOException;
  19.     String getRemoteAddr();
  20.     String getRemoteHost();
  21.     void setAttribute(String name, Object o);
  22.     void removeAttribute(String name);
  23.     Locale getLocale();
  24.     Enumeration<Locale> getLocales();
  25.     boolean isSecure();
  26.     RequestDispatcher getRequestDispatcher(String path);
  27.     String getRealPath(String path);
  28.     int getRemotePort();
  29.     String getLocalName();
  30.     String getLocalAddr();
  31.     int getLocalPort();
  32.     ServletContext getServletContext();
  33.     AsyncContext startAsync() throws IllegalStateException;
  34.     AsyncContext startAsync(ServletRequest servletRequest,
  35.                            ServletResponse servletResponse)
  36.         throws IllegalStateException;
  37.     boolean isAsyncStarted();
  38.     boolean isAsyncSupported();
  39.     AsyncContext getAsyncContext();
  40.     DispatcherType getDispatcherType();
  41. }
复制代码

javax.servlet.ServletResponse接口用于向客户端发送响应:
  1. public interface ServletResponse {
  2.     String getCharacterEncoding();
  3.     String getContentType();
  4.     ServletOutputStream getOutputStream() throws IOException;
  5.     PrintWriter getWriter() throws IOException;
  6.     void setCharacterEncoding(String charset);
  7.     void setContentLength(int len);
  8.     void setContentLengthLong(long len);
  9.     void setContentType(String type);
  10.     void setBufferSize(int size);
  11.     int getBufferSize();
  12.     void flushBuffer() throws IOException;
  13.     void resetBuffer();
  14.     boolean isCommitted();
  15.     void reset();
  16.     void setLocale(Locale loc);
  17.     Locale getLocale();
  18. }
复制代码

5.2 核心类

javax.servlet.GenericServlet是一个抽象类,实现了Servlet接口和ServletConfig接口,提供了Servlet接口中除service()方法外的所有方法的默认实现:
  1. public abstract class GenericServlet
  2.     implements Servlet, ServletConfig, java.io.Serializable {
  3.    
  4.     private transient ServletConfig config;
  5.    
  6.     public GenericServlet() {}
  7.    
  8.     @Override
  9.     public void destroy() {
  10.         // 默认实现为空
  11.     }
  12.    
  13.     @Override
  14.     public String getInitParameter(String name) {
  15.         return getServletConfig().getInitParameter(name);
  16.     }
  17.    
  18.     @Override
  19.     public Enumeration<String> getInitParameterNames() {
  20.         return getServletConfig().getInitParameterNames();
  21.     }
  22.    
  23.     @Override
  24.     public ServletConfig getServletConfig() {
  25.         return config;
  26.     }
  27.    
  28.     @Override
  29.     public ServletContext getServletContext() {
  30.         return getServletConfig().getServletContext();
  31.     }
  32.    
  33.     @Override
  34.     public String getServletInfo() {
  35.         return "";
  36.     }
  37.    
  38.     @Override
  39.     public void init(ServletConfig config) throws ServletException {
  40.         this.config = config;
  41.         this.init();
  42.     }
  43.    
  44.     public void init() throws ServletException {
  45.         // 默认实现为空
  46.     }
  47.    
  48.     @Override
  49.     public abstract void service(ServletRequest req, ServletResponse res)
  50.         throws ServletException, IOException;
  51.    
  52.     @Override
  53.     public String getServletName() {
  54.         return config.getServletName();
  55.     }
  56.    
  57.     public void log(String msg) {
  58.         getServletContext().log(getServletName() + ": " + msg);
  59.     }
  60.    
  61.     public void log(String message, Throwable t) {
  62.         getServletContext().log(getServletName() + ": " + message, t);
  63.     }
  64. }
复制代码

javax.servlet.http.HttpServlet是一个抽象类,继承自GenericServlet类,专门用于处理HTTP请求:
  1. public abstract class HttpServlet extends GenericServlet {
  2.    
  3.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  4.         throws ServletException, IOException {
  5.         
  6.         String protocol = req.getProtocol();
  7.         String msg = lStrings.getString("http.method_get_not_supported");
  8.         if (protocol.endsWith("1.1")) {
  9.             resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
  10.         } else {
  11.             resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
  12.         }
  13.     }
  14.    
  15.     protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  16.         throws ServletException, IOException {
  17.         
  18.         String protocol = req.getProtocol();
  19.         String msg = lStrings.getString("http.method_post_not_supported");
  20.         if (protocol.endsWith("1.1")) {
  21.             resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
  22.         } else {
  23.             resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
  24.         }
  25.     }
  26.    
  27.     // 其他doXxx方法...
  28.    
  29.     @Override
  30.     public void service(ServletRequest req, ServletResponse res)
  31.         throws ServletException, IOException {
  32.         
  33.         HttpServletRequest request;
  34.         HttpServletResponse response;
  35.         
  36.         try {
  37.             request = (HttpServletRequest) req;
  38.             response = (HttpServletResponse) res;
  39.         } catch (ClassCastException e) {
  40.             throw new ServletException("non-HTTP request or response");
  41.         }
  42.         
  43.         service(request, response);
  44.     }
  45.    
  46.     protected void service(HttpServletRequest req, HttpServletResponse resp)
  47.         throws ServletException, IOException {
  48.         
  49.         String method = req.getMethod();
  50.         
  51.         if (method.equals(METHOD_GET)) {
  52.             long lastModified = getLastModified(req);
  53.             if (lastModified == -1) {
  54.                 doGet(req, resp);
  55.             } else {
  56.                 long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
  57.                 if (ifModifiedSince < lastModified) {
  58.                     maybeSetLastModified(resp, lastModified);
  59.                     doGet(req, resp);
  60.                 } else {
  61.                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
  62.                 }
  63.             }
  64.         } else if (method.equals(METHOD_HEAD)) {
  65.             long lastModified = getLastModified(req);
  66.             maybeSetLastModified(resp, lastModified);
  67.             doHead(req, resp);
  68.         } else if (method.equals(METHOD_POST)) {
  69.             doPost(req, resp);
  70.         } else if (method.equals(METHOD_PUT)) {
  71.             doPut(req, resp);
  72.         } else if (method.equals(METHOD_DELETE)) {
  73.             doDelete(req, resp);
  74.         } else if (method.equals(METHOD_OPTIONS)) {
  75.             doOptions(req, resp);
  76.         } else if (method.equals(METHOD_TRACE)) {
  77.             doTrace(req, resp);
  78.         } else {
  79.             String errMsg = lStrings.getString("http.method_not_implemented");
  80.             Object[] errArgs = new Object[1];
  81.             errArgs[0] = method;
  82.             errMsg = MessageFormat.format(errMsg, errArgs);
  83.             
  84.             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
  85.         }
  86.     }
  87.    
  88.     // 其他方法...
  89. }
复制代码

6. 处理HTTP请求和响应

在Servlet开发中,处理HTTP请求和响应是最常见的任务。HttpServletRequest和HttpServletResponse接口提供了处理HTTP请求和响应的方法。

6.1 获取请求参数

HttpServletRequest提供了多种方法来获取客户端提交的参数:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public class ParameterServlet extends HttpServlet {
  9.    
  10.     @Override
  11.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  12.             throws ServletException, IOException {
  13.         
  14.         response.setContentType("text/html");
  15.         PrintWriter out = response.getWriter();
  16.         
  17.         out.println("<html>");
  18.         out.println("<head>");
  19.         out.println("<title>请求参数示例</title>");
  20.         out.println("</head>");
  21.         out.println("<body>");
  22.         out.println("<h1>请求参数示例</h1>");
  23.         out.println("<form method='post'>");
  24.         out.println("姓名: <input type='text' name='name'><br>");
  25.         out.println("年龄: <input type='text' name='age'><br>");
  26.         out.println("性别: ");
  27.         out.println("<input type='radio' name='gender' value='male'>男");
  28.         out.println("<input type='radio' name='gender' value='female'>女<br>");
  29.         out.println("爱好: ");
  30.         out.println("<input type='checkbox' name='hobbies' value='reading'>阅读");
  31.         out.println("<input type='checkbox' name='hobbies' value='sports'>运动");
  32.         out.println("<input type='checkbox' name='hobbies' value='music'>音乐<br>");
  33.         out.println("<input type='submit' value='提交'>");
  34.         out.println("</form>");
  35.         out.println("</body>");
  36.         out.println("</html>");
  37.     }
  38.    
  39.     @Override
  40.     protected void doPost(HttpServletRequest request, HttpServletResponse response)
  41.             throws ServletException, IOException {
  42.         
  43.         // 设置请求和响应的字符编码,防止中文乱码
  44.         request.setCharacterEncoding("UTF-8");
  45.         response.setContentType("text/html;charset=UTF-8");
  46.         
  47.         PrintWriter out = response.getWriter();
  48.         
  49.         // 获取单个参数值
  50.         String name = request.getParameter("name");
  51.         String age = request.getParameter("age");
  52.         String gender = request.getParameter("gender");
  53.         
  54.         // 获取多个参数值(如复选框)
  55.         String[] hobbies = request.getParameterValues("hobbies");
  56.         
  57.         out.println("<html>");
  58.         out.println("<head>");
  59.         out.println("<title>请求参数结果</title>");
  60.         out.println("</head>");
  61.         out.println("<body>");
  62.         out.println("<h1>请求参数结果</h1>");
  63.         out.println("<p>姓名: " + name + "</p>");
  64.         out.println("<p>年龄: " + age + "</p>");
  65.         out.println("<p>性别: " + gender + "</p>");
  66.         out.println("<p>爱好: ");
  67.         if (hobbies != null) {
  68.             for (String hobby : hobbies) {
  69.                 out.println(hobby + " ");
  70.             }
  71.         }
  72.         out.println("</p>");
  73.         out.println("<a href='parameter'>返回</a>");
  74.         out.println("</body>");
  75.         out.println("</html>");
  76.     }
  77. }
复制代码

6.2 处理HTTP头信息

HTTP头信息包含了请求和响应的元数据。HttpServletRequest和HttpServletResponse提供了处理头信息的方法:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import java.util.Enumeration;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. public class HeaderServlet extends HttpServlet {
  10.    
  11.     @Override
  12.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  13.             throws ServletException, IOException {
  14.         
  15.         response.setContentType("text/html");
  16.         PrintWriter out = response.getWriter();
  17.         
  18.         // 设置响应头
  19.         response.setHeader("Custom-Header", "CustomValue");
  20.         response.addHeader("Another-Header", "AnotherValue");
  21.         
  22.         out.println("<html>");
  23.         out.println("<head>");
  24.         out.println("<title>HTTP头信息示例</title>");
  25.         out.println("</head>");
  26.         out.println("<body>");
  27.         out.println("<h1>HTTP头信息示例</h1>");
  28.         
  29.         // 显示请求头信息
  30.         out.println("<h2>请求头信息:</h2>");
  31.         out.println("<table border='1'>");
  32.         out.println("<tr><th>头名称</th><th>头值</th></tr>");
  33.         
  34.         Enumeration<String> headerNames = request.getHeaderNames();
  35.         while (headerNames.hasMoreElements()) {
  36.             String headerName = headerNames.nextElement();
  37.             String headerValue = request.getHeader(headerName);
  38.             out.println("<tr><td>" + headerName + "</td><td>" + headerValue + "</td></tr>");
  39.         }
  40.         out.println("</table>");
  41.         
  42.         // 显示特定请求头信息
  43.         out.println("<h2>特定请求头信息:</h2>");
  44.         out.println("<p>User-Agent: " + request.getHeader("User-Agent") + "</p>");
  45.         out.println("<p>Accept: " + request.getHeader("Accept") + "</p>");
  46.         out.println("<p>Accept-Language: " + request.getHeader("Accept-Language") + "</p>");
  47.         
  48.         out.println("</body>");
  49.         out.println("</html>");
  50.     }
  51. }
复制代码

6.3 请求转发和重定向

在Web应用中,经常需要将请求转发到其他资源或者重定向到其他URL。

请求转发是在服务器端进行的,客户端浏览器不知道发生了转发,URL不会改变。
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.RequestDispatcher;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public class ForwardServlet extends HttpServlet {
  9.    
  10.     @Override
  11.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  12.             throws ServletException, IOException {
  13.         
  14.         // 设置请求属性
  15.         request.setAttribute("message", "这是通过请求转发传递的消息");
  16.         
  17.         // 获取请求分发器
  18.         RequestDispatcher dispatcher = request.getRequestDispatcher("/target.jsp");
  19.         
  20.         // 转发请求
  21.         dispatcher.forward(request, response);
  22.     }
  23. }
复制代码

重定向是告诉客户端浏览器去请求另一个URL,URL会改变。
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. public class RedirectServlet extends HttpServlet {
  8.    
  9.     @Override
  10.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  11.             throws ServletException, IOException {
  12.         
  13.         // 设置会话属性
  14.         request.getSession().setAttribute("message", "这是通过重定向传递的消息");
  15.         
  16.         // 重定向到目标URL
  17.         response.sendRedirect(request.getContextPath() + "/target.jsp");
  18.     }
  19. }
复制代码

7. 使用注解配置Servlet

从Servlet 3.0开始,我们可以使用注解来配置Servlet,而无需在web.xml中进行配置。这种方式简化了开发过程。

7.1 @WebServlet注解

@WebServlet注解用于声明一个Servlet:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. @WebServlet(
  10.     name = "AnnotationServlet",
  11.     urlPatterns = {"/annotation", "/anno"},
  12.     loadOnStartup = 1,
  13.     initParams = {
  14.         @WebInitParam(name = "adminEmail", value = "admin@example.com"),
  15.         @WebInitParam(name = "adminPhone", value = "123456789")
  16.     }
  17. )
  18. public class AnnotationServlet extends HttpServlet {
  19.    
  20.     @Override
  21.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  22.             throws ServletException, IOException {
  23.         
  24.         response.setContentType("text/html");
  25.         PrintWriter out = response.getWriter();
  26.         
  27.         // 获取初始化参数
  28.         String adminEmail = getInitParameter("adminEmail");
  29.         String adminPhone = getInitParameter("adminPhone");
  30.         
  31.         out.println("<html>");
  32.         out.println("<head>");
  33.         out.println("<title>注解配置Servlet示例</title>");
  34.         out.println("</head>");
  35.         out.println("<body>");
  36.         out.println("<h1>注解配置Servlet示例</h1>");
  37.         out.println("<p>管理员邮箱: " + adminEmail + "</p>");
  38.         out.println("<p>管理员电话: " + adminPhone + "</p>");
  39.         out.println("</body>");
  40.         out.println("</html>");
  41.     }
  42. }
复制代码

7.2 @WebFilter注解

@WebFilter注解用于声明一个过滤器:
  1. package com.example.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. import javax.servlet.annotation.WebInitParam;
  11. @WebFilter(
  12.     urlPatterns = {"/*"},
  13.     initParams = {
  14.         @WebInitParam(name = "encoding", value = "UTF-8")
  15.     }
  16. )
  17. public class EncodingFilter implements Filter {
  18.    
  19.     private String encoding;
  20.    
  21.     @Override
  22.     public void init(FilterConfig filterConfig) throws ServletException {
  23.         encoding = filterConfig.getInitParameter("encoding");
  24.     }
  25.    
  26.     @Override
  27.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  28.             throws IOException, ServletException {
  29.         
  30.         request.setCharacterEncoding(encoding);
  31.         response.setCharacterEncoding(encoding);
  32.         
  33.         chain.doFilter(request, response);
  34.     }
  35.    
  36.     @Override
  37.     public void destroy() {
  38.         // 清理代码
  39.     }
  40. }
复制代码

7.3 @WebListener注解

@WebListener注解用于声明一个监听器:
  1. package com.example.listener;
  2. import javax.servlet.ServletContextEvent;
  3. import javax.servlet.ServletContextListener;
  4. import javax.servlet.annotation.WebListener;
  5. import javax.servlet.http.HttpSessionEvent;
  6. import javax.servlet.http.HttpSessionListener;
  7. @WebListener
  8. public class AppListener implements ServletContextListener, HttpSessionListener {
  9.    
  10.     @Override
  11.     public void contextInitialized(ServletContextEvent sce) {
  12.         System.out.println("应用程序启动");
  13.         sce.getServletContext().setAttribute("appStartTime", System.currentTimeMillis());
  14.     }
  15.    
  16.     @Override
  17.     public void contextDestroyed(ServletContextEvent sce) {
  18.         System.out.println("应用程序关闭");
  19.     }
  20.    
  21.     @Override
  22.     public void sessionCreated(HttpSessionEvent se) {
  23.         System.out.println("会话创建: " + se.getSession().getId());
  24.     }
  25.    
  26.     @Override
  27.     public void sessionDestroyed(HttpSessionEvent se) {
  28.         System.out.println("会话销毁: " + se.getSession().getId());
  29.     }
  30. }
复制代码

8. 会话管理

在Web应用中,HTTP协议是无状态的,这意味着服务器不会记录之前请求的任何信息。为了在多个请求之间保持用户状态,Servlet提供了会话管理机制。

8.1 HttpSession基础

HttpSession接口提供了会话管理的方法:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import java.util.Date;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import javax.servlet.http.HttpSession;
  10. public class SessionServlet extends HttpServlet {
  11.    
  12.     @Override
  13.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  14.             throws ServletException, IOException {
  15.         
  16.         response.setContentType("text/html");
  17.         PrintWriter out = response.getWriter();
  18.         
  19.         // 获取或创建会话
  20.         HttpSession session = request.getSession();
  21.         
  22.         // 获取会话ID
  23.         String sessionId = session.getId();
  24.         
  25.         // 检查是否是新会话
  26.         boolean isNew = session.isNew();
  27.         
  28.         // 获取会话创建时间
  29.         long creationTime = session.getCreationTime();
  30.         
  31.         // 获取最后访问时间
  32.         long lastAccessedTime = session.getLastAccessedTime();
  33.         
  34.         // 设置会话属性
  35.         session.setAttribute("username", "JohnDoe");
  36.         session.setAttribute("loginTime", new Date());
  37.         
  38.         // 获取会话属性
  39.         String username = (String) session.getAttribute("username");
  40.         Date loginTime = (Date) session.getAttribute("loginTime");
  41.         
  42.         out.println("<html>");
  43.         out.println("<head>");
  44.         out.println("<title>会话管理示例</title>");
  45.         out.println("</head>");
  46.         out.println("<body>");
  47.         out.println("<h1>会话管理示例</h1>");
  48.         out.println("<p>会话ID: " + sessionId + "</p>");
  49.         out.println("<p>是否是新会话: " + isNew + "</p>");
  50.         out.println("<p>会话创建时间: " + new Date(creationTime) + "</p>");
  51.         out.println("<p>最后访问时间: " + new Date(lastAccessedTime) + "</p>");
  52.         out.println("<p>用户名: " + username + "</p>");
  53.         out.println("<p>登录时间: " + loginTime + "</p>");
  54.         out.println("<p><a href='invalidate'>使会话失效</a></p>");
  55.         out.println("</body>");
  56.         out.println("</html>");
  57.     }
  58. }
复制代码

8.2 会话超时和失效

可以设置会话的超时时间,或者手动使会话失效:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import javax.servlet.http.HttpSession;
  8. public class SessionInvalidateServlet extends HttpServlet {
  9.    
  10.     @Override
  11.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  12.             throws ServletException, IOException {
  13.         
  14.         HttpSession session = request.getSession(false);
  15.         
  16.         if (session != null) {
  17.             // 使会话失效
  18.             session.invalidate();
  19.             response.getWriter().println("会话已失效");
  20.         } else {
  21.             response.getWriter().println("没有活动的会话");
  22.         }
  23.     }
  24. }
复制代码

8.3 会话配置

可以通过web.xml或注解配置会话超时时间:
  1. <!-- 在web.xml中配置会话超时时间(分钟) -->
  2. <session-config>
  3.     <session-timeout>30</session-timeout>
  4. </session-config>
复制代码

或者在Servlet中使用注解:
  1. @WebServlet(
  2.     urlPatterns = {"/sessionConfig"},
  3.     initParams = {
  4.         @WebInitParam(name = "timeout", value = "15")
  5.     }
  6. )
  7. public class SessionConfigServlet extends HttpServlet {
  8.    
  9.     @Override
  10.     public void init() throws ServletException {
  11.         String timeout = getInitParameter("timeout");
  12.         if (timeout != null) {
  13.             getServletContext().setSessionTimeout(Integer.parseInt(timeout) * 60);
  14.         }
  15.     }
  16.    
  17.     // ...
  18. }
复制代码

9. Servlet过滤器

Servlet过滤器可以用于拦截请求和响应,进行预处理和后处理。过滤器可以用于日志记录、安全检查、数据压缩、编码转换等场景。

9.1 创建过滤器

创建一个过滤器需要实现javax.servlet.Filter接口:
  1. package com.example.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. import javax.servlet.http.HttpServletRequest;
  11. @WebFilter("/*")
  12. public class LoggingFilter implements Filter {
  13.    
  14.     @Override
  15.     public void init(FilterConfig filterConfig) throws ServletException {
  16.         System.out.println("LoggingFilter初始化");
  17.     }
  18.    
  19.     @Override
  20.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  21.             throws IOException, ServletException {
  22.         
  23.         HttpServletRequest httpRequest = (HttpServletRequest) request;
  24.         
  25.         // 记录请求信息
  26.         System.out.println("请求URL: " + httpRequest.getRequestURL());
  27.         System.out.println("请求方法: " + httpRequest.getMethod());
  28.         System.out.println("远程地址: " + httpRequest.getRemoteAddr());
  29.         System.out.println("查询字符串: " + httpRequest.getQueryString());
  30.         
  31.         long startTime = System.currentTimeMillis();
  32.         
  33.         try {
  34.             // 继续处理请求
  35.             chain.doFilter(request, response);
  36.         } finally {
  37.             // 记录处理时间
  38.             long endTime = System.currentTimeMillis();
  39.             System.out.println("请求处理时间: " + (endTime - startTime) + "ms");
  40.         }
  41.     }
  42.    
  43.     @Override
  44.     public void destroy() {
  45.         System.out.println("LoggingFilter销毁");
  46.     }
  47. }
复制代码

9.2 多个过滤器的执行顺序

当有多个过滤器应用于同一个URL时,它们的执行顺序由web.xml中的配置顺序或注解的类名顺序决定。
  1. package com.example.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. @WebFilter("/*")
  11. public class FirstFilter implements Filter {
  12.    
  13.     @Override
  14.     public void init(FilterConfig filterConfig) throws ServletException {
  15.         System.out.println("FirstFilter初始化");
  16.     }
  17.    
  18.     @Override
  19.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  20.             throws IOException, ServletException {
  21.         
  22.         System.out.println("FirstFilter - 请求预处理");
  23.         
  24.         // 继续处理请求
  25.         chain.doFilter(request, response);
  26.         
  27.         System.out.println("FirstFilter - 响应后处理");
  28.     }
  29.    
  30.     @Override
  31.     public void destroy() {
  32.         System.out.println("FirstFilter销毁");
  33.     }
  34. }
复制代码
  1. package com.example.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. @WebFilter("/*")
  11. public class SecondFilter implements Filter {
  12.    
  13.     @Override
  14.     public void init(FilterConfig filterConfig) throws ServletException {
  15.         System.out.println("SecondFilter初始化");
  16.     }
  17.    
  18.     @Override
  19.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  20.             throws IOException, ServletException {
  21.         
  22.         System.out.println("SecondFilter - 请求预处理");
  23.         
  24.         // 继续处理请求
  25.         chain.doFilter(request, response);
  26.         
  27.         System.out.println("SecondFilter - 响应后处理");
  28.     }
  29.    
  30.     @Override
  31.     public void destroy() {
  32.         System.out.println("SecondFilter销毁");
  33.     }
  34. }
复制代码

9.3 过滤器配置

除了使用注解,还可以在web.xml中配置过滤器:
  1. <filter>
  2.     <filter-name>LoggingFilter</filter-name>
  3.     <filter-class>com.example.filter.LoggingFilter</filter-class>
  4.     <init-param>
  5.         <param-name>logLevel</param-name>
  6.         <param-value>DEBUG</param-value>
  7.     </init-param>
  8. </filter>
  9. <filter-mapping>
  10.     <filter-name>LoggingFilter</filter-name>
  11.     <url-pattern>/*</url-pattern>
  12. </filter-mapping>
  13. <filter>
  14.     <filter-name>AuthenticationFilter</filter-name>
  15.     <filter-class>com.example.filter.AuthenticationFilter</filter-class>
  16. </filter>
  17. <filter-mapping>
  18.     <filter-name>AuthenticationFilter</filter-name>
  19.     <url-pattern>/secure/*</url-pattern>
  20. </filter-mapping>
复制代码

10. Servlet监听器

Servlet监听器用于监听Web应用中的事件,如Servlet上下文的创建和销毁、会话的创建和销毁、请求的初始化等。

10.1 ServletContextListener

ServletContextListener用于监听Servlet上下文的创建和销毁事件:
  1. package com.example.listener;
  2. import javax.servlet.ServletContextEvent;
  3. import javax.servlet.ServletContextListener;
  4. import javax.servlet.annotation.WebListener;
  5. @WebListener
  6. public class MyServletContextListener implements ServletContextListener {
  7.    
  8.     @Override
  9.     public void contextInitialized(ServletContextEvent sce) {
  10.         System.out.println("Servlet上下文初始化");
  11.         
  12.         // 在应用程序启动时执行初始化操作
  13.         sce.getServletContext().setAttribute("appStartTime", System.currentTimeMillis());
  14.         
  15.         // 加载配置文件
  16.         // 初始化数据库连接池
  17.         // 等等
  18.     }
  19.    
  20.     @Override
  21.     public void contextDestroyed(ServletContextEvent sce) {
  22.         System.out.println("Servlet上下文销毁");
  23.         
  24.         // 在应用程序关闭时执行清理操作
  25.         // 关闭数据库连接池
  26.         // 保存统计数据
  27.         // 等等
  28.     }
  29. }
复制代码

10.2 HttpSessionListener

HttpSessionListener用于监听会话的创建和销毁事件:
  1. package com.example.listener;
  2. import javax.servlet.http.HttpSessionEvent;
  3. import javax.servlet.http.HttpSessionListener;
  4. import javax.servlet.annotation.WebListener;
  5. @WebListener
  6. public class MyHttpSessionListener implements HttpSessionListener {
  7.    
  8.     private int activeSessions = 0;
  9.    
  10.     @Override
  11.     public void sessionCreated(HttpSessionEvent se) {
  12.         activeSessions++;
  13.         System.out.println("会话创建: " + se.getSession().getId());
  14.         System.out.println("活动会话数: " + activeSessions);
  15.         
  16.         se.getSession().getServletContext().setAttribute("activeSessions", activeSessions);
  17.     }
  18.    
  19.     @Override
  20.     public void sessionDestroyed(HttpSessionEvent se) {
  21.         activeSessions--;
  22.         System.out.println("会话销毁: " + se.getSession().getId());
  23.         System.out.println("活动会话数: " + activeSessions);
  24.         
  25.         se.getSession().getServletContext().setAttribute("activeSessions", activeSessions);
  26.     }
  27. }
复制代码

10.3 ServletRequestListener

ServletRequestListener用于监听请求的创建和销毁事件:
  1. package com.example.listener;
  2. import javax.servlet.ServletRequestEvent;
  3. import javax.servlet.ServletRequestListener;
  4. import javax.servlet.annotation.WebListener;
  5. import javax.servlet.http.HttpServletRequest;
  6. @WebListener
  7. public class MyServletRequestListener implements ServletRequestListener {
  8.    
  9.     @Override
  10.     public void requestInitialized(ServletRequestEvent sre) {
  11.         HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
  12.         System.out.println("请求初始化: " + request.getRequestURI());
  13.     }
  14.    
  15.     @Override
  16.     public void requestDestroyed(ServletRequestEvent sre) {
  17.         HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
  18.         System.out.println("请求销毁: " + request.getRequestURI());
  19.     }
  20. }
复制代码

10.4 属性监听器

属性监听器用于监听作用域中属性的添加、删除和替换事件:
  1. package com.example.listener;
  2. import javax.servlet.ServletContextAttributeEvent;
  3. import javax.servlet.ServletContextAttributeListener;
  4. import javax.servlet.annotation.WebListener;
  5. @WebListener
  6. public class MyAttributeListener implements ServletContextAttributeListener {
  7.    
  8.     @Override
  9.     public void attributeAdded(ServletContextAttributeEvent event) {
  10.         System.out.println("属性添加: " + event.getName() + " = " + event.getValue());
  11.     }
  12.    
  13.     @Override
  14.     public void attributeRemoved(ServletContextAttributeEvent event) {
  15.         System.out.println("属性移除: " + event.getName() + " = " + event.getValue());
  16.     }
  17.    
  18.     @Override
  19.     public void attributeReplaced(ServletContextAttributeEvent event) {
  20.         System.out.println("属性替换: " + event.getName() +
  21.                           " 旧值 = " + event.getValue() +
  22.                           " 新值 = " + event.getServletContext().getAttribute(event.getName()));
  23.     }
  24. }
复制代码

11. 实际应用示例

让我们通过一个完整的用户登录示例来综合运用前面学到的Servlet技术。

11.1 数据库准备

首先,我们创建一个用户表:
  1. CREATE DATABASE servlet_demo;
  2. USE servlet_demo;
  3. CREATE TABLE users (
  4.     id INT AUTO_INCREMENT PRIMARY KEY,
  5.     username VARCHAR(50) NOT NULL UNIQUE,
  6.     password VARCHAR(100) NOT NULL,
  7.     email VARCHAR(100),
  8.     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  9. );
  10. INSERT INTO users (username, password, email) VALUES
  11. ('admin', 'admin123', 'admin@example.com'),
  12. ('user1', 'password1', 'user1@example.com'),
  13. ('user2', 'password2', 'user2@example.com');
复制代码

11.2 数据库连接工具类

创建一个数据库连接工具类:
  1. package com.example.util;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. public class DatabaseUtil {
  6.     private static final String URL = "jdbc:mysql://localhost:3306/servlet_demo?useSSL=false&serverTimezone=UTC";
  7.     private static final String USERNAME = "root";
  8.     private static final String PASSWORD = "root";
  9.    
  10.     static {
  11.         try {
  12.             Class.forName("com.mysql.cj.jdbc.Driver");
  13.         } catch (ClassNotFoundException e) {
  14.             e.printStackTrace();
  15.         }
  16.     }
  17.    
  18.     public static Connection getConnection() throws SQLException {
  19.         return DriverManager.getConnection(URL, USERNAME, PASSWORD);
  20.     }
  21.    
  22.     public static void closeConnection(Connection connection) {
  23.         if (connection != null) {
  24.             try {
  25.                 connection.close();
  26.             } catch (SQLException e) {
  27.                 e.printStackTrace();
  28.             }
  29.         }
  30.     }
  31. }
复制代码

11.3 用户模型类

创建一个用户模型类:
  1. package com.example.model;
  2. import java.sql.Timestamp;
  3. public class User {
  4.     private int id;
  5.     private String username;
  6.     private String password;
  7.     private String email;
  8.     private Timestamp createdAt;
  9.    
  10.     public User() {}
  11.    
  12.     public User(int id, String username, String password, String email, Timestamp createdAt) {
  13.         this.id = id;
  14.         this.username = username;
  15.         this.password = password;
  16.         this.email = email;
  17.         this.createdAt = createdAt;
  18.     }
  19.    
  20.     // Getters and Setters
  21.     public int getId() {
  22.         return id;
  23.     }
  24.    
  25.     public void setId(int id) {
  26.         this.id = id;
  27.     }
  28.    
  29.     public String getUsername() {
  30.         return username;
  31.     }
  32.    
  33.     public void setUsername(String username) {
  34.         this.username = username;
  35.     }
  36.    
  37.     public String getPassword() {
  38.         return password;
  39.     }
  40.    
  41.     public void setPassword(String password) {
  42.         this.password = password;
  43.     }
  44.    
  45.     public String getEmail() {
  46.         return email;
  47.     }
  48.    
  49.     public void setEmail(String email) {
  50.         this.email = email;
  51.     }
  52.    
  53.     public Timestamp getCreatedAt() {
  54.         return createdAt;
  55.     }
  56.    
  57.     public void setCreatedAt(Timestamp createdAt) {
  58.         this.createdAt = createdAt;
  59.     }
  60.    
  61.     @Override
  62.     public String toString() {
  63.         return "User [id=" + id + ", username=" + username + ", email=" + email + ", createdAt=" + createdAt + "]";
  64.     }
  65. }
复制代码

11.4 用户DAO类

创建一个用户数据访问对象(DAO)类:
  1. package com.example.dao;
  2. import java.sql.Connection;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import com.example.model.User;
  9. import com.example.util.DatabaseUtil;
  10. public class UserDao {
  11.    
  12.     public User findByUsername(String username) {
  13.         String sql = "SELECT * FROM users WHERE username = ?";
  14.         Connection connection = null;
  15.         PreparedStatement statement = null;
  16.         ResultSet resultSet = null;
  17.         User user = null;
  18.         
  19.         try {
  20.             connection = DatabaseUtil.getConnection();
  21.             statement = connection.prepareStatement(sql);
  22.             statement.setString(1, username);
  23.             resultSet = statement.executeQuery();
  24.             
  25.             if (resultSet.next()) {
  26.                 user = new User();
  27.                 user.setId(resultSet.getInt("id"));
  28.                 user.setUsername(resultSet.getString("username"));
  29.                 user.setPassword(resultSet.getString("password"));
  30.                 user.setEmail(resultSet.getString("email"));
  31.                 user.setCreatedAt(resultSet.getTimestamp("created_at"));
  32.             }
  33.         } catch (SQLException e) {
  34.             e.printStackTrace();
  35.         } finally {
  36.             try {
  37.                 if (resultSet != null) resultSet.close();
  38.                 if (statement != null) statement.close();
  39.                 if (connection != null) DatabaseUtil.closeConnection(connection);
  40.             } catch (SQLException e) {
  41.                 e.printStackTrace();
  42.             }
  43.         }
  44.         
  45.         return user;
  46.     }
  47.    
  48.     public List<User> findAll() {
  49.         String sql = "SELECT * FROM users";
  50.         Connection connection = null;
  51.         PreparedStatement statement = null;
  52.         ResultSet resultSet = null;
  53.         List<User> users = new ArrayList<>();
  54.         
  55.         try {
  56.             connection = DatabaseUtil.getConnection();
  57.             statement = connection.prepareStatement(sql);
  58.             resultSet = statement.executeQuery();
  59.             
  60.             while (resultSet.next()) {
  61.                 User user = new User();
  62.                 user.setId(resultSet.getInt("id"));
  63.                 user.setUsername(resultSet.getString("username"));
  64.                 user.setPassword(resultSet.getString("password"));
  65.                 user.setEmail(resultSet.getString("email"));
  66.                 user.setCreatedAt(resultSet.getTimestamp("created_at"));
  67.                 users.add(user);
  68.             }
  69.         } catch (SQLException e) {
  70.             e.printStackTrace();
  71.         } finally {
  72.             try {
  73.                 if (resultSet != null) resultSet.close();
  74.                 if (statement != null) statement.close();
  75.                 if (connection != null) DatabaseUtil.closeConnection(connection);
  76.             } catch (SQLException e) {
  77.                 e.printStackTrace();
  78.             }
  79.         }
  80.         
  81.         return users;
  82.     }
  83.    
  84.     public boolean save(User user) {
  85.         String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
  86.         Connection connection = null;
  87.         PreparedStatement statement = null;
  88.         int result = 0;
  89.         
  90.         try {
  91.             connection = DatabaseUtil.getConnection();
  92.             statement = connection.prepareStatement(sql);
  93.             statement.setString(1, user.getUsername());
  94.             statement.setString(2, user.getPassword());
  95.             statement.setString(3, user.getEmail());
  96.             result = statement.executeUpdate();
  97.         } catch (SQLException e) {
  98.             e.printStackTrace();
  99.         } finally {
  100.             try {
  101.                 if (statement != null) statement.close();
  102.                 if (connection != null) DatabaseUtil.closeConnection(connection);
  103.             } catch (SQLException e) {
  104.                 e.printStackTrace();
  105.             }
  106.         }
  107.         
  108.         return result > 0;
  109.     }
  110. }
复制代码

11.5 认证过滤器

创建一个认证过滤器,用于保护需要登录才能访问的资源:
  1. package com.example.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.annotation.WebFilter;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import javax.servlet.http.HttpSession;
  13. @WebFilter("/secure/*")
  14. public class AuthenticationFilter implements Filter {
  15.    
  16.     @Override
  17.     public void init(FilterConfig filterConfig) throws ServletException {
  18.         // 初始化代码
  19.     }
  20.    
  21.     @Override
  22.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  23.             throws IOException, ServletException {
  24.         
  25.         HttpServletRequest httpRequest = (HttpServletRequest) request;
  26.         HttpServletResponse httpResponse = (HttpServletResponse) response;
  27.         
  28.         HttpSession session = httpRequest.getSession(false);
  29.         
  30.         boolean isLoggedIn = (session != null && session.getAttribute("user") != null);
  31.         
  32.         if (isLoggedIn) {
  33.             // 用户已登录,继续处理请求
  34.             chain.doFilter(request, response);
  35.         } else {
  36.             // 用户未登录,重定向到登录页面
  37.             httpResponse.sendRedirect(httpRequest.getContextPath() + "/login");
  38.         }
  39.     }
  40.    
  41.     @Override
  42.     public void destroy() {
  43.         // 清理代码
  44.     }
  45. }
复制代码

11.6 登录Servlet

创建一个处理用户登录的Servlet:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import javax.servlet.http.HttpSession;
  9. import com.example.dao.UserDao;
  10. import com.example.model.User;
  11. @WebServlet("/login")
  12. public class LoginServlet extends HttpServlet {
  13.    
  14.     private UserDao userDao;
  15.    
  16.     @Override
  17.     public void init() throws ServletException {
  18.         userDao = new UserDao();
  19.     }
  20.    
  21.     @Override
  22.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  23.             throws ServletException, IOException {
  24.         
  25.         // 如果用户已经登录,重定向到欢迎页面
  26.         HttpSession session = request.getSession(false);
  27.         if (session != null && session.getAttribute("user") != null) {
  28.             response.sendRedirect(request.getContextPath() + "/secure/welcome");
  29.             return;
  30.         }
  31.         
  32.         request.getRequestDispatcher("/WEB-INF/view/login.jsp").forward(request, response);
  33.     }
  34.    
  35.     @Override
  36.     protected void doPost(HttpServletRequest request, HttpServletResponse response)
  37.             throws ServletException, IOException {
  38.         
  39.         String username = request.getParameter("username");
  40.         String password = request.getParameter("password");
  41.         
  42.         User user = userDao.findByUsername(username);
  43.         
  44.         if (user != null && user.getPassword().equals(password)) {
  45.             // 登录成功
  46.             HttpSession session = request.getSession();
  47.             session.setAttribute("user", user);
  48.             
  49.             // 重定向到欢迎页面
  50.             response.sendRedirect(request.getContextPath() + "/secure/welcome");
  51.         } else {
  52.             // 登录失败
  53.             request.setAttribute("errorMessage", "用户名或密码错误");
  54.             request.getRequestDispatcher("/WEB-INF/view/login.jsp").forward(request, response);
  55.         }
  56.     }
  57. }
复制代码

11.7 登出Servlet

创建一个处理用户登出的Servlet:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import javax.servlet.http.HttpSession;
  9. @WebServlet("/logout")
  10. public class LogoutServlet extends HttpServlet {
  11.    
  12.     @Override
  13.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  14.             throws ServletException, IOException {
  15.         
  16.         HttpSession session = request.getSession(false);
  17.         
  18.         if (session != null) {
  19.             session.invalidate();
  20.         }
  21.         
  22.         response.sendRedirect(request.getContextPath() + "/login");
  23.     }
  24. }
复制代码

11.8 欢迎Servlet

创建一个欢迎页面Servlet:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. @WebServlet("/secure/welcome")
  9. public class WelcomeServlet extends HttpServlet {
  10.    
  11.     @Override
  12.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  13.             throws ServletException, IOException {
  14.         
  15.         request.getRequestDispatcher("/WEB-INF/view/welcome.jsp").forward(request, response);
  16.     }
  17. }
复制代码

11.9 用户列表Servlet

创建一个显示用户列表的Servlet:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import java.util.List;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import com.example.dao.UserDao;
  10. import com.example.model.User;
  11. @WebServlet("/secure/users")
  12. public class UserListServlet extends HttpServlet {
  13.    
  14.     private UserDao userDao;
  15.    
  16.     @Override
  17.     public void init() throws ServletException {
  18.         userDao = new UserDao();
  19.     }
  20.    
  21.     @Override
  22.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  23.             throws ServletException, IOException {
  24.         
  25.         List<User> users = userDao.findAll();
  26.         request.setAttribute("users", users);
  27.         
  28.         request.getRequestDispatcher("/WEB-INF/view/userList.jsp").forward(request, response);
  29.     }
  30. }
复制代码

11.10 注册Servlet

创建一个处理用户注册的Servlet:
  1. package com.example.servlet;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import com.example.dao.UserDao;
  9. import com.example.model.User;
  10. @WebServlet("/register")
  11. public class RegisterServlet extends HttpServlet {
  12.    
  13.     private UserDao userDao;
  14.    
  15.     @Override
  16.     public void init() throws ServletException {
  17.         userDao = new UserDao();
  18.     }
  19.    
  20.     @Override
  21.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  22.             throws ServletException, IOException {
  23.         
  24.         request.getRequestDispatcher("/WEB-INF/view/register.jsp").forward(request, response);
  25.     }
  26.    
  27.     @Override
  28.     protected void doPost(HttpServletRequest request, HttpServletResponse response)
  29.             throws ServletException, IOException {
  30.         
  31.         String username = request.getParameter("username");
  32.         String password = request.getParameter("password");
  33.         String confirmPassword = request.getParameter("confirmPassword");
  34.         String email = request.getParameter("email");
  35.         
  36.         // 验证密码是否匹配
  37.         if (!password.equals(confirmPassword)) {
  38.             request.setAttribute("errorMessage", "两次输入的密码不匹配");
  39.             request.getRequestDispatcher("/WEB-INF/view/register.jsp").forward(request, response);
  40.             return;
  41.         }
  42.         
  43.         // 检查用户名是否已存在
  44.         User existingUser = userDao.findByUsername(username);
  45.         if (existingUser != null) {
  46.             request.setAttribute("errorMessage", "用户名已存在");
  47.             request.getRequestDispatcher("/WEB-INF/view/register.jsp").forward(request, response);
  48.             return;
  49.         }
  50.         
  51.         // 创建新用户
  52.         User newUser = new User();
  53.         newUser.setUsername(username);
  54.         newUser.setPassword(password);
  55.         newUser.setEmail(email);
  56.         
  57.         boolean success = userDao.save(newUser);
  58.         
  59.         if (success) {
  60.             // 注册成功,重定向到登录页面
  61.             response.sendRedirect(request.getContextPath() + "/login?registered=true");
  62.         } else {
  63.             // 注册失败
  64.             request.setAttribute("errorMessage", "注册失败,请稍后再试");
  65.             request.getRequestDispatcher("/WEB-INF/view/register.jsp").forward(request, response);
  66.         }
  67.     }
  68. }
复制代码

11.11 JSP视图

创建登录页面(login.jsp):
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5.     <meta charset="UTF-8">
  6.     <title>登录</title>
  7.     <style>
  8.         .error {
  9.             color: red;
  10.         }
  11.     </style>
  12. </head>
  13. <body>
  14.     <h1>用户登录</h1>
  15.    
  16.     <%-- 显示注册成功消息 --%>
  17.     <c:if test="${not empty param.registered}">
  18.         <p style="color: green;">注册成功,请登录!</p>
  19.     </c:if>
  20.    
  21.     <%-- 显示错误消息 --%>
  22.     <c:if test="${not empty errorMessage}">
  23.         <p class="error">${errorMessage}</p>
  24.     </c:if>
  25.    
  26.     <form action="${pageContext.request.contextPath}/login" method="post">
  27.         <div>
  28.             <label for="username">用户名:</label>
  29.             <input type="text" id="username" name="username" required>
  30.         </div>
  31.         <div>
  32.             <label for="password">密码:</label>
  33.             <input type="password" id="password" name="password" required>
  34.         </div>
  35.         <div>
  36.             <input type="submit" value="登录">
  37.         </div>
  38.     </form>
  39.    
  40.     <p>还没有账号?<a href="${pageContext.request.contextPath}/register">立即注册</a></p>
  41. </body>
  42. </html>
复制代码

创建注册页面(register.jsp):
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5.     <meta charset="UTF-8">
  6.     <title>注册</title>
  7.     <style>
  8.         .error {
  9.             color: red;
  10.         }
  11.     </style>
  12. </head>
  13. <body>
  14.     <h1>用户注册</h1>
  15.    
  16.     <%-- 显示错误消息 --%>
  17.     <c:if test="${not empty errorMessage}">
  18.         <p class="error">${errorMessage}</p>
  19.     </c:if>
  20.    
  21.     <form action="${pageContext.request.contextPath}/register" method="post">
  22.         <div>
  23.             <label for="username">用户名:</label>
  24.             <input type="text" id="username" name="username" required>
  25.         </div>
  26.         <div>
  27.             <label for="email">邮箱:</label>
  28.             <input type="email" id="email" name="email" required>
  29.         </div>
  30.         <div>
  31.             <label for="password">密码:</label>
  32.             <input type="password" id="password" name="password" required>
  33.         </div>
  34.         <div>
  35.             <label for="confirmPassword">确认密码:</label>
  36.             <input type="password" id="confirmPassword" name="confirmPassword" required>
  37.         </div>
  38.         <div>
  39.             <input type="submit" value="注册">
  40.         </div>
  41.     </form>
  42.    
  43.     <p>已有账号?<a href="${pageContext.request.contextPath}/login">立即登录</a></p>
  44. </body>
  45. </html>
复制代码

创建欢迎页面(welcome.jsp):
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5.     <meta charset="UTF-8">
  6.     <title>欢迎</title>
  7. </head>
  8. <body>
  9.     <h1>欢迎, ${user.username}!</h1>
  10.     <p>您的邮箱: ${user.email}</p>
  11.     <p>注册时间: ${user.createdAt}</p>
  12.    
  13.     <p>
  14.         <a href="${pageContext.request.contextPath}/secure/users">查看用户列表</a> |
  15.         <a href="${pageContext.request.contextPath}/logout">退出登录</a>
  16.     </p>
  17. </body>
  18. </html>
复制代码

创建用户列表页面(userList.jsp):
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6.     <meta charset="UTF-8">
  7.     <title>用户列表</title>
  8.     <style>
  9.         table {
  10.             border-collapse: collapse;
  11.             width: 100%;
  12.         }
  13.         th, td {
  14.             border: 1px solid #ddd;
  15.             padding: 8px;
  16.             text-align: left;
  17.         }
  18.         th {
  19.             background-color: #f2f2f2;
  20.         }
  21.         tr:nth-child(even) {
  22.             background-color: #f9f9f9;
  23.         }
  24.     </style>
  25. </head>
  26. <body>
  27.     <h1>用户列表</h1>
  28.    
  29.     <table>
  30.         <tr>
  31.             <th>ID</th>
  32.             <th>用户名</th>
  33.             <th>邮箱</th>
  34.             <th>注册时间</th>
  35.         </tr>
  36.         <c:forEach items="${users}" var="user">
  37.             <tr>
  38.                 <td>${user.id}</td>
  39.                 <td>${user.username}</td>
  40.                 <td>${user.email}</td>
  41.                 <td>${user.createdAt}</td>
  42.             </tr>
  43.         </c:forEach>
  44.     </table>
  45.    
  46.     <p>
  47.         <a href="${pageContext.request.contextPath}/secure/welcome">返回首页</a> |
  48.         <a href="${pageContext.request.contextPath}/logout">退出登录</a>
  49.     </p>
  50. </body>
  51. </html>
复制代码

11.12 配置web.xml

最后,配置web.xml文件:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  5.          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  6.          version="4.0">
  7.    
  8.     <display-name>Servlet Demo</display-name>
  9.    
  10.     <welcome-file-list>
  11.         <welcome-file>index.html</welcome-file>
  12.     </welcome-file-list>
  13.    
  14.     <session-config>
  15.         <session-timeout>30</session-timeout>
  16.     </session-config>
  17.    
  18.     <error-page>
  19.         <error-code>404</error-code>
  20.         <location>/WEB-INF/view/error404.jsp</location>
  21.     </error-page>
  22.    
  23.     <error-page>
  24.         <error-code>500</error-code>
  25.         <location>/WEB-INF/view/error500.jsp</location>
  26.     </error-page>
  27.    
  28. </web-app>
复制代码

创建一个简单的首页(index.html):
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Servlet Demo</title>
  6. </head>
  7. <body>
  8.     <h1>欢迎访问Servlet Demo</h1>
  9.     <p>
  10.         <a href="login">登录</a> |
  11.         <a href="register">注册</a>
  12.     </p>
  13. </body>
  14. </html>
复制代码

12. 最佳实践和常见问题

12.1 最佳实践

在Servlet开发中,推荐使用Model-View-Controller(MVC)架构模式:

• Model:负责业务逻辑和数据访问(如User类和UserDao类)
• View:负责显示数据(如JSP页面)
• Controller:接收用户输入并调用Model和View(如Servlet类)

Servlet应该只作为控制器,负责接收请求、调用业务逻辑、选择视图并返回响应。业务逻辑应该放在单独的类中。

使用Data Access Object(DAO)模式将数据库访问逻辑与业务逻辑分离,提高代码的可维护性和可测试性。

使用数据库连接池(如HikariCP、Apache DBCP等)来管理数据库连接,提高性能和资源利用率。

在执行SQL查询时,使用PreparedStatement而不是Statement,以防止SQL注入攻击。
  1. // 不安全的方式
  2. String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
  3. Statement statement = connection.createStatement();
  4. ResultSet resultSet = statement.executeQuery(sql);
  5. // 安全的方式
  6. String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
  7. PreparedStatement statement = connection.prepareStatement(sql);
  8. statement.setString(1, username);
  9. statement.setString(2, password);
  10. ResultSet resultSet = statement.executeQuery();
复制代码

使用try-with-resources语句自动关闭资源,避免资源泄漏。
  1. // 传统方式
  2. Connection connection = null;
  3. PreparedStatement statement = null;
  4. ResultSet resultSet = null;
  5. try {
  6.     connection = DatabaseUtil.getConnection();
  7.     statement = connection.prepareStatement(sql);
  8.     // ...
  9.     resultSet = statement.executeQuery();
  10.     // ...
  11. } catch (SQLException e) {
  12.     e.printStackTrace();
  13. } finally {
  14.     try {
  15.         if (resultSet != null) resultSet.close();
  16.         if (statement != null) statement.close();
  17.         if (connection != null) connection.close();
  18.     } catch (SQLException e) {
  19.         e.printStackTrace();
  20.     }
  21. }
  22. // 使用try-with-resources
  23. try (Connection connection = DatabaseUtil.getConnection();
  24.      PreparedStatement statement = connection.prepareStatement(sql)) {
  25.     // ...
  26.     try (ResultSet resultSet = statement.executeQuery()) {
  27.         // ...
  28.     }
  29. } catch (SQLException e) {
  30.     e.printStackTrace();
  31. }
复制代码

使用日志框架(如Log4j、SLF4J等)记录应用程序的运行信息,便于调试和问题排查。

使用编码过滤器统一设置请求和响应的字符编码,避免中文乱码问题。
  1. @WebFilter("/*")
  2. public class EncodingFilter implements Filter {
  3.    
  4.     private String encoding;
  5.    
  6.     @Override
  7.     public void init(FilterConfig filterConfig) throws ServletException {
  8.         encoding = filterConfig.getInitParameter("encoding");
  9.         if (encoding == null) {
  10.             encoding = "UTF-8";
  11.         }
  12.     }
  13.    
  14.     @Override
  15.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  16.             throws IOException, ServletException {
  17.         
  18.         request.setCharacterEncoding(encoding);
  19.         response.setCharacterEncoding(encoding);
  20.         
  21.         chain.doFilter(request, response);
  22.     }
  23.    
  24.     @Override
  25.     public void destroy() {
  26.         // 清理代码
  27.     }
  28. }
复制代码

使用Servlet的异常处理机制统一处理异常,提供友好的错误页面。
  1. <!-- 在web.xml中配置错误页面 -->
  2. <error-page>
  3.     <exception-type>java.lang.Exception</exception-type>
  4.     <location>/WEB-INF/view/error.jsp</location>
  5. </error-page>
  6. <error-page>
  7.     <error-code>404</error-code>
  8.     <location>/WEB-INF/view/error404.jsp</location>
  9. </error-page>
  10. <error-page>
  11.     <error-code>500</error-code>
  12.     <location>/WEB-INF/view/error500.jsp</location>
  13. </error-page>
复制代码

12.2 常见问题

中文乱码是Servlet开发中常见的问题,主要原因是字符编码不一致。解决方法:

1. 设置请求的字符编码:
  1. request.setCharacterEncoding("UTF-8");
复制代码

1. 设置响应的字符编码:
  1. response.setContentType("text/html;charset=UTF-8");
复制代码

1. 使用编码过滤器统一设置字符编码。

Servlet路径映射不正确会导致404错误。解决方法:

1. 检查web.xml或@WebServlet注解中的URL模式是否正确。
2. 检查请求URL是否包含正确的上下文路径。
3. 使用绝对路径(以”/“开头)而不是相对路径。

会话过早失效会导致用户需要频繁登录。解决方法:

1. 设置合适的会话超时时间:
  1. <session-config>
  2.     <session-timeout>30</session-timeout>
  3. </session-config>
复制代码

1. 在需要长时间保持登录的场景,使用Cookie或令牌(Token)机制。

Servlet默认是单例多线程的,如果Servlet类中包含可变的实例变量,可能会导致并发问题。解决方法:

1. 避免在Servlet中使用实例变量存储请求特定的数据。
2. 使用局部变量而不是实例变量。
3. 如果必须使用实例变量,确保它们是线程安全的(如使用同步块或并发集合)。

未正确关闭数据库连接、文件流等资源会导致资源泄漏。解决方法:

1. 使用try-with-resources语句自动关闭资源。
2. 在finally块中关闭资源。
3. 使用连接池管理数据库连接。

Servlet应用程序可能会面临性能问题。解决方法:

1. 使用缓存减少数据库访问。
2. 使用连接池管理数据库连接。
3. 优化SQL查询。
4. 使用异步处理提高吞吐量。
5. 使用CDN加速静态资源。

总结

通过本文的学习,我们从Servlet的Hello World示例开始,逐步深入了解了Servlet的核心技术,包括Servlet生命周期、Servlet API、请求和响应处理、会话管理、过滤器和监听器等内容。我们还通过一个完整的用户登录示例,综合运用了这些技术,并分享了一些最佳实践和常见问题的解决方法。

Servlet作为Java Web开发的基础技术,虽然现在有许多更高级的框架(如Spring MVC),但理解Servlet的核心原理对于掌握Java Web开发仍然至关重要。希望本文能够帮助你更好地理解和应用Servlet技术,构建出高效、可靠的Java Web应用程序。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

手机版|联系我们|小黑屋|TG频道|RSS |网站地图

Powered by Pixtech

© 2025-2026 Pixtech Team.

>