Spring AnnotationMethodHandlerAdapter и перехватчики для чтения аннотаций - PullRequest
1 голос
/ 03 февраля 2011

У меня есть базовый контроллер Spring MVC, который выглядит следующим образом:

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

У меня будет несколько похожих URL-адресов, которые могут быть доступны только зарегистрированным пользователям.Поскольку это междисциплинарная проблема, я бы хотел использовать АОП, и я хотел бы сделать это с помощью аннотаций.Другими словами, я хотел бы добавить аннотацию @RequiresLogin для каждого метода контроллера, который должен быть секретным.

AnnotationMethodHandlerAdapter поддерживает концепцию перехватчиков, которая на первый взгляд кажется правильным путем дляэтот.Тем не менее, я хочу знать, какой метод будет вызываться, чтобы я мог проверить его для моей @RequiresLogin аннотации.Я вижу, что есть параметр «Object handler», который передается, но я не уверен, как превратить его в класс и метод, который будет вызываться.

Идеи?

Ответы [ 5 ]

2 голосов
/ 03 февраля 2011

Нет хороших способов получить сигнатуру метода в перехватчике.

Попробуйте применить регулярный совет AOP к вашему контроллеру, Spring MVC хорошо с ним работает, пока используется проксирование целевого класса.

1 голос
/ 11 августа 2011

Как насчет ResolveHandlerMethodInterceptor с использованием отражения. Код ниже является экспериментальным и зависит от версии (весна 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

1 голос
/ 03 февраля 2011

Несмотря на то, что использование Spring Security было бы оптимальным подходом, вы можете реализовать аналогичную функциональность, используя Spring Aspect. Вот пример использования 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 будет зависеть от вашего приложения. В этом примере он проверяет наличие любого класса, аннотированного с помощью Controller, в пакете com.test.controllers и всех подпакетах. Смотрите http://static.springsource.org/spring/docs/3.0.x/reference/aop.html для дополнительных опций.

Удачи!

1 голос
/ 03 февраля 2011

Как правильно пишет axtavt, Spring-AOP хорошо работает с контроллерами, если используется proxy-target-class. Но есть также возможность использования JDK-прокси, если вы следуете некоторым (утомительным) соглашениям:

Работа с интерфейсными классами @Controller

Распространенная ловушка при работе с аннотированные классы контроллеров происходит при применении функциональности, которая требует создания прокси-прокси для объект контроллера (например, @Transactional методов). Обычно ты представит интерфейс для контроллер для использования JDK динамического прокси. Чтобы сделать эту работу, вы должны переместить @RequestMapping аннотации к интерфейсу в качестве отображения механизм может только «видеть» интерфейс выставлен по доверенности. Как Альтернативно, вы можете выбрать активировать proxy-target-class = "true" в конфигурация для функциональность применяется к контроллер (в нашей транзакции сценарий в <tx:annotation-driven />). Это указывает на то, что на основе CGLIB должны использоваться прокси подкласса вместо основанного на интерфейсе JDK прокси. Для получения дополнительной информации о см. различные механизмы проксирования Раздел 7.6, «Механизмы прокси» .

Источник: 15.3.2 Отображение запросов с помощью @ RequestMapping

0 голосов
/ 03 февраля 2011

Итак, перечисленные подходы хороши, но все они имеют ограничения.AOP - хорошая идея, но его ограничение заключается в том, что мне нужен способ получить доступ к объектам запроса и ответа, если я хочу перенаправить или изменить ответ.Методы контроллера не обязательно нуждаются в запросах и ответах, а требование, чтобы они выглядели, выглядит не элегантным.Я мог бы использовать весеннюю магию, чтобы получить объект запроса от Аспекта, но я не мог найти способ получить ответ.

В конце концов я нашел средний путь.Я использовал бин-фильтр, чтобы получить объекты запроса и ответа и сохранить их в ThreadLocal.Затем я создал аспект, имеющий ссылку на этот фильтр, чтобы он мог легко видеть объекты запроса и ответа.

Затем я обернул аспект вокруг методов, основанных на аннотации, поэтому я даже не сталнужно проверить, присутствовала ли аннотация с использованием кода.

Этот комбинированный подход, кажется, работает отлично!

Единственный недостаток - я не могу найти хороший способ написатьинтеграционный тест, который проверяет, что аспект вызывается при поступлении запроса на этот URL.Немного страшно, что удаление одной аннотации оставляет все мои тесты успешными, но пропускает неавторизованных пользователей.

Спасибо всем за отличные предложения!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...