我和你有同样的问题。我还没有一个干净的解决方案,但我相信我已经取得了一些进展,所以我想与你分享我到目前为止的发现。
我按照three_cups_of_java 的建议探索了拦截器的使用,但遇到了各种问题(如下所述)。目前我正在尝试使用自定义 AnnotationMethodHandlerAdapter,但我还没有完成这项工作。
拦截器
由于拦截器无法访问它们拦截的控制器对象(更正:它们确实可以访问它,但对执行流程的控制有限),控制器和拦截器必须通过会话中的对象进行通信。
这是我的意思的一个稍微简化的例子:
在我们的旧架构中,我们有自己的基础控制器,每个人都可以扩展。它本身扩展了 MultiActionController,并添加了一些自定义行为 - 例如在您的示例中,在发布请求 before 调用处理程序方法之后更新服务器端视图。这是因为所有控制器都提供了模板方法的实现(例如getViewKeyInSession())。
因此,基本控制器中的自定义代码大致如下所示:
// inside handleRequestInternal method
if (request.getMethod().equals("POST") {
updateViewAfterPost (session.get(getViewKeyInSession());
}
return super.handleRequestInternal();
现在,当我们将此代码移至拦截器时,我们遇到了几个问题:
- 拦截器无法调用 getViewKeyInSession(),迫使我们对所有控制器使用相同的会话密钥(不好),或者遵守一些约定,即视图的会话密钥基于 url 或参数请求(到目前为止这也不好)。
- 单个控制器不能再覆盖
updateModelAfterPost 的行为。这通常不是必需的,但不幸的是,对于某些控制器来说这是必需的。
- 如果控制器提供了 updateModelAfterPost 的实现,并希望向拦截器发出信号,它对拦截器的帮助不感兴趣,则需要在会话中放置一个标记对象以供拦截器查看,然后需要在之前的 GET 请求期间执行(也不好且不灵活)。
使用自定义 AnnotationMethodHandlerAdapter
目前我正在考虑直接在我的 xml 中指定 DefaultAnnotationHandlerMapping(而不是 mvc:annotation-driven),然后为其提供自定义 AnnotationMethodHandlerAdapter。
正如我之前所说,我没有取得足够的进展来呈现完整的结果,但是我的目标是这样的:
我认为 AnnotationMethodHandlerAdapter 是 Spring 提供的 MultiActionController,但用于 pojo 控制器。例如,我已经知道如何插入我自己的方法解析器(参见 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;
}
(当然,这只是一个玩具示例。在实际代码中,您需要更好的异常处理)
更新:
我用自定义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 的
SimpleControllerHandlerAdapter,但我还必须用我自己的类扩展它,因为它没有实现 Ordered 接口。
- 我在定义验证器时遇到了问题,因为我没有 jsr-303 的 jar。因此我放弃了验证器和转换服务的声明。上面的xml sn-p正是我用的,不是为了回答而简化的精简版。
最后,这里是相关类的代码:
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;
}
}