Spring攔截器中通過request獲取到該請求對應Controller中的method對象


背景:項目使用Spring 3.1.0.RELEASE,從dao到Controller層全部是基於注解配置。我的需求是想在自定義的Spring攔截器中通過request獲取到該請求對應於Controller中的目標method方法對象。Controller和攔截器代碼如下:

AdminController

@Controller
@RequestMapping("/admin")
public class AdminController {

    /**
     * init:初始頁面. <br/>
     *
     * @author chenzhou
     * @param request 請求
     * @param response 響應
     * @return 登陸頁
     * @since JDK 1.6
     */
    @RequestMapping("/init")
    public ModelAndView init(HttpServletRequest request,
            HttpServletResponse response){
        Map<String, Object> model = new HashMap<String, Object>();
        List<Role> roleList = this.adminService.getRoleList();
        model.put("roleList", roleList);
        return new ModelAndView(this.getLoginPage(), model);
    }

    //……
}

LoginInterceptor

public class LoginInterceptor extends HandlerInterceptorAdapter {
    /**
     * This implementation always returns <code>true</code>.
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        return true;
    }

    /**
     * This implementation is empty.
     */
    public void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }

    /**
     * This implementation is empty.
     */
    public void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}

servlet xml配置文件定義:

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> 
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
        <list>
            <bean class="com.chenzhou.examples.erm.util.interceptor.LoginInterceptor"/>
        </list>
    </property>
</bean>

我的需求是想在preHandle方法中通過request獲取該請求訪問的目標Controller中的方法對象。之前找了很久也沒有找到比較好的方案,就采取了最老土的通過比較requestURL和Controller類和方法上的RequestMappingURL來進行獲取,這樣也能勉強實現,但是這種方式我自己都覺得特別惡心。首先,這種方式需要使用反射來獲取Controller中的所有方法,然后遍歷method數組,逐個進行RequestMappingURL的比對,效率低下。其次,如果RequestMapping定義了類似於@RequestMapping("/{id}")這種動態參數url,則無法進行比較。

     因為上面這種方式不好,我就一直想找一個更好的方案。不得已只能向人求助,第一個就想到了Iteye上對於Spring研究得很熟悉的jinnianshilongnian龍年兄,我相信經常上iteye的博友們對龍年兄應該都很熟悉。龍年兄給了我一個方案,就是通過把handler對象轉換為HandlerMethod類型,然后直接getMethod,代碼如下:

/**
 * This implementation always returns <code>true</code>.
 */
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    System.out.println("*********************preHandle********************");
    System.out.println(handler.getClass());
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    System.out.println(handlerMethod.getMethod());
    return true;
}

注:HandlerMethod類是Spring 3.1.0.RELEASE版本中才有的,之前我使用的Spring 3.0.6.RELEASE版本,里面是找不到這個類的

根據龍年兄提供的方法,測試之后報錯,報錯信息如下:

*********************preHandle********************
class com.chenzhou.examples.erm.web.AdminController
2012-10-21 16:28:25 org.apache.catalina.core.StandardWrapperValve invoke
嚴重: Servlet.service() for servlet erm threw exception
java.lang.ClassCastException: com.chenzhou.examples.erm.web.AdminController cannot be cast to org.springframework.web.method.HandlerMethod
    at com.chenzhou.examples.erm.util.interceptor.LoginInterceptor.preHandle(LoginInterceptor.java:37)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:891)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
……

   根據錯誤提示可以看出是HandlerMethod handlerMethod = (HandlerMethod) handler;這一步報錯了,根據System.out.println(handler.getClass());打印的結果可以得知handler是該請求訪問的Controller類,無法轉換成HandlerMethod對象。這次還是龍年兄幫我找出了原因,解決方案是使用

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> 
替換 
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> 

因為DefaultAnnotationHandlerMapping只能返回Controller對象,不會映射到Controller中的方法級別。替換之后servlet xml配置如下:

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> 
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> 
    <property name="interceptors">
        <list>
            <bean class="com.chenzhou.examples.erm.util.interceptor.LoginInterceptor"/>
        </list>
    </property>
</bean>

  重啟tomcat測試之后發現再次報錯,報了另外一個錯誤,具體信息如下:

2012-10-21 16:39:39 org.apache.catalina.core.StandardWrapperValve invoke
嚴重: Servlet.service() for servlet erm threw exception
javax.servlet.ServletException: No adapter for handler [public org.springframework.web.servlet.ModelAndView com.chenzhou.examples.erm.web.AdminController.init(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)]: Does your handler implement a supported interface like Controller?
……

    這一次,請求根本沒有到達攔截器容器就已經報錯了,錯誤提示的意思是找不到handler對象對應的Adapter類。我在RequestMappingHandlerMapping類對應的spring-webmvc-3.1.0.RELEASE.jar 包里找到了該類對應的Adapter類:RequestMappingHandlerAdapter,然后在servlet xml中進行了配置:

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> 
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> 
    <property name="interceptors">
        <list>
            <bean class="com.chenzhou.examples.erm.util.interceptor.LoginInterceptor"/>
        </list>
    </property>
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

然后重新啟動tomcat后訪問http://localhost:8080/erm/admin/init 結果正常,控制台日志信息如下:

Shell代碼

*********************preHandle********************
class org.springframework.web.method.HandlerMethod
public org.springframework.web.servlet.ModelAndView com.chenzhou.examples.erm.web.AdminController.init(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)

    從日志信息可以看出,handler對象在經過類型轉換后轉換成了HandlerMethod類型,通過handler.getMethod方法,獲取到了該請求訪問的方法為com.chenzhou.examples.erm.web.AdminController.init

     注:非常感謝jinnianshilongnian 開濤兄的幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM