Spring MVC Контроллер Дизайн - PullRequest
16 голосов
/ 10 марта 2011

Мы переносим приложение 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 (которые затем вызвали бы описательные функции).Моя непосредственная проблема заключается в том, как мы имеем дело с различными параметрами, входящими в этот последний метод?Я не вижу способа справиться с этой ситуацией.

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

Ответы [ 4 ]

5 голосов
/ 23 марта 2011

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

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

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

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

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

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

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

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

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

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

Использование пользовательского аннотацииMethodHandlerAdapter

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

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

Я думаю о AnnotationMethodHandlerAdapter как о поставляемом Spring MultiActionController, но для контроллеров pojo. Например, я уже знаю, как подключить к нему собственный распознаватель методов (см. Этот вопрос ) и другие полезности 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>
  • Как уже упоминалось в комментарии, я развернулстенография.Мне пришлось явно определить два сопоставления обработчиков, а также определить два адаптера обработчиков.
  • К сожалению, в моем унаследованном коде некоторые контроллеры являются транснациональными и передаются через cglib.AnnotationMethodHandlerAdapter плохо справляется с этим, поэтому я установил порядок элементов таким образом, чтобы устаревшие адаптеры отображения обработчиков и обработчиков действовали первыми, а адаптер сопоставления обработчиков на основе аннотаций - вторыми.
  • Я должен был явно определить Spring 10 *, но мне также пришлось расширить его своим собственным классом, потому что он не реализует интерфейс Ordered.
  • У меня была проблемаопределение валидатора, потому что у меня не было банки для 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;
    }


}
0 голосов
/ 25 марта 2011

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

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

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

http://www.springsource.org/webflow

0 голосов
/ 14 марта 2011

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

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

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

0 голосов
/ 11 марта 2011

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

Это помогает?

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