1. 程式人生 > >springmvc學習筆記(28)——自定義攔截器

springmvc學習筆記(28)——自定義攔截器

1. 自定義攔截器

所謂的攔截器,就是用來攔截請求,因此我們可以對使用者發來的請求做處理。 

  • 寫一個類,實現攔截器的介面
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class FirstInterceptor implements HandlerInterceptor{

    /**
     * 該方法在 目標方法之前被呼叫
     * 可以用來做 許可權,日誌,事務
     * 若 return false,後續的攔截器和目標方法將不被呼叫
     * 若 return true,後續的攔截器和目標方法正常呼叫
     */
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
            Object arg2) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    /**
     * 該方法在 目標方法之後,渲染檢視之前 被呼叫
     * 可以用來 修改請求域,修改目標檢視等
     */
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
            Object arg2, ModelAndView arg3) throws Exception {
        System.out.println("postHandle");

    }
    /**
     * 該方法在 目標方法被呼叫,渲染檢視之後被呼叫
     * 可以用來 釋放資源
     */
    @Override
    public void afterCompletion(HttpServletRequest arg0,
            HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("afterCompletion");

    }


}

程式碼中已經有了詳細的註釋,包括三個方法的呼叫順序方法 
在三個方法中打印出一句話,以便我們能看到效果

  讓攔截器起作用,就要在springmvc配置檔案中配置

     <!-- 攔截器 -->
    <mvc:interceptors>
        <!-- 配置自定義的攔截器 -->
        <bean class="com.zj.interceptor.FirstInterceptor"></bean>
    </mvc:interceptors>

 到這裡,配置就完成了,訪問任意目標方法,控制檯打印出如下結果 

 2. 配置攔截器的作用範圍

 <mvc:interceptors>

     <!-- 配置自定義的攔截器 -->
     <bean class="com.zj.interceptor.FirstInterceptor"></bean>

      <mvc:interceptor>
         <!-- 只有訪問/hello這個路徑,攔截器才起作用 -->
         <mvc:mapping path="/hello"/>
         <!-- 
            除了/index這個路徑,其他都起作用
            <mvc:exclude-mapping path="/index"/>
         -->
         <bean class="com.zj.interceptor.SecondInterceptor"></bean>
      </mvc:interceptor>
  </mvc:interceptors>

 

在最外層是一個mvc:interceptors,在裡面,我們又配置了一個mvc:interceptor。請注意,前者有s,後者沒有。
在mvc:interceptor中,我們可以配置攔截器SecondInterceptor的作用範圍,只作用於某個路徑或者只有某個路徑不被作用

3. 多個攔截器的呼叫順序

上面我們配了兩個攔截器,現在我們來測試以下他們的呼叫順序

訪問某個路徑,控制檯打印出如下結果 

  • 在配置檔案中,FirstInterceptor攔截器的配置在SecondInterceptor前面
  • 根據結果分析可以得到,preHandle()方法的呼叫順序和攔截器的配置順序一致
  • postHandle()和afterCompletion()方法的呼叫順序則是反序

4. 特殊情況


剛剛的例子中,FirstInterceptor和SecondInterceptor兩個攔截器的preHandler方法都是return true,因此後面的方面都可以正常執行。

現在假設SecondInterceptor的preHandle返回false,會怎樣?我們來看結果截圖 

 

1. 從結果中看到,FirstInterceptor 的preHandle先被執行,這沒問題

2.然後執行SecondInterceptor的preHandle,它返回false,因此後續的目標方法都不被執行,但是,FirstInterceptor的afterCompletion方法居然被執行了。
3.如果從原始碼分析,可以明白這是為什麼,但是對於大多數人來說這太難。
4.我們從這個結果中總結出一個結論:一個攔截器一旦preHandle被執行return true,那麼它就一定要釋放資源(之前說過afterCompletion方法用來釋放資源)
 

======================================================

根據上面的分析下面我寫了一個不使用xml進行攔截路徑的案例。

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.xxxx.dkyw.moudles.doctorinfo.service.DoctorInfoService;
import com.xxxx.dkyw.moudles.hosinfo.service.HosInfoService;
import com.xxxx.dkyw.moudles.examorg.service.ExamOrgInfoService;
import com.xxxx.dkyw.moudles.organizationinfo.service.OrganizationInfoService;
import com.xxxx.dkyw.core.extensions.process.service.ProcessService;
import com.xxxx.dkyw.persistence.entity.HosInfo;
import com.xxxx.dkyw.persistence.entity.OrganizationInfo;
import com.xxxx.dkyw.persistence.entity.Process;

