1.基礎知識
1.1面向對象編程(OOP)、面向切面編程(AOP)
面向對象編程:
將需求功能划分為不同的、相對獨立的和封裝良好的類,使他們有屬於自己的行為,依靠繼承和多態等來定義彼此的關系。
面向切面編程:
將通用需求功能從不相關的類中分離出來,使很多類共享一個行為,一旦發生變化,只需修改這個行為即可。
http://blog.csdn.net/small_mouse0/article/details/62895790這個例子解釋的比較容易理解。
1.2 Java反射機制
反射概念: 主要是指程序可以訪問,檢測和修改它本身狀態或行為的一種能力,並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
反射機制的作用:
1,反編譯:.class-->.java
2,通過反射機制訪問java對象的屬性,方法,構造方法等;
反射機制的類:
(Java反射相關的API在包java.lang.reflect中。)
java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
反射就是把Java的各種成分映射成相應的Java類。
1.3 過濾器和攔截器的區別
①攔截器是基於Java的反射機制的,而過濾器是基於函數回調。
②攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
③攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。
④攔截器可以訪問action上下文、值棧里的對象,而過濾器不能訪問。
⑤在action的生命周期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次。
⑥攔截器可以獲取IOC容器中的各個bean,而過濾器就不行,這點很重要,在攔截器里注入一個service,可以調用業務邏輯。
1.過濾器是JavaEE標准,采用函數回調的方式進行。是在請求進入容器之后,還未進入Servlet之前進行預處理,並且在請求結束返回給前端這之間進行后期處理。
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("before..."); chain.doFilter(request, response); System.out.println("after..."); }
chain.doFilter(request, response);這個方法的調用作為分水嶺。事實上調用Servlet的doService()方法是在chain.doFilter(request, response);這個方法中進行的。
2.攔截器是被包裹在過濾器之中的。
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); }
a.preHandle()這個方法是在過濾器的chain.doFilter(request, response)方法的前一步執行,也就是在 [System.out.println("before...")][chain.doFilter(request, response)]之間執行。 b.preHandle()方法之后,在return ModelAndView之前進行,可以操控Controller的ModelAndView內容。 c.afterCompletion()方法是在過濾器返回給前端前一步執行,也就是在[chain.doFilter(request, response)][System.out.println("after...")]之間執行。
3.SpringMVC的機制是由同一個Servlet來分發請求給不同的Controller,其實這一步是在Servlet的service()方法中執行的。所以過濾器、攔截器、service()方法,dispatc()方法的執行順序應該是這樣的,大致畫了個圖:其實非常好測試,自己寫一個過濾器,一個攔截器,然后在這些方法中都加個斷點,一路F8下去就得出了結論。
|
2.攔截器Interceptor
攔截器依賴的技術就是Java的動態代理。
2.1 模型分為以下模塊
業務組件:是被代理和被攔截的對象。
代理處理器:實現了InvocationHandler接口的一個對象
代理對象:Proxy對象。
攔截器:普通的JavaBean,在調用業務方法的之前或者之后會自動攔截並執行自己的一些方法。
客戶端:執行業務處理的入口。
2.2 模型的實現
一、業務組件:分為業務接口和業務
/** * 業務組件接口 */ public interface BusinessInterface { public void doSomething(); } /** * 業務組件 */ public class BusinessClass implements BusinessInterface{ public void doSomething() { System.out.println("業務組件BusinessClass方法調用:doSomething()"); } } |
|
二、代理處理器:包含了業務對象綁定動態代理類的處理,並實現了InvocationHandler接口的invoke方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 動態代理處理器工具
*/
public class DynamicProxyHandler implements InvocationHandler {
private Object business; //被代理對象
private InterceptorClass interceptor = new InterceptorClass(); //攔截器
/**
* 動態生成一個代理類對象,並綁定被代理類和代理處理器
* @param business
* @return 代理類對象
*/
public Object bind(Object business) {
this.business = business;
return Proxy.newProxyInstance(
//被代理類的ClassLoader
business.getClass().getClassLoader(),
//要被代理的接口,本方法返回對象會自動聲稱實現了這些接口
business.getClass().getInterfaces(),
//代理處理器對象
this);
}
/**
* 代理要調用的方法,並在方法調用前后調用連接器的方法.
*
* @param proxy 代理類對象
* @param method 被代理的接口方法
* @param args 被代理接口方法的參數
* @return 方法調用返回的結果
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
interceptor.before();
result=method.invoke(business,args);
interceptor.after();
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}
三、攔截器:普通的JavaBean,在調用業務方法的之前或者之后會自動攔截並執行自己的一些方法。
/**
* 攔截器
*/
public class InterceptorClass {
public void before(){
System.out.println("攔截器InterceptorClass方法調用:before()!");
}
public void after(){
System.out.println("攔截器InterceptorClass方法調用:after()!");
}
}
四、模擬客戶端:執行業務處理的入口。
/**
* 客戶端
*/
public class Client {
public static void main(String args[]) {
DynamicProxyHandler handler = new DynamicProxyHandler();
BusinessInterface business = new BusinessClass();
BusinessInterface businessProxy = (BusinessInterface) handler.bind(business);
businessProxy.doSomething();
}
}
2.3 SpringMVC中使用Interceptor攔截器
一、定義Interceptor實現類
SpringMVC 中的Interceptor 攔截請求是通過HandlerInterceptor 來實現的。在SpringMVC 中定義一個Interceptor 非常簡單,主要有兩種方式,第一種方式是要定義的Interceptor類要實現了Spring 的HandlerInterceptor 接口,或者是這個類繼承實現了HandlerInterceptor 接口的類,比如Spring 已經提供的實現了HandlerInterceptor 接口的抽象類HandlerInterceptorAdapter ;第二種方式是實現Spring的WebRequestInterceptor接口,或者是繼承實現了WebRequestInterceptor的類。
(一)實現HandlerInterceptor接口
HandlerInterceptor 接口中定義了三個方法,我們就是通過這三個方法來對用戶的請求進行攔截處理的。
(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值Boolean 類型的,當它返回為false 時,表示請求結束,后續的Interceptor 和Controller 都不會再執行;當返回值為true 時就會繼續調用下一個Interceptor 的preHandle 方法,如果已經是最后一個Interceptor 的時候就會是調用當前請求的Controller 方法。
(2)postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括后面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調用。postHandle 方法,顧名思義就是在當前請求進行處理之后,也就是Controller 方法調用之后執行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會后執行,這和Struts2 里面的Interceptor 的執行過程有點類型。Struts2 里面的Interceptor 的執行過程也是鏈式的,只是在Struts2 里面需要手動調用ActionInvocation 的invoke 方法來觸發對下一個Interceptor 或者是Action 的調用,然后每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執行的,而invoke 方法之后的內容就是反向的。
(3)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之后,也就是在DispatcherServlet 渲染了對應的視圖之后執行。這個方法的主要作用是用於進行資源清理工作的。
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;
public class SpringMVCInterceptor implements HandlerInterceptor {
/** * preHandle方法是進行處理器攔截用的,顧名思義,該方法將在Controller處理之前進行調用,SpringMVC中的Interceptor攔截器是鏈式的,可以同時存在多個Interceptor,然后SpringMVC會根據聲明的前后順序一個接一個的執行,而且所有的Interceptor中的preHandle方法都會在 Controller方法調用之前調用。SpringMVC的這種Interceptor鏈式結構也是可以進行中斷的,這種中斷方式是令preHandle的返回值為false,當preHandle的返回值為false的時候整個請求就結束了。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub return false; }
/** * 這個方法只會在當前這個Interceptor的preHandle方法返回值為true的時候才會執行。postHandle是進行處理器攔截用的,它的執行時間是在處理器進行處理之 * 后,也就是在Controller的方法調用之后執行,但是它會在DispatcherServlet進行視圖的渲染之前執行,也就是說在這個方法中你可以對ModelAndView進行操 * 作。這個方法的鏈式結構跟正常訪問的方向是相反的,也就是說先聲明的Interceptor攔截器該方法反而會后調用,這跟Struts2里面的攔截器的執行過程有點像, * 只是Struts2里面的intercept方法中要手動的調用ActionInvocation的invoke方法,Struts2中調用ActionInvocation的invoke方法就是調用下一個Interceptor * 或者是調用action,然后要在Interceptor之前調用的內容都寫在調用invoke之前,要在Interceptor之后調用的內容都寫在調用invoke方法之后。 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub
}
/** * 該方法也是需要當前對應的Interceptor的preHandle方法的返回值為true時才會執行。該方法將在整個請求完成之后,也就是DispatcherServlet渲染了視圖執行, * 這個方法的主要作用是用於清理資源的,當然這個方法也只能在當前這個Interceptor的preHandle方法的返回值為true時才會執行。 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub
}
} |
3.過濾器Filter
Servlet技術中的一種技術。
3.1 配置
在web.xml文件中配置該Filter,使用init-param元素為該Filter配置參數,init-param可接受如下兩個子元素:
param-name:指定參數名。
param-value:指定參數值。
filter 、filter-mapping 、servlet、servlet-mapping 才構成一個完整的攔截器配置。
Filter類需要實現Filter接口,該接口有init、doFilter、destroy3個方法,,3個方法順序執行。
1、新建一個類,實現Filter接口 2、實現doFilter()方法,打印一句話,來證明能夠進行攔截 3、在web.xml中進行配置(參照Servlet配置) 4、訪問一個頁面,看看能不能攔截 1>[java] view plaincopy package com.test.filter;
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class Demo1Filter implements Filter { private FilterConfig filterConfig;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Demo1過濾前"); System.out.println(filterConfig.getInitParameter("param1")); chain.doFilter(request, response);//放行。讓其走到下個鏈或目標資源中 System.out.println("Demo1過濾后"); }
public void init(FilterConfig filterConfig) throws ServletException { System.out.println("初始化了"); this.filterConfig = filterConfig; }
public void destroy() { System.out.println("銷毀了"); } } 2>在web.xml中進行配置 [html] view plaincopy <filter> <filter-name>Demo1Filter</filter-name> <filter-class>com.itheima.filter.Demo1Filter</filter-class> <init-param> <param-name>param1</param-name> <param-value>value</param-value> </init-param> </filter> <filter-mapping> <filter-name>Demo1Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <!-- 沒有配置dispatcher就是默認request方式的 --> <dispatcher>FORWARD</dispatcher> <dispatcher>ERROR</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> |
3.2 應用場景
1> 通過控制對chain.doFilter的方法的調用,來決定是否需要訪問目標資源。
比如,可以在用戶權限驗證等等。判斷用戶是否有訪問某些資源的權限,有權限放行,沒權限不執行chain.doFilter方法。
2> 通過在調用chain.doFilter方法之前,做些處理來達到某些目的。
比如,解決中文亂碼的問題等等。可以在doFilter方法前,執行設置請求編碼與響應的編碼。甚至可以對request接口進行封裝裝飾來處理get請求方式的中文亂碼問題(重寫相應的request.getParameter方法)。
3> 通過在調用chain.doFilter方法之后,做些處理來達到某些目的。
比如對整個web網站進行壓縮。在調用chain.doFilter方法之前用類A對response對象進行封裝裝飾,重寫getOutputStream和重寫getWriter方法。在類A內部中,將輸出內容緩存進ByteArrayOutputStream流中,然后在chain.doFilter方法執行后,獲取類A中ByteArrayOutputStream流緩存數據,用GZIPOutputStream流進行壓縮下。
3.3 實現攔截的原理
Filter接口中有一個doFilter方法,當開發人員編寫好Filter類實現doFilter方法,並配置對哪個web資源進行攔截后,WEB服務器每次在調用web資源的service方法之前(服務器內部對資源的訪問機制決定的),都會先調用一下filter的doFilter方法。
3.4 生命周期
和Servlet一樣Filter的創建和銷毀也是由WEB服務器負責。不過與Servlet區別的是,它是:
1>在應用啟動的時候就進行裝載Filter類(與Servlet的load-on-startup配置效果相同)。
2>容器創建好Filter對象實例后,調用init()方法。接着被Web容器保存進應用級的集合容器中去了等待着,用戶訪問資源。
3>當用戶訪問的資源正好被Filter的url-pattern攔截時,容器會取出Filter類調用doFilter方法,下次或多次訪問被攔截的資源時,Web容器會直接取出指定Filter對象實例調用doFilter方法(Filter對象常駐留Web容器了)。
4>當應用服務被停止或重新裝載了,則會執行Filter的destroy方法,Filter對象銷毀。
注意:init方法與destroy方法只會直接一次。