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(); 可以返回整個請求路徑的名稱