@Component
public class AdminPermOrgInterceptor implements HandlerInterceptor {
	@Autowired
	private DoctorInfoService doctorInfoService;
	@Autowired
	private ProcessService processService;
	@Autowired
	private HosInfoService hosInfoService;
	@Autowired
	private OrganizationInfoService organizationInfoService;
	@Autowired
	private ExamOrgInfoService examOrgInfoService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}

	@SuppressWarnings("static-access")
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		String absolutePath = request.getScheme()+"://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
		String url = request.getRequestURL().toString();
		if(request.getMethod().toUpperCase().equals("GET")) {
			if(StringUtils.containsAny(url, "/doctor/", "/exam/", "/examorg/", "/hos/", "/org/") && 
					StringUtils.containsAny(url, "edit", "view")) {
				Map<String, Object> model = modelAndView.getModel();
				
				HandlerMethod methodHandler = (HandlerMethod) handler;
				Class<?>[] parameterTypes = methodHandler.getMethod().getParameterTypes();
				Class<?> beanClass = parameterTypes[0];
				
				String beanName = beanClass.getName();
				char[] simpleName = beanClass.getSimpleName().toCharArray();
				simpleName[0] += 32;
				
				Object value = model.get(String.valueOf(simpleName));
				Class<?> valueClass = value.getClass().forName(beanName);
				Field[] fields = valueClass.getDeclaredFields();
				String uuid = null;
				String orgUuid = null;
				List<String> orgUuids = null;
				for(Field f: fields) {
					f.setAccessible(true);
					if(f.getName().equals("uuid")) {
						uuid = f.get(value).toString();
						break;
					}
				}
				
				if(StringUtils.contains(url, "/doctor/")) {	//	醫師資訊
//					DoctorBaseInfo doctorBaseInfo = doctorInfoService.getDoctorBaseInfoByUuid(uuid);
//					orgUuid = doctorBaseInfo.getOrgUuid();
					orgUuid = doctorInfoService.getDoctorOrgUuid(uuid);
				}else if(StringUtils.contains(url, "/exam/")) {	//	考核進度資訊
					Process process = processService.getProcessByUuid(uuid);
					orgUuid = process.getOrgUuid();
				}else if(StringUtils.contains(url, "/hos/")) {	//	衛生機構資訊
					HosInfo hosInfo = hosInfoService.getHosInfoByUuid(uuid);
					orgUuid = hosInfo.getOrgUuid();
				}else if(StringUtils.contains(url, "/org/")) {	//	行政機構資訊
					OrganizationInfo orgInfo = organizationInfoService.getOrgInfoByUuid(uuid);
					orgUuid = orgInfo.getFirstUuid();
				}else {	//	考核機構資訊
					orgUuids = examOrgInfoService.getBelongOrgIdByUuid(uuid);
				}
				
				List<String> permOrgList = (List<String>) request.getSession().getAttribute("SESSION_ORG_LIST");
				
				if(CollectionUtils.isNotEmpty(permOrgList)) {
					if(StringUtils.isNotEmpty(orgUuid)) {
						OrganizationInfo org = organizationInfoService.findOrgFirstUuid(orgUuid);
						orgUuid = org.getUuid();
						
						if(!permOrgList.contains(orgUuid)) {	//	當前使用者的機構許可權無法檢視使用者的情況
//							response.sendError(HttpServletResponse.SC_FORBIDDEN, "沒有許可權的操作。");
							
							response.sendRedirect(absolutePath + "/a/forbidden");
						}
					}else if(CollectionUtils.isNotEmpty(orgUuids)) {
						List<OrganizationInfo> orgs = organizationInfoService.queryOrgsByUuidList(orgUuids);
						for(OrganizationInfo o: orgs) {
							
							if(!permOrgList.contains(o.getFirstUuid())) {
								response.sendRedirect(absolutePath + "/a/forbidden");
							}
						}
					}else {
						response.sendRedirect(absolutePath + "/a/forbidden");
					}
				
					
				}
			}
		}
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}

}

request.getSchema() 可以返回當前頁面使用的協議,http 或是 https;

request.getServerName() 可以返回當前頁面所在的伺服器的名字;

request.getServerPort() 可以返回當前頁面所在的伺服器使用的埠

request.getContextPath() 可以返回當前頁面所在的應用的名字

request.getRequestURL().toString(); 可以返回整個請求路徑的名稱