首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Jetty 9 實戰:HttpSessionListener 與 Filter 的程式化註冊與使用

     編輯:Java 相關知識

在嵌入式 Jetty 9 或無 web.xml 的 Spring Boot 應用中,需以程式化方式註冊 `HttpSessionListener` 與 `Filter`。本文提供完整代碼範例、註冊 API 解析、生命週期觸發條件與常見坑點,助你快速掌握 Jetty 組件配置。

一、HttpSessionListener:監聽 Session 建立與銷毀

1. 實現監聽器

import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;

public class MyHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("sessionCreated: " + se.getSession().getId());
        // 可在此初始化 Session 屬性、計數在線用戶等
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("sessionDestroyed: " + se.getSession().getId());
        // 清理資源、下線統計等
    }
}

2. 程式化註冊(嵌入式 Jetty)

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.server.session.DefaultSessionCache;
import org.eclipse.jetty.server.session.SessionHandler;

public class JettyServer {
    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);
        
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);

        // 關鍵:獲取 SessionHandler 並添加 EventListener
        context.getSessionHandler().addEventListener(new MyHttpSessionListener());

        // 添加 Servlet 測試
        context.addServlet(TestServlet.class, "/test");

        server.start();
        server.join();
    }
}

3. 關鍵觸發條件

  • sessionCreated:首次調用 `request.getSession()` 或 `request.getSession(true)` 時觸發。
  • sessionDestroyed:Session 超時、調用 `session.invalidate()`、伺服器關閉時觸發。
  • 注意:若整個請求鏈路從未調用 `getSession()`,則不會創建 Session,也不会觸發 listener。

二、Filter:攔截請求與回應

1. 實現過濾器

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter 初始化: " + filterConfig.getFilterName());
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
        if (req instanceof HttpServletRequest) {
            HttpServletRequest httpReq = (HttpServletRequest) req;
            System.out.println("攔截請求: " + httpReq.getServletPath());
            // 可在此做:編碼統一、登入校驗、日誌記錄、XSS 防護等
        }
        chain.doFilter(req, resp); // 必須調用,否則請求鏈中斷
    }

    @Override
    public void destroy() {
        System.out.println("Filter 銷毀");
    }
}

2. 程式化註冊

import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import jakarta.servlet.DispatcherType;
import java.util.EnumSet;

// 在上述 ServletContextHandler 中添加:
context.addFilter(
    new FilterHolder(new MyFilter()),  // 包裝 Filter 實例
    "/*",                              // URL 模式
    EnumSet.allOf(DispatcherType.class) // 派發類型:REQUEST, FORWARD, INCLUDE, ERROR, ASYNC
);

3. DispatcherType 詳解

類型觸發場景
REQUEST客戶端直接請求(預設)
FORWARD`RequestDispatcher.forward()` 轉發
INCLUDE`RequestDispatcher.include()` 包含
ERROR錯誤頁面派發
ASYNC異步請求 (`startAsync()`)

使用 `EnumSet.allOf(DispatcherType.class)` 覆蓋所有場景;若僅需攔截外部請求,用 `EnumSet.of(DispatcherType.REQUEST)`。

4. 觸發條件限制

  • Filter 只在URL 模式匹配且請求路徑存在時觸發。
  • 若請求路徑 404(無對應 Servlet),不會觸發 Filter(除非配置 `error-page` 派發)。

三、Spring Boot 整合 Jetty 時的註冊方式

Spring Boot 2.x/3.x 內嵌 Jetty 時,推薦使用 `ServletContextInitializer` 或 `FilterRegistrationBean`:

@Configuration
public class JettyConfig {
    @Bean
    public ServletContextInitializer sessionListenerInitializer() {
        return servletContext -> {
            servletContext.addListener(MyHttpSessionListener.class);
        };
    }

    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new MyFilter());
        bean.addUrlPatterns("/*");
        bean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
        bean.setOrder(1);
        return bean;
    }
}

四、常見問題排查

現象原因解決
Listener 不觸發請求未調用 getSession()確認業務代碼主動獲取 Session
Filter 不攔截URL 模式不匹配 / 請求 404檢查 addFilter urlPatterns、確認 Servlet 映射
Session 丟失SessionHandler 未啟用 / Cookie 未攜帶 JSESSIONIDServletContextHandler.SESSIONS、檢查瀏覽器 Cookie
重複過濾DispatcherType 包含 FORWARD 且轉發鏈長按需精確設定 DispatcherType

五、總結

Jetty 9 程式化註冊核心 API:

  • ServletContextHandler.getSessionHandler().addEventListener(listener) 註冊 Session 監聽器。
  • ServletContextHandler.addFilter(FilterHolder, urlPattern, dispatcherTypes) 註冊過濾器。

掌握觸發條件(Session 需主動創建、Filter 需路徑匹配且非 404)與 `DispatcherType` 語義,能避免 90% 的配置坑。Spring Boot 專案優先使用 `ServletContextInitializer` / `FilterRegistrationBean` 享受自動配置優勢。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved