補struts2.1兩天快速入門之輕鬆搞定struts2核心--攔截器

由於攔截器的重要性,決定將其從(struts2.1兩天快速入門第一天下午 抽取出來講)

 

本講將結合模擬用戶權限驗證展開:判斷用戶是否有權限請求訪問某一模塊或頁面.

 

第九講、自定義攔截器

 

   9.1 首先定義一個User實體類,如下:

public class User implements Serializable {
	private String username;
	private String password;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

  9.2 編寫一個用戶登陸的Action類,如下:

public class UserLoginAction {
	//接收復合類型的參數,與struts1.x相類似,此時表單的元素名稱
	//應該爲:user.username   user.password
	private User user;  
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public String execute(){
	//模擬用戶登陸,實際應用是從數據庫裏取的,這裏只是爲了方便測試和學習	
	if("yulon".equals(user.getUsername())&&"123456".equals(user.getPassword())){
		//爲了不讓我們看到真正的session,struts2框架對其作了一層封裝,用一個Map對象來存儲.
		//Map map = ActionContext.getContext().getSession();
		//map.put("user", user);
		//把兩段代碼合成一段,就不用導入Map類
		ActionContext.getContext().getSession().put("user", user);
		return Action.SUCCESS;   //返回到歡迎頁面
	 }
		ActionContext.getContext().put("msg", "用戶登陸失敗!");
		//返回到登陸頁面,struts定義了一系列字符串常量,方便用戶使用,統一的好處
		return Action.LOGIN;   
	}
}

    知識提示:注意這裏返回的是login,而不是LOGIN.查看ActionContext部分源碼及相關文檔可知,它是一個與線程相關的類,同一個線程內獲取的都是同一個ActionContext實例,原理與ThreadLocal相關,之前有講過ThreadLocal相關知識,ThreadLocal實現在同一個線程內的數據共享,可以更深入得理解ActionContext底層的實現原理.

 

  9.3  在struts.xml配置文件裏新增一個package包,名稱叫testinterceptor,命名空間定義爲/test2,如下配置:     
 

  <package name="testinterceptor" namespace="/test2" extends="struts-default">
      <!--定義一個轉發到登陸頁面的Action-->
    	<action name="loginUI">
    		<result>/WEB-INF/jsp/login.jsp</result>
    	</action>
    	<action name="login" class="cn.gkit.action.UserLoginAction" method="execute">
    	  <!--重定向到同一個包內名字叫index的Action-->
    		<result name="success" type="redirectAction">index</result>
    		<result name="login">/WEB-INF/jsp/login.jsp</result>
    	</action>
    	<!--爲了方便測試,同時也定義一個登出的Action-->
    	<action name="logout" class="cn.gkit.action.UserLogoutAction" method="execute">
    		<result name="success" type="redirectAction">loginUI</result>
    	</action>
    	<!--用戶登陸成功後的歡迎頁面-->
    	<action name="index">
    		<result name="success">/WEB-INF/jsp/welcome.jsp</result>
    		<result name="login" type="redirectAction">loginUI</result>
    	</action>
  </package>

  9.4 編寫UserLogoutAction類

public class UserLogoutAction {
	public String execute(){
		//取出當前登陸的用戶
		User user = (User)ActionContext.getContext().getSession().get("user");
		if(user!=null){
			//註銷用戶信息
			ActionContext.getContext().getSession().remove("user");
		}
		return Action.SUCCESS;
	}
}

  9.5 編寫\WEB-INF\jsp\login.jsp頁面

 

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>用戶登陸</title>
  </head>
  <span>${msg}</span>
  <body>
    <s:form action="login" namespace="/test2" method="post">
      用戶名: <s:textfield title="填寫用戶名" name="user.username" ></s:textfield><br/>
      密   碼 :<s:password title="填寫密碼" name="user.password"></s:password><br/>
        <s:submit value="登陸"></s:submit>
    </s:form>
  </body>
</html>

    知識提示:使用struts2標籤首先在導入相關uri, 可以在struts2核心包下的META-INF目錄下找到struts-tags.tld文件,裏面就有uri的定義,具體標籤的使用暫不在本章細講,大家可以先琢磨一下.

  9.6 編寫\WEB-INF\jsp\welcome.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>歡迎首頁</title>
  </head>
  <body>
    歡迎用戶${sessionScope.user.username}的到來!<br />
 <a href='<s:url action="logout" namespace="/test2" />'>退出</a>
  </body>
</html>

  

在沒有使用攔截器之前,是可以直接訪問/struts2.1/test2/index請求的.

 

  9.7實現自己的攔截器cn.gkit.web.interceptor.UserAuthInterceptor(有三種方法)

 

    第一種方法是實現com.opensymphony.xwork2.interceptor.Interceptor接口,實現相應的方法就行.

public class UserAuthInterceptor implements Interceptor{

	public void destroy() {
	}
	public void init() {
 init() 		System.out.println("服務器啓動過程中自動加載");
	}

	public String intercept(ActionInvocation invocation) throws Exception {
		 //ActionContext.getContext().getSession().get("user");
		Map map = invocation.getInvocationContext().getSession();
		if(map.get("user")==null){
System.out.println("用戶還沒登陸");
			return Action.LOGIN;  //struts定義了一系統字符串常量,注意這裏返回的是login,而不是LOGIN.
		}else
		{
			String result = invocation.invoke();//如果用戶已登陸就通過驗證,繼承執行下一個攔截器
			System.out.println("返回結果:"+result);
			return result; 
		}
	}
}

    知識提示:invocation.getInvocationContext()獲取到的是一個與ActionInvocation相關的ActionContext ,但使用ActionContext.getContext()也一樣可以取得.

 

   第二種方法是繼承AbstractInterceptor抽象,此類的設計思想跟之前講的類型轉換器類似.它本身也是實現了Interceptor接口,由於我們一般都不用到init()方法和destroy()方法,因此通常情況下都是通過繼承AbstractInterceptor來實現我們的攔截器.如下:

public class UserAuthInterceptor extends AbstractInterceptor{
               @Override
	public String intercept(ActionInvocation invocation) throws Exception {
		 //ActionContext.getContext().getSession().get("user");
		Map map = invocation.getInvocationContext().getSession();
		if(map.get("user")==null){
System.out.println("用戶還沒登陸");
			return Action.LOGIN;  
		}else
		{
			String result = invocation.invoke();
			System.out.println("返回結果:"+result);
			return result; 
		}
	}
}

   知識提示:查看AbstractInterceptor源碼,你會感到驚訝,如下:

 public abstract class AbstractInterceptor implements Interceptor {

    public void init() {
    }

    public void destroy() {
    }
    public abstract String intercept(ActionInvocation invocation) throws Exception;
}

    知識提示:你沒有看錯,就是幾行代碼,它只是幫我們實現了兩個不常用到的方法,並且還是空實現. 但裏面包含的一種設計思想值得我們去學習研究,所以說學習框架的最高境界是學習它的思想,思想就存在代碼當中,大家有時間可以多查看一下它的源碼.

 

  9.8 將我們編寫好的攔截器類加載到struts.xml配置文件中,如下定義:

 

<interceptors>
     <!-- 加載自己編寫的攔截器 -->
    <interceptor name="authInterceptor" class="cn.gkit.web.interceptor.UserAuthInterceptor" />
</interceptors>

 

    9.9 將我們編寫好的攔截器應用到具體的action類上,將struts.xml 名稱爲index的<action>改如下:

 

<action name="index">
    	<result name="success">/WEB-INF/jsp/welcome.jsp</result>
    	<result name="login" type="redirectAction">loginUI</result>
    	<interceptor-ref name="authInterceptor"></interceptor-ref>
</action>

    此時:若訪問/struts2.1/test2/index,請求將先會被自己定義的authInterceptor攔截器攔截,執行intercept方法的代碼,判斷用戶若沒有登陸則轉到登陸頁面,若存在纔會放行(即執行下一步,這裏的下一步會執行請求對應的Action方法).

    問題:爲什麼說攔截器是struts2的核心呢?是因爲struts2的很多工作都是通過攔截器來實現的,在你定義的<package>的同時,只要你extends 了struts-default包,就同時擁有了struts-default包內定義的全部內容.通過查看struts-default.xml文件可知,它裏面定義了很多攔截器,每個攔截器負責完成不同的工作.如裏面名字爲params的攔截器的作用是會將頁面表單的參數會自動賦值到action裏的屬性。其中在最後定義了一個默認的攔截器棧<default-interceptor-ref name="defaultStack"/>,默認情況下默認攔截器棧會應用到包內定義的所有action身上. 但如果你手工在一個<action>添加一個額外的攔截器後,此時默認的攔截器棧對本<action>不起作用.解決的辦法如下:

     第一種解決方案:重新引入defaultStack攔截器棧,將index改如下:

<action name="index">
    	<result name="success">/WEB-INF/jsp/welcome.jsp</result>
    	<result name="login" type="redirectAction">loginUI</result>
    	<interceptor-ref name="authInterceptor"></interceptor-ref>
    	<!-- 重新引入 defaultStack-->
    	<interceptor-ref name="defaultStack"></interceptor-ref>
</action>

