Spring AnnotationMethodHandlerAdapter和annotation-reading拦截器

时间:2011-02-03 00:18:05

标签: spring spring-mvc

我有一个基本的Spring MVC控制器,如下所示:

@Controller
public void MyController {
     @RequestMapping("/secret")
     public String show() {
         return "secret.jsp";
     }
}

我将有几个类似的网址,只有登录用户才能访问。由于这是一个跨领域的问题,我想使用AOP,我想通过注释来完成这项工作。换句话说,我想在每个需要保密的控制器方法上抛出@RequiresLogin注释。

AnnotationMethodHandlerAdapter支持拦截器的概念,这似乎是正确的方法。但是,我想知道将调用哪个方法,以便我可以检查它是否为@RequiresLogin注释。我看到有一个传入的“对象处理程序”参数,但我不知道如何将其转换为将被调用的类和方法。

想法?

5 个答案:

答案 0 :(得分:2)

没有好方法可以在拦截器中获得方法签名。

尝试将常规AOP建议应用于您的控制器,只要使用目标类代理,Spring MVC就可以很好地使用它。

答案 1 :(得分:1)

正如axtavt正确编写的那样,如果使用proxy-target-class,Spring-AOP可以很好地与控制器配合使用。但是,如果你遵循一些(繁琐的)约定,也有可能使用JDK代理:

  

使用基于接口的@Controller类

     

与他人合作时常见的陷阱   发生了带注释的控制器类   当应用功能时   需要为其创建代理代理   控制器对象(例如   @Transactional方法)。通常你   将介绍一个接口   控制器为了使用JDK动态   代理。 要做到这一点,你必须这样做   移动@RequestMapping注释   到界面作为映射   机制只能“看到”界面   由代理公开。作为一个   另外,你可以选择   激活proxy-target-class =“true”   的配置   适用于的功能   控制器(在我们的交易中   <tx:annotation-driven />)中的场景。   这样做表示基于CGLIB   应该使用子类代理   而不是基于接口的JDK   代理。有关的更多信息   各种代理机制见   第7.6节,“Proxying mechanisms”

来源: 15.3.2 Mapping requests with @RequestMapping

答案 2 :(得分:1)

虽然使用spring security是最佳方法,但您可以使用Spring Aspects实现类似的功能。以下是使用Aspect检查包含特定注释的方法的示例。

@Aspect
public class MyAspect {
    @Around("execution(* com.test.controllers..*.**(..)) && " +
            "within(@org.springframework.sterotype.Controller *)")
    public Object execute(ProceedingJoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        if (target != null) {
            Signature tSig = joinPoint.getSignature();
            if (tSig instanceof MethodSignature) {
                MethodSignature mSig = (MethodSignature) tSig;
                Method method = mSig.getMethod();
                if (method != null && method.isAnnotationPresent(MyAnnotation.class)) {
                    // do something
                    // parameters are available from joinPoint.getArgs();
                }
            }
        }
    }
    // allow method invocation to continue
    return joinPoint.proceed();
}

@Around建议的格式将特定于您的应用程序。在此示例中,它将检查com.test.controllers包和所有子包中使用Controller注释的任何类。有关其他选项,请参阅http://static.springsource.org/spring/docs/3.0.x/reference/aop.html

祝你好运!

答案 3 :(得分:1)

ResolveHandlerMethodInterceptor如何使用反射。 下面的代码是实验性的和版本相关的(spring 3.0.2)。

import java.lang.reflect.Method;

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

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;

public class ResolveHandlerMethodInterceptor implements HandlerInterceptor {
    public final static String HANDLER_METHOD = "handlerMethod";
    // Here is your servlet name
    public final static String SERVLET_NAME = "XXXXX";


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView)
            throws Exception {
        Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD);
        System.out.println("postHandle>>>" + handlerMethod);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception)
            throws Exception {
        Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD);
        System.out.println("afterCompletion>>>" + handlerMethod);
    }

    @Override   
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {

        ServletContext servletContext = request.getSession().getServletContext();
        String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + SERVLET_NAME;
        WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext, attrName);
        AnnotationMethodHandlerAdapter adapter = context.getBean(AnnotationMethodHandlerAdapter.class);     

        Method getMethodResolverMethod = adapter.getClass().getDeclaredMethod("getMethodResolver", Object.class);
        getMethodResolverMethod.setAccessible(true);
        Object servletHandlerMethodResolver = getMethodResolverMethod.invoke(adapter, object);

        Method resolveHandlerMethod = servletHandlerMethodResolver.getClass().getMethod("resolveHandlerMethod", HttpServletRequest.class);
        resolveHandlerMethod.setAccessible(true);
        Method handlerMethod = (Method) resolveHandlerMethod.invoke(servletHandlerMethodResolver, request);
        request.setAttribute(HANDLER_METHOD, handlerMethod);

        System.out.println("preHandle>>>" + handlerMethod);

        return true;
    }
}

==参考==
http://toby.epril.com/?p=934
http://www.jarvana.com/jarvana/view/org/springframework/spring-webmvc/3.0.2.RELEASE/spring-webmvc-3.0.2.RELEASE-sources.jar!/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java?format=ok

答案 4 :(得分:0)

因此,列出的这些方法很好,但它们都有局限性。 AOP的东西是一个好主意,但它的局限在于,如果我想重定向或修改响应,我需要一种方法来获取请求和响应对象。控制器方法不一定需要请求和响应,并且要求它们看起来不够优雅。我可以使用spring magic从Aspect获取请求对象,但我找不到获得响应的方法。

最终,我提出了一个中途。我使用过滤器bean来获取请求和响应对象,并将它们存储在ThreadLocal中。然后我创建了一个引用该过滤器的方面,以便它可以轻松地查看请求和响应对象。

然后我根据注释使方法包围方法,所以我甚至不需要使用代码检查注释是否存在。

这种组合方法看起来效果很好!

唯一的缺点是,我无法找到一种编写集成测试的好方法,该测试验证在对该URL有传入请求时调用该方面。删除单个注释会使我的所有测试都通过但允许未经授权的用户通过,这有点可怕。

感谢大家提出的好建议!