Spring Конструкция контроллера MVC

Мы переносим приложение struts на Spring MVC и используем аннотацию @Controller для направления страниц на различные вызовы методов.

У меня возникли проблемы с определением хорошей стратегии повторного использования.

В основном мы делаем то же самое на многих наших страницах:

prepareView(..., ...); //Various params -- could likely be standardized

if (!allowedToView()) {
    mav.setViewName(injectedErrorPage);
}

performBusinessLogic(..., ...);  //Various params -- not seeing how to standardize

persistEntities();
finalizeView(..., ...);  // Various params -- could likely be standardized

Какие стратегии используются для создания окончательного метода, который позволит разработчикам "забыть" об этих процессах? Я думал о создании абстрактного класса, но действительно не так, как я вижу, чтобы "стандартизировать" это из-за различий в том, что каждый метод будет принимать.

Например, мы имеем следующее:

@RequestMapping("params="assign", method=RequestMethod.Post)
public ModelAndView assign(@SessionAttribute(value="sessionAttr") Pojo pojo,
                           @ModelAttribute("command") CommandPojo commandPojo,
                           BindingResult result) {
    //Follows pattern above
}

@RequestMapping()
public ModelAndView filterResults(@SessionAttribute(value="sessionAttr") Pojo pojo,
                                  @RequestAttribute("requestAttr") String requestAttr,
                                  @ModelAttribute("command") CommandPojo2 commandPojo2,
                                  BindingResult result) {

    //Follows pattern above
}

Для окончательного метода потребуется, чтобы это было разбито на два POJO (которые затем вызывали описательные функции). Мое непосредственное беспокойство заключается в том, как мы имеем дело с различными параметрами, входящими в этот окончательный метод? Я не вижу никакого способа справиться с этой ситуацией.

Было бы неплохо, если бы мы могли использовать этот "окончательный" метод с защищенными функциями, которые мы могли бы переопределить там, где это необходимо.

Ответы

Ответ 1

У меня та же проблема, что и вы. У меня пока нет чистого решения, но я считаю, что сделал некоторый прогресс, поэтому я решил поделиться с вами тем, что нашел до сих пор.

Я изучил использование перехватчиков, как это было предложено three_cups_of_java, но я столкнулся с различными проблемами (описанными ниже). В настоящее время я пытаюсь использовать пользовательский AnnotationMethodHandlerAdapter, но я еще не сделан с этими усилиями.

перехватчики

Так как перехватчики не имеют доступа к объекту контроллера, который они перехватывают ( коррекция:, у них есть к нему доступ, но с ограниченным контролем над потоком выполнения), контроллер и перехватчик должны общаться через объекты в сеансе.

Вот несколько упрощенный пример того, что я имею в виду:

В нашей старой архитектуре у нас есть собственный базовый контроллер, который все расширяют. Он сам расширяет MultiActionController и добавляет некоторое пользовательское поведение - например, в вашем примере, обновляя представление на стороне сервера после запроса на публикацию перед вызовом метода обработчика. Это работает, потому что все контроллеры обеспечивают реализацию метода шаблона (например, getViewKeyInSession()).

Таким образом, пользовательский код в базовом контроллере выглядит примерно так:

// inside handleRequestInternal method
if (request.getMethod().equals("POST") {
    updateViewAfterPost (session.get(getViewKeyInSession());
}
return super.handleRequestInternal();

Теперь, когда мы переместили этот код на перехватчик, мы сталкиваемся с несколькими проблемами:

  • Перехватчик не может вызывать getViewKeyInSession(), заставляя нас использовать один и тот же ключ сеанса для всех контроллеров (не очень хорошо) или придерживаться какого-либо соглашения о том, что ключ сеанса для представления основан на URL-адресе или параметре запрос (пока это тоже не хорошо).
  • Индивидуальные контроллеры больше не могут переопределять поведение updateModelAfterPost. Обычно это не нужно, но, к сожалению, это было необходимо для некоторых контроллеров.
  • Если контроллер предоставляет реализацию updateModelAfterPost и хочет сигнализировать перехватчику, что он не заинтересован в помощи перехватчика, это необходимо сделать, помещая объект-маркер в сеанс для просмотра перехватчика, и это необходимо сделать это во время предыдущего запроса GET (также не хорошо и не гибко).

Использование пользовательского параметра AnnotationMethodHandlerAdapter

В настоящее время я просматриваю указание DefaultAnnotationHandlerMapping непосредственно в моем xml (вместо mvc:annotation-driven), а затем поставляя ему пользовательский AnnotationMethodHandlerAdapter.

Как я сказал ранее, я не добился достаточного прогресса, чтобы представить полные результаты, однако направление, на которое я нацеливаюсь, таково:

Я думаю об AnnotationMethodHandlerAdapter как Spring -supplied MultiActionController, но для контроллеров pojo. Например, я уже знаю, как подключить к нему свой собственный метод resolver (см. Этот question) и другие Spring лакомства.

Этот адаптер имеет несколько методов, которые вы можете переопределить, например, invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler),
и возможно handle(HttpServletRequest request, HttpServletResponse response, Object handler)
также.

В вашем пользовательском коде вы можете проверить класс обработчика, а затем действовать соответствующим образом. Чтобы продолжить мой предыдущий пример, если класс обработчика имеет метод updateViewAfterPost или если он реализует определенный интерфейс, вы можете вызвать этот метод, а затем вызвать super, чтобы Spring продолжить регулярный вызов. Таким образом, код выглядит примерно так:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    // inspect handler object, look for marker interface, methods and/or annotations
    // perform pre-processing on the handler object
    // e.g. handler.updateViewAfterPost(request, response)
    ModelAndView mav = super.handle (request, response, handler);
    // post-processing on the handler object
    return mav;
}

(Разумеется, это просто пример игрушек. В реальном коде вам понадобится более эффективная обработка исключений)

UPDATE:

Я пробовал вышеупомянутую стратегию с помощью AnnotationMethodHandlerAdapter, и это действительно работает. Я использовал интерфейс маркера на моем контроллере pojo и ввел жизненный цикл только один новый метод с именем updateModelAfterPost, и он работает так, как ожидалось.

Есть несколько небольших оговорок, с которыми я столкнулся, главным образом потому, что я сочетал старые способы с новыми способами в одном и том же контексте mvc. Ниже вы можете увидеть изменения, которые я внес в контекст xml, а затем список проблем, которые, как мне кажется, стоит выделить.

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="order" value="2" />
 </bean>

<bean class="com.sample.MyAnnotationMethodHandlerAdapter">
    <property name="order" value="2" />
</bean>

<bean class="com.sample.MySimpleControllerHandlerAdapter" >
    <property name="order" value="1" />
</bean>

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="order" value="1" />
    <property name="mappings">
        <props>
            ...
        </props>
    </property>
</bean>
  • Как упоминалось в комментарии, я развернул < mvc: annotation-driven > стенография. Я должен был явно определить два отображения обработчика, а также определить два адаптера обработчика.
  • К сожалению, в моем устаревшем коде некоторые контроллеры являются трансациональными и проксируются cglib. AnnotationMethodHandlerAdapter не очень хорошо справляется с этим, поэтому я устанавливаю порядок элементов таким образом, чтобы сначала выполнялось сопоставление обработчика обработчика и адаптер обработчика, а привязка обработчика на основе аннотаций и адаптер обработчика выполнялись вторым.
  • Мне пришлось явно определить Spring SimpleControllerHandlerAdapter, но мне также пришлось расширять его с помощью моего собственного класса, потому что он не реализует интерфейс Ordered.
  • У меня возникла проблема с определением валидатора, потому что у меня не было jar для jsr-303. Поэтому я отказался от объявления валидаторов и службы преобразования. Вышеупомянутый фрагмент xml - это именно то, что я использую, это упрощенная версия, упрощенная для ответа.

и, наконец, вот код для соответствующих классов:

package com.sample;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;

public class MyAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof MyMarkerInterface) {
            MyMarkerInterface handler2 = (MyMarkerInterface) handler;
            handler2.updateModelAfterPost(request);
        }
        return super.invokeHandlerMethod(request, response, handler);
    }

}


