好吧,我的问题可能听起来有点模糊,但无论如何它在这里。 我正在使用Spring MVC 3.1.M1,JSP 2.1构建一个Web应用程序(没有Tiles,我使用普通的JSP标记文件来构建我的布局)。
基本上,我的页面是使用一些常见部分的布局构建的 - 页眉,页脚,横幅,菜单等。这些部分大多是动态的,即包含当前用户的相关信息。
JSP没有“组件”概念,因此我无法在某个地方定义我的模板的一部分及其支持java代码,并将它们连接在一起。在我的@Controllers中,我必须完全填充我的模型,包括页眉,页脚,菜单和其他内容的数据。我真正想要做的是避免这种代码重复。具有一些通用模型填充方法的抽象BaseController类也看起来不太好。
JSP和Spring MVC是经常一起使用的,所以我希望在这个主题上存在一些最佳实践。 让我们讨论一下。
答案 0 :(得分:4)
springframework包含处理程序拦截器,作为处理程序映射机制的一部分
在拦截器中,您可以在执行实际处理程序之前使用postHandle
方法。
这样的拦截器必须实现org.springframework.web.servlet.HandlerInterceptor
或org.springframework.web.servlet.handler.HandlerInterceptorAdapter
以简化实现。
public class MyHandlerInterceptor extends HandlerInterceptorAdapter {
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
//populate header, menu, footer, ... model
}
}
以及处理程序映射的配置。
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean id="myInterceptor" class="...MyHandlerInterceptor"/>
</list>
</property>
答案 1 :(得分:4)
好的,所以我花了一些时间使用Spring MVC参考和示例应用程序,并找到了一些其他方法来完成我的任务。他们在这里:
1)第一种方式,糟糕且无法使用,这里就提一下。抽象BaseController的方法有populateHeaderData(模型模型),populateFooterData(模型模型)等。扩展BaseController的所有控制器类中的所有@RequestMapping方法都调用这些方法来填充特定于布局的模型数据。
优点:无
缺点:代码重复保持不变,只是减少了重复代码的数量
2) @ModelAttribute方法,即隐式模型丰富。看起来像
@Controller
@RequestMapping(value="/account")
public class AccountController {
@ModelAttribute("visitorName")
private String putVisitor() {
return visitorService.getVisitorName();
}
// handler methods
}
在JSP中,
<span id="username">Welcome, ${visitorName}!</span>
优点:无需明确调用模型丰富方法 - 它只是起作用
缺点:这是一个棘手的事情。 Spring MVC使用“推”模板模型而不是“拉”。在这种情况下,它意味着当调用此类中定义的任何@RequestMapping方法时,将调用此类的所有@ModelAttribute方法。如果模板确实需要visitorName并且模板实际存在于特定操作,则没有区别。这包括对表单提交的POST请求等。事实上,这强制我们改变控制器分离。例如,所有表单提交都应该在单独的控制器类中,并且处理程序方法应该按布局分组。我必须多考虑一下,也许它看起来并不那么糟糕。
更多缺点:假设我们的布局A和B具有相同的非静态页眉,B和C具有相同的非静态页脚(所有其他部分都不同)。我们无法为布局B实现基类,因为Java中没有多重继承。
向观众提问: Spring MVC引用声明“处理程序方法支持以下返回类型:ModelAndView对象,模型隐式地使用命令对象和@ModelAttribute注释引用数据访问器方法的结果......”。这些命令对象到底是什么?
3)我自己的拉式方法。我们可以以
的形式创建自定义上下文@Component("headerContext")
public class HeaderContext {
@Autowired
private VisitorService visitorService;
public String getVisitorName() {
return visitorService.getVisitorName();
}
// more getters here
}
然后,通过
将这些bean公开给JSP EL<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/"/>
<beans:property name="suffix" value=".jsp"/>
<beans:property name="exposedContextBeanNames" value="headerContext,footerContext"/>
</beans:bean>
在header.tag(重用标题的JSP标记文件)
中<span id="username">Welcome, ${headerContext.visitorName}!</span>
优点:“拉”策略(没有人要求 - 没有任何东西可以使用),很容易制作上下文@Scope(“请求”)并启用请求范围的缓存,多重继承没有问题。只需在一个地方编码,在一个地方配置,可以在任何JSP或标记文件中用作通常的表达式。
缺点:在一个框架内混合推送和拉动(必须多考虑一下),在上下文实现类中没有Spring MVC支持(我的意思是控制器处理程序方法中这些讨厌的预先填充的参数),只是春豆。
答案 2 :(得分:2)
最后,我决定坚持@ModelAttribute方法,尽管有其局限性。
/**
* Base class for all page controllers (i.e., not form submits)
* @author malexejev
* 23.03.2011
*/
public abstract class AbstractPageController {
@Autowired
private VisitorService visitorService;
@Autowired
private I18nSupport i18nSupport;
@Value("${xxx.env}")
private String environment;
/**
* Implicit model enrichment with reference data.
* No heavy operations allowed here, since it is executed before any handler method of
* all extending controllers
*/
@ModelAttribute("appContext")
public Map<String, Object> populateReferenceData(HttpServletRequest request) {
Map<String, Object> dataMap = new HashMap<String, Object>();
// FIXME some data is app-wide and constant, no need to re-create such map entries
// I should take care about it when more reference data is added
dataMap.put("visitorName", visitorService.getVisitorName());
dataMap.put("env", environment);
dataMap.put("availableLanguages", i18nSupport.getAvailableLanguages());
dataMap.put("currentPath", request.getPathInfo() != null ? request.getPathInfo() : request.getServletPath());
return Collections.unmodifiableMap(dataMap);
}
}
这样我就可以通过$ {appContext.visitorName}获取视图中的数据。它允许我透明地切换到Spring bean实现(参见上面的答案中的第3个,@ Component(“headerContext”)),以防将来出现@ModelAttributes问题。
感谢所有人的讨论。我在这里找不到任何“银弹”解决方案,因此我不会将任何答案标记为已接受,但会对这个问题的所有答案进行投票。
答案 3 :(得分:1)
你有几种选择,虽然它们也不完美..
这些只是进行讨论的一些想法
答案 4 :(得分:0)
handler interceptor适用于每个页面中使用的共享数据。
如果你想要细粒度的“组件”,你真的应该重新考虑使用apache磁贴。 从那里你可以为每个瓷砖使用一个“控制器”(ViewPreparer),如下所示:
http://richardbarabe.wordpress.com/2009/02/19/apache-tiles-2-viewpreparer-example/