    第二種解決方案:定義自已的攔截器棧,如下:

<interceptors>
    <!-- 加載自己編寫的攔截器 -->
    <interceptor name="authInterceptor" class="cn.gkit.web.interceptor.UserAuthInterceptor" />
    <!-- 定義攔截器棧 -->
    <interceptor-stack name="myInterceptorStack">
    	<!-- 引用用戶定義的攔截器 -->
    	<interceptor-ref name="authInterceptor"></interceptor-ref>
    	<!-- 同時也要重新把默認的攔截器棧引入進來 -->
    	<interceptor-ref name="defaultStack"></interceptor-ref>
    </interceptor-stack>
</interceptors>

    將自己定義好的攔截器棧應用到具體action類中,如將<action>改寫成:

   

 <action name="index">
    	<result name="success">/WEB-INF/jsp/welcome.jsp</result>
    	<result name="login" type="redirectAction">loginUI</result>
    	<!-- 引入自定義攔截器棧 -->
    	<interceptor-ref name="myInterceptorStack"></interceptor-ref>
 </action>

   第三種解決方案:重新定義默認攔截器棧

  

<interceptors>
    <!-- 加載自己編寫的攔截器 -->
    <interceptor name="authInterceptor" class="cn.gkit.web.interceptor.UserAuthInterceptor" />
    <!-- 定義攔截器棧 -->
    <interceptor-stack name="myInterceptorStack">
    	<!-- 引用用戶定義的攔截器 -->
    	<interceptor-ref name="authInterceptor"></interceptor-ref>
    	<!-- 同時也要重新把默認的攔截器棧引入進來 -->
    	<interceptor-ref name="defaultStack"></interceptor-ref>
    </interceptor-stack>
</interceptors>
 <!-- 重新定義默認的攔截器棧,覆蓋掉struts-default定義的默認攔截器棧 -->
<default-interceptor-ref name="myInterceptorStack"></default-interceptor-ref>

    注意:此時在testinterceptor包內定義的所有<action>默認都會被myInterceptorStack攔截器攔截,index不用重新定義攔截器,改如下:

<action name="index">
    <result name="success">/WEB-INF/jsp/welcome.jsp</result>
    <result name="login" type="redirectAction">loginUI</result>
</action>

 

    具體使用哪一種方法按項目需求而定

 

 9.10 在 9.7節裏面還有一種自定義攔截器的方法沒講到:就是方法過濾攔截器(MethodFilterInterceptor)

     

     編寫方法過濾攔截器第一步:編寫繼承於MethodFilterInterceptor抽象類的攔截器

cn.gkit.web.interceptor.MyMethodFilterInterceptor,如下:
public class MyMethodFilterInterceptor extends MethodFilterInterceptor {

	@Override
	protected String doIntercept(ActionInvocation invocation) throws Exception {
		System.out.println("執行了MyMethodFilterInterceptor攔截器");
		String resultString = invocation.invoke();
		System.out.println("返回的結果:"+resultString);
		return resultString;
	}
}
 第二步,在上次編寫的test包內加載這個攔截器,如下:
<interceptors>
  <interceptor name="mymethodinterceptor" class="cn.gkit.web.interceptor.MyMethodFilterInterceptor"></interceptor>
</interceptors>
 第三步,在具體的action裏應用該攔截器,如下:
<package name="test" namespace="/test" extends="gkit">
   <interceptors>
   	<!-- 定義方法過濾攔截器 -->
   	<interceptor name="mymethodinterceptor" class="cn.gkit.web.interceptor.MyMethodFilterInterceptor"/>
   </interceptors>
    <action name="*User" class="cn.gkit.action.HelloWorldAction" method="{1}" >
        	<param name="message">屬性注入</param>
	<result name="success">/WEB-INF/jsp/helloworld.jsp</result> 
	<interceptor-ref name="mymethodinterceptor">
	<!--指定要攔截的方法  includeMethods的優先級比excludeMethods的要高-->
		<param name="includeMethods">add,execute</param>
	</interceptor-ref>
	<interceptor-ref name="defaultStack"></interceptor-ref>
     </action>
</package>
   知識提示:MethodFilterInterceptor有兩個重要的屬性 excludeMethods includeMethods 排除/包含action裏的某個業務方法,多個方法用逗號分開.以上配置表示:cn.gkit.action.HelloWorldAction內的add與execute方法將會被mymethodinterceptor攔截器攔截.
攔截器至此已基本上講完,謝謝大家的閱讀!也希望大家能從中學到東西.如果有某處地方看不懂,大家可以與我一起討論!
 
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章