Stories

Detail Return Return

Servlet詳解 - Stories Detail

概念

  • Servlet是運行在服務端的小程序(Server Applet),可以處理客户端的請求並返回響應,主要用於構建動態的Web應用,是SpringMVC的基礎。

生命週期

加載和初始化

  • 懶加載(默認在客户端第一次請求加載到容器中),通過反射實例化,並調用init(),且init()只能被調用一次,因此每個Servlet是單例的,需注意線程安全。

請求處理

  • Servlet 容器收到url請求後,路由到對應的Servlet,調用service()方法處理客户端請求,並返回響應。
  • 每次服務器收到一個請求時,Servlet 容器都會分配一個線程並調用service()方法,根據請求類型,執行對應的方法,也會存在線程安全問題,避免使用全局變量、非同步數據結構等。

銷燬

  • destroy()只會被調用一次,當容器被正常關閉時,釋放一些使用了的資源。
  • 異常終止情況,不會調用destroy()。

流程圖

其他應用

1. 過濾器(Filter)

1.1 作用

  • 對請求和響應進行預處理和後處理。
  • 典型應用場景:
    • 權限驗證。
    • 請求參數編碼處理。
    • 日誌記錄。
    • 防止 XSS 和 SQL 注入。

1.2 工作流程

  • 過濾器在 Servlet 執行之前運行,可以攔截並修改請求。
  • 過濾器在 Servlet 執行之後運行,可以修改響應。

1.3 關鍵接口

javax.servlet.Filter 接口,核心方法:

  1. init(FilterConfig filterConfig):在容器啓動時初始化過濾器。
  2. doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    • 進行請求/響應的過濾處理。
    • 調用 chain.doFilter() 將請求傳遞到下一個過濾器或目標 Servlet。
  1. destroy():在容器關閉時釋放資源。

1.4 示例

過濾器實現

@WebFilter(urlPatterns = "/*") // 攔截所有請求
public class LoggingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("過濾器初始化");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("請求到達過濾器");
        chain.doFilter(request, response); // 將請求傳遞到下一個過濾器或 Servlet
        System.out.println("響應從過濾器返回");
    }

    @Override
    public void destroy() {
        System.out.println("過濾器銷燬");
    }
}

2. 監聽器(Listener)

2.1 作用

  • 監聽 Web 應用中對象(如請求、會話、上下文)生命週期事件或屬性變化。
  • 典型應用場景:
    • 統計在線人數。
    • 初始化全局資源。
    • 監控會話銷燬以釋放資源。

2.2 關鍵接口

Servlet 提供了多種監聽器接口:

  1. 應用上下文監聽器
    • ServletContextListener:監聽應用啓動和銷燬事件。
  1. 會話監聽器
    • HttpSessionListener:監聽會話創建和銷燬事件。
    • HttpSessionAttributeListener:監聽會話屬性變化。
  1. 請求監聽器
    • ServletRequestListener:監聽請求創建和銷燬事件。
    • ServletRequestAttributeListener:監聽請求屬性變化。

2.3 示例

在線人數統計

@WebListener
public class OnlineUserListener implements HttpSessionListener {
    private static int onlineUsers = 0;

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        onlineUsers++;
        System.out.println("用户上線,當前在線人數:" + onlineUsers);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        onlineUsers--;
        System.out.println("用户下線,當前在線人數:" + onlineUsers);
    }
}

3. 異步處理

3.1 作用

  • Servlet 3.0 引入異步處理,用於提高性能和響應速度。
  • 異步處理允許在 Servlet 請求線程結束後繼續處理任務,釋放容器線程資源。

3.2 核心方法

  1. 在 Servlet 中啓動異步支持:
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
}
  1. 異步請求處理邏輯:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    AsyncContext asyncContext = req.startAsync();
    asyncContext.start(() -> {
        try {
            Thread.sleep(2000); // 模擬耗時操作
            asyncContext.getResponse().getWriter().write("異步請求完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            asyncContext.complete(); // 完成異步處理
        }
    });
}

4. 文件上傳與下載

4.1 文件上傳

配置文件上傳支持

Servlet 3.0 引入了對文件上傳的直接支持,通過 @MultipartConfig 註解。

示例

@WebServlet("/upload")
@MultipartConfig(location = "/tmp", fileSizeThreshold = 1024 * 1024, maxFileSize = 5 * 1024 * 1024)
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Part filePart = req.getPart("file"); // 獲取上傳文件的部分
        String fileName = filePart.getSubmittedFileName();
        filePart.write("/uploads/" + fileName); // 保存文件
        resp.getWriter().write("文件上傳成功:" + fileName);
    }
}

4.2 文件下載

示例

@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String fileName = "example.txt";
        resp.setContentType("application/octet-stream");
        resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        try (InputStream in = new FileInputStream("/uploads/" + fileName);
             OutputStream out = resp.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        }
    }
}

Add a new Comments

Some HTML is okay.