Spring MVC控制器设计

Sco*_*ott 16 java 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
Run Code Online (Sandbox Code Playgroud)

有哪些策略用于创建最终方法,允许开发人员"忘记"这些流程?我考虑过制作一个抽象类,但由于每种方法的不同,我真的没有办法"标准化".

例如,我们有以下内容:

@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
}
Run Code Online (Sandbox Code Playgroud)

拥有最终方法需要将其分解为两个POJO(然后将调用描述性函数).我直接关注的是如何处理最终方法中的不同参数?我认为没有办法处理这种情况.

如果我们仍然可以使用受保护函数的"最终"方法,我们可以在需要时覆盖它.

Yon*_*oni 5

我和你有同样的问题.我还没有一个干净的解决方案,但我相信我取得了一些进展,所以我想我会分享到目前为止我找到的内容.

我探讨了three_cups_of_java建议的拦截器的使用,但我遇到了各种问题(如下所述).目前我正在尝试使用自定义AnnotationMethodHandlerAdapter,但我还没有完成这项工作.

拦截器

由于拦截器无法访问它们拦截的控制器对象(更正:它们可以访问它,但对执行流程的控制有限),控制器和拦截器必须通过会话中的对象进行通信.

这是我的意思的一个简化示例:

在我们的旧架构中,我们有自己的基本控制器,每个人都可以扩展.它本身扩展了MultiActionController,并添加了一些自定义行为 - 比如在您的示例中,调用处理程序方法之前更新发布请求之后的服务器端视图.这是有效的,因为所有控制器都提供模板方法的实现(例如getViewKeyInSession()).

因此,基本控制器中的自定义代码看起来大致如下:

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

现在,当我们将此代码移动到拦截器时,我们遇到了几个问题:

  1. 拦截器不能调用getViewKeyInSession(),强制我们对所有控制器使用相同的会话密钥(不好),或者遵守一些约定,即视图的会话密钥基于URL或请求的参数(到目前为止,这也不好.
  2. 单个控制器不能再覆盖其行为updateModelAfterPost.这通常不是必需的,但不幸的是,有些控制器是必要的.
  3. 如果控制器提供updateModelAfterPost的实现并想要向拦截器发出信号,表示它对拦截器的帮助不感兴趣,则需要在会话中放置一个标记对象以供拦截器查看,并且需要执行此操作它在上一次GET请求期间(也不好并且不灵活).

使用Custom AnnotationMethodHandlerAdapter

目前我正在寻找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;
}
Run Code Online (Sandbox Code Playgroud)

(当然,这只是一个玩具示例.在实际代码中,您需要更好的异常处理)

更新:

我用自定义尝试了上述策略,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>
Run Code Online (Sandbox Code Playgroud)
  • 正如评论中所提到的,我展开了<mvc:annotation-driven>的简写.我必须显式定义两个处理程序映射,并定义两个处理程序适配器.
  • 不幸的是,在我的遗留代码中,一些控制器是转换的,并由cglib代理.这AnnotationMethodHandlerAdapter不能很好地处理,因此我设置了元素的顺序,使得遗留处理程序映射和处理程序适配器首先起作用,而基于注释的处理程序映射和处理程序适配器起作用.
  • 我必须明确定义Spring SimpleControllerHandlerAdapter,但我还必须使用自己的类扩展它,因为它没有实现Ordered接口.
  • 我在定义验证器时遇到了问题,因为我没有jsr-303的jar.因此,我放弃了验证器和转换服务的声明.上面的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;
    }


}
Run Code Online (Sandbox Code Playgroud)