java spring

Spring 攔截處理器

謝騏澤 2020/07/24 11:05:18
1727

前言

在開發程式時,會有需要全局處理的程式邏輯,傳統的Servlet容器中,可以使用過濾器(Filter)和監聽器(Listener),而在Spring的框架中,可以使用攔截器(Interceptor)和面向切面程式aop(Aspect-Oriented Programming),本文主要簡單的介紹 FilterListenerInterceptoraop的區別,且如何在spring中做使用。

 


 

監聽器 (Listener)

  • 監聽器(Listener)是依賴於Servlet容器,可以監聽程式中某些對象,訊息的創建,銷毀和修改等動作的發生,然後進行相對應的處理。
  • 根據監聽對象,將監聽器分為3類:
    • ServletContext:對應applicationinterfaceServletContextListener。在整個服務中只有一個,服務啟動時初始化,服務關閉時銷毀。可用於session的緩存,Ex:結合redis,在服務啟動時,從資料庫讀取資料到session中。
    • HttpSession:對應sessioninterfaceHttpSessionListener。在session啟用時建立,而關閉後會被銷毀。
    • ServletRequest:對應requestinterfaceServletRequestListenerrequest是客戶發送請求時所創建的,用於請求數據的處理,請求處理完後銷毀。可以用於客戶訊息的包裝。
  • 實現監聽器(Listener)

1.        建立監聽器

u  實現ServletContextListenerHttpSessionListenerServletRequestListener

u  覆寫方法,範例使用的是ServletContextListener,所以覆寫contextInitialized()contextDestroyed()的方法

u  Configuration新增@WebListener

u  Application新增@ServletComponentScan

@WebListener
public class ServletContextListenerConfig implements ServletContextListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(ServletContextListenerConfig.class);

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        LOGGER.info("ServletContextListener context initialized ...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        LOGGER.info("ServletContextListener context destroyed ...");
    }

}
@SpringBootApplication
@ServletComponentScan
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

2.        啟動服務和關閉服務就可以看到監聽器執行的動作

 

過濾器 (Filter)

  • 過濾器(Filter)是依賴於Servlet容器,可以對幾乎所有請求進行過濾。過濾器是對資料進行過濾,可以預先處理這些需要被處理的資料,過濾器也可以操作權限的控制,在沒有授權的情況下,可以有效地把無權限的資源或請求給過濾掉。
  • 實現過濾器(Filter)

1.        建立過濾器

u  實現Filter

u  覆寫doFilter()的方法

u  Configuration新增@WebFilter

u  Application新增@ServletComponentScan

@WebFilter(filterName = "RequestFilter", urlPatterns = "/book")
public class RequestFilter implements Filter {
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        LOGGER.info("Request do filter ...");
        chain.doFilter(request, response);
    }

}
@SpringBootApplication
@ServletComponentScan
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

2.        發動請求就可以看到過濾器執行的動作

 

攔截器 (Interceptor)

  • 攔截器(Interceptor)是依賴於於Spring的框架,是基於Java的動態代理實現。用於切點來執行方法,避免過多重複的程式,可以用於記錄Log和全局的配置,非常類似aop(Aspect-Oriented Programming)
  • 通過實現 HandlerInterceptor接口,實現三種方法。
    • preHandle():在請求前執行,回傳值為布林值,如果是true則通過攔截器,false則不通過攔截器。
    • postHandle():請求處理完後執行。
    • afterCompletion():請求及回應結束後執行。
  • 透過Configuration配置攔截器,實現WebMvcConfigurerinterface,實現addInterceptors方法。
  • 實現攔截器(Interceptor)

1.        建立攔截器

u  實現HandlerInterceptor

u  覆寫preHandle()postHandle()afterCompletion()方法

public class DemoInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LOGGER.info("Interceptor do preHandle ...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        LOGGER.info("Interceptor do postHandle ...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LOGGER.info("Interceptor do afterCompletion ...");
    }

}

2.        建立Web MVC的配置將此攔截器註冊進去

u  實現WebMvcConfigurer

u  覆寫addInterceptors()的方法

                                        i. 使用addInterceptor()將攔截器註冊進去

                                      ii.  使用addPathPatterns()告訴攔截器,需要攔截的路徑

u  Configuration新增@Configuration

u  Application新增@EnableWebMvc

攔截器

public class DemoInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LOGGER.info("Interceptor do preHandle ...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        LOGGER.info("Interceptor do postHandle ...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        LOGGER.info("Interceptor do afterCompletion ...");
    }

}

Web MVC的配置

 

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
    }

}
@SpringBootApplication
@EnableWebMvc
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

3.        發動請求就可以看到攔截器執行的動作

 

AOP (Aspect-Oriented Programming)

  • AOP類似於攔截器,但Springaop功能更強大,且只需要簡單的配置就能輕鬆的達成目的。
  • 定義AOP時,不需要像攔截器那麼麻煩,只需要透過註解就能實現
  • AOP常用的註解
    • @Aspect:定義java為一個切面類別。
    • @Pointcut:定義一個節點,可以使用表達式定義。
    • @Before:進入節點的開頭,開始執行切面的方法。
    • @After:進入節點的結尾,開始執行切面的方法。
    • @AfterReturning:進入節點的結尾且返回值時,開始執行切面的方法。
    • @Around:在節點前後執行切面的方法,並控制和執行節點本身的內容。相對於@Before@After的結合。
    • @Order:定義aop執行的優先順序,order的值越小,會越被先執行。
  • 實現AOP(Aspect-Oriented Programming)

1.        載入jar

u  Gradle

dependencies {
	implementation('org.springframework.boot:spring-boot-starter-aop')
}

u  Maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.        建立AOP

u  新增@Aspect@Component,讓Spring知道,這個類別為切面類別的組件。

u  新增@Around並且定義節點,節點定義在:只要經過RestController,就會執行切面的方法。

@Aspect
@Order(1)
@Component
public class ControllerAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class);

    @Around(value = "within(@org.springframework.web.bind.annotation.RestController *)")
    public Object controllerAround(ProceedingJoinPoint jp) throws Throwable {
        LOGGER.info("Aop do controller around before method ...");

        Object result = jp.proceed();

        LOGGER.info("Aop do controller around after method ...");

        return result;
    }

}

3.        發動請求就可以看到切面類別執行的動作

 

執行順序

  • 攔截的順序

Listener -> Filter -> Interceptor -> AOP -> 執行的方法 -> AOP -> Interceptor -> Filter -> Listener

 

 

結語

透過這些過濾或攔截的方式,不管是在對於請求的資料做驗證或修改,還是在每一個步驟必須執行的動作,都是可以有效且避免重複的程式出現,從Listener -> Filter -> Interceptor -> AOP,越後面,功能則是更細緻且更強大

換言之,順序也是越後面,請求首先進入Servlet,越早過濾或攔截,對於系統的性能的消耗也相對會變少,在開發時,必須根據實際的業務情況進行分析與評估,這樣就可以更有效的控制過濾器與攔截器

謝騏澤