package com.sample;

import org.springframework.core.Ordered;
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;

public class MySimpleControllerHandlerAdapter extends SimpleControllerHandlerAdapter implements Ordered {

    private int order = 0;

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }


}

Ответ 2

Не могли бы вы реализовать базовый класс, как вы предлагаете, и заставить шаблон дизайна Template Method, а также заимствовать у того, что сказал Никс в своем раннем комментарий к вашему вопросу об использовании коллекции параметров в этом базовом классе?

Помогает ли это?

Ответ 3

Если параметры меняются от функции к функции, я думаю, что предложение @Nix для сбора параметров является хорошим. В качестве альтернативы вы можете использовать var arg объектов. Но вам может потребоваться проверка, чтобы увидеть, присутствуют ли все параметры до того, как функция вызывается как проверка состояния Pre. Или, может быть, сочетание обоих подобных, вы бы знали, что некоторые параметры всегда необходимы, а другие - необязательные. Поэтому используйте varargs для опционального варианта для filterResults

public ModelAndView filterResults(@SessionAttribute(value="sessionAttr") Pojo pojo,
                                  @RequestAttribute("requestAttr") String requestAttr,
                                  @ModelAttribute("command") CommandPojo2 commandPojo2,
                                  Object...restOfParameters){}

Это может быть объединено с шаблоном шаблона, который ранее был рассмотрен.

Ответ 4

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

Все настроено через xml, что дает вам одну точку, где находится вся ваша логика и навигационная система (есть плагин eclipse для визуализации навигации, если вам не нравится xml). spring Webflow также отлично справляется с другими контроллерами mvc, если вам нужно обрабатывать некоторые запросы старомодным способом. И последнее, но не менее важное: spring webflow добавляет некоторые области применения для ваших переменных, которые очень удобны. Помимо запроса, сеанса и приложения, вы также получаете область потока и беседы, которая похожа на область сеанса, но только для текущего окна приложения. Это означает, что у вас может быть несколько окон/вкладок в вашем браузере без какого-либо вмешательства друг в друга.

Но вы должны проверить это самостоятельно, есть небольшое справочное руководство, доступное на веб-сайте spring, а также несколько демонстраций в их репозитории svn. Кроме того, книга "spring в действии" затрагивает тему веб-потока. Надеюсь, это полезно.

http://www.springsource.org/webflow