来自多个来源的 Spring MVC 复杂模型群体
好吧,我的问题可能听起来有点模糊,但无论如何,它就是这样。 我正在使用 Spring MVC 3.1.M1、JSP 2.1 构建一个 Web 应用程序(没有 Tiles,我使用纯 JSP 标记文件来组成我的布局)。
基本上,我的页面是使用一些常见部分的布局构建的 - 页眉、页脚、横幅、菜单等。这些部分大多数是动态的,即包含当前用户的相关信息。
JSP 没有“组件”概念,因此我无法在某个地方定义部分模板及其支持的 java 代码,并将其耦合在一起。在我的 @Controllers 中,我必须完全填充我的模型,包括页眉、页脚、菜单和其他内容的数据。我真正想做的是避免这种代码重复。具有一些通用模型填充方法的抽象 BaseController 类看起来也不太好。
JSP 和 Spring MVC 经常一起使用,因此我希望在这个主题上存在一些最佳实践。 我们来讨论一下这个。
Well, my question may sound a little bit fuzzy, but here it is, anyways.
I'm building a web application using Spring MVC 3.1.M1, JSP 2.1 (without Tiles, I use plain JSP tag files to compose my layouts).
Basically, my pages are built using layouts of some common parts - header, footer, banner, menu etc. Most of these parts are dynamic, i.e. contain current user's related information.
JSP does not have a "component" notion, so I cannot define part of my template and its backing java code in some one place, coupled together. In my @Controllers, I have to fully populate my model, including data for header, footer, menu and other stuff. What I really want to do is to avoid this code duplication. Abstract BaseController class with some generic model population methods does not look good too.
JSP and Spring MVC are a very often used together, so I expect some best-practices to exist on this subject.
Lets discuss this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
好的,我花了一些时间研究 Spring MVC 参考和示例应用程序,并找到了一些其他方法来完成我的任务。它们是:
1) 第一种方式,糟糕且无法使用,仅在此提及。抽象 BaseController,具有 populateHeaderData(Model model)、populateFooterData(Model model) 等方法。扩展 BaseController 的所有控制器类中的所有 @RequestMapping 方法都会调用这些方法来填充特定于布局的模型数据。
优点: 无
缺点: 代码重复保持不变,只是重复代码量减少了
2) @ModelAttribute 方法,即隐式模型丰富。看起来像
在 JSP 中,
优点:不需要显式调用模型丰富方法 - 它只是工作
缺点:这是一件棘手的事情。 Spring MVC 使用“推”模板模型而不是“拉”。在这种情况下,这意味着当调用此类中定义的任何 @RequestMapping 方法时,将调用此类的所有 @ModelAttribute 方法。如果模板确实需要访问者名称以及模板是否确实存在用于特定操作,则没有区别。这包括表单提交的 POST 请求等。事实上,这迫使我们改变控制器分离。例如,所有表单提交都应该位于单独的控制器类中,并且处理程序方法应该以某种方式按布局分组。我得再想一想,也许事情并没有乍看起来那么糟糕。
更多缺点:假设我们的布局 A 和 B 具有相同的非静态页眉,B 和 C 具有相同的非静态页脚(所有其他部分都不同)。我们无法实现布局 B 的基类,因为 Java 中没有多重继承。
向观众提问:
Spring MVC 参考指出“处理程序方法支持以下返回类型:ModelAndView 对象,其中模型隐式地通过命令对象和 @ModelAttribute 注释的参考数据访问器方法的结果进行丰富......”。这些命令对象到底是什么?
3) 我自己的类似拉动的方法。我们可以以 Then 的形式创建自定义上下文
将这些 bean 公开给 JSP EL
,通过And in header.tag(用于重用标头的 JSP 标记文件)
。 优点: “拉”策略(没有人问 - 没有什么execeded),轻松创建上下文@Scope(“request”)并启用请求范围的缓存,多重继承没有问题。只需在一处编码、在一处配置,并且可以作为常用表达式在任何 JSP 或标记文件中使用。
缺点:在一个框架内混合推和拉(必须更多地考虑它),上下文实现类中没有 Spring MVC 支持(我的意思是控制器处理程序方法中这些令人讨厌的预填充参数),只有 spring bean 。
Ok, so I spent some time with Spring MVC reference and sample applications, and found some additional ways to accomplish my mission. Here they are:
1) Way number one, bad and unusable, just to mention here. Abstract BaseController with methods like populateHeaderData(Model model), populateFooterData(Model model) and so on. All @RequestMapping methods in all controller classes that extend BaseController call these methods to populate layout-specific model data.
Pros: none
Cons: code duplication remains the same, just the amount of duplicated code is reduced
2) @ModelAttribute methods, i.e. implicit model enrichment. Looks like
And in JSP,
Pros: no need to call model enrichment methods explicitly - it just works
Cons: its a tricky thing here. Spring MVC utilizes "push" templating model instead of "pull". What it means in this case is that when any of @RequestMapping methods, defined in this class, is called, all @ModelAttribute methods of this class are invoked. There is no difference if template really needs visitorName and if the template actually exist for specific action. This includes POST requests for form submits, etc. In fact, this forces us to change controllers separation. For example, all form submits should be in separate controller classes, and handler methods should be somehow grouped by layouts. I have to think more about it, maybe its not that bad as it looks at first glance.
More cons: suppose we have layouts A and B with the same non-static header, and B and C with the same non-static footer (all other parts are different). We cannot implement base class for layout B, since there is no multiple inheritance in Java.
Question to the audience:
Spring MVC reference states "The following return types are supported for handler methods: A ModelAndView object, with the model implicitly enriched with command objects and the results of @ModelAttribute annotated reference data accessor methods... ". What the hell these command objects are?
3) My own pull-like method. We can create custom contexts in a form of
Then, expose such beans to JSP EL via
And in header.tag (JSP tag file for reused header)
Pros: "pull" strategy (nobody asks - nothing is exeduted), easy to make contexts @Scope("request") and enable request-wide caching, no problems with multiple inheritance. Just coded in one place, configured in one place and may be used in any JSP or tag file as a usual expression.
Cons: mix of push and pull within one framework (have to think more about it), no Spring MVC support in context implementation classes (I mean these nasty prepopulated arguments in controller handler methods), just spring beans.
springframework 包含处理程序拦截器作为处理程序映射机制的一部分。
在拦截器中,您可以在执行实际处理程序之前使用
postHandle
方法。这样的拦截器必须实现 org.springframework.web.servlet.HandlerInterceptor 或 org.springframework.web.servlet.handler.HandlerInterceptorAdapter 以简化实现。
以及处理程序映射的配置。
The springframework contains handler interceptors as part of the handler mapping mechanism.
Within the interceptor you can use the
postHandle
method before the actual handler is executed.Such a interceptor must implement the
org.springframework.web.servlet.HandlerInterceptor
or theorg.springframework.web.servlet.handler.HandlerInterceptorAdapter
for simplified implementation.and the configuration for the handler mapping.
最后,我决定坚持使用 @ModelAttribute 方法,尽管它有其局限性。
这样我就可以通过 ${appContext.visitorName} 获取视图中的数据。它允许我透明地切换到 Spring bean 实现(请参阅上面我的答案中的第 3 个 @Component("headerContext") ),以防将来出现 @ModelAttributes 的任何问题。
谢谢大家讨论。我在这里没有看到任何“银弹”解决方案,因此我不会将任何答案标记为已接受,但会投票赞成该问题的所有答案。
Finally, I decided to stick with @ModelAttribute approach, despite its limitations.
This way i can get data in views via ${appContext.visitorName}. It allows me to switch transparently to Spring bean implementation (see No 3 in my answer above, @Component("headerContext") ) in case of any future problems with @ModelAttributes.
Thanks all to discussion. I dont see any "silver bullet" solution found here, so I will not mark any answer as accepted, but will vote up all answers to this question.
好吧,你有几个选择,尽管它们也不完美..
这些只是一些让讨论继续下去的想法
well you have several options, though they are not perfect either..
these are just some ideas to get some discussion going
处理程序拦截器适用于每个页面中使用的共享数据。
如果你想要细粒度的“组件”,你真的应该重新考虑使用 apachetiles。
从那里您可以使用“控制器”(ViewPreparer )对于此处指出的每个图块:
http:// richardbarabe.wordpress.com/2009/02/19/apache-tiles-2-viewpreparer-example/
handler interceptor is fine for shared data which is used in every page.
if you want fine grained "components" you really should reconsider using apache tiles.
from there you can use a "controller" (ViewPreparer) for each tile as pointed here:
http://richardbarabe.wordpress.com/2009/02/19/apache-tiles-2-viewpreparer-example/