【问题标题】:Add attributes to the model of all controllers in Spring 3在 Spring 3 中为所有控制器的模型添加属性
【发布时间】:2011-11-13 17:13:26
【问题描述】:

我的 Spring 3 应用程序中的每个视图都有一组可以依赖的属性。所以每个控制器的第一行是这样的:

ControllerHelper.addDefaultModel(model, personManager, request);

我会在里面添加

  • 如果有人登录,则从数据库中检索用户对象和全名
  • 通常设置一次的变量集(例如imagesHost
  • 访问者可以切换到的语言集
  • 当前语言
  • 一些统计数据(例如我们系统中的总人数)

这一切都允许每个视图显示登录用户的姓名、轻松引用图像位置、语言列表和网站的一些整体统计信息。

所以问题是,控制器模型对象是存储所有数据的最佳位置,还是有更方便的位置让视图访问这些信息同样容易?

其次,我真的很希望不必将上面的ControllerHelper 行作为每个控制器的第一行。实际上并不总是第一行,有时我首先检查是否需要在该控制器中重定向,因为我不想无缘无故地浪费资源填充模型。也许过滤器或注释或一些 Spring 回调机制可以确保在控制器完成之后调用 ControllerHelper 代码,但在视图渲染之前 之前,如果返回了重定向?

【问题讨论】:

  • 你不能把这些东西放在会话中吗?
  • 所以用户对象和全名也许我可以放入会话中,其余的东西放入会话中没有任何意义。我将会话保存在数据库中以便于集群,因此我尽量将其保持在最低限度。如果我将用户对象和全名存储在会话中,这意味着我将为每个请求进行数据库调用。无论如何,我正在通过在每次请求时从数据库中检索这些对象来执行此操作,但至少检索对象可以更轻松地进行休眠缓存。
  • 我有同样的问题要解决。 @在。你是怎么解决的?

标签: java spring spring-mvc spring-3


【解决方案1】:

你可以写一个org.springframework.web.servlet.HandlerInterceptor。 (或其便利子类HandlerInterceptorAdapter

@见:Spring Reference chapter: 15.4.1 Intercepting requests - the HandlerInterceptor interface

有方法:

void postHandle(HttpServletRequest request,
                HttpServletResponse response,
                Object handler,
                ModelAndView modelAndView) throws Exception;

这个方法在控制器完成之后和视图渲染之前被调用。所以你可以使用它,给ModelMap添加一些属性

一个例子:

/**
 * Add the current version under name {@link #VERSION_MODEL_ATTRIBUTE_NAME}
 * to each model. 
 * @author Ralph
 */
public class VersionAddingHandlerInterceptor extends HandlerInterceptorAdapter {

    /**
     * The name under which the version is added to the model map.
     */
    public static final String VERSION_MODEL_ATTRIBUTE_NAME =
                "VersionAddingHandlerInterceptor_version";

    /**        
     *  it is my personal implmentation 
     *  I wanted to demonstrate something usefull
     */
    private VersionService versionService;

    public VersionAddingHandlerInterceptor(final VersionService versionService) {
        this.versionService = versionService;
    }

    @Override
    public void postHandle(final HttpServletRequest request,
            final HttpServletResponse response, final Object handler,
            final ModelAndView modelAndView) throws Exception {

        if (modelAndView != null) {
            modelAndView.getModelMap().
                  addAttribute(VERSION_MODEL_ATTRIBUTE_NAME,
                               versionService.getVersion());
        }
    }
}

webmvc-config.xml

<mvc:interceptors>
    <bean class="demo.VersionAddingHandlerInterceptor" autowire="constructor" />
</mvc:interceptors>

【讨论】:

【解决方案2】:

您也可以在方法上使用@ModelAttribute,例如

@ModelAttribute("version")
public String getVersion() {
   return versionService.getVersion();
}

这将为控制器中的所有请求映射添加它。如果你把它放在一个超类中,那么它可以被所有扩展它的控制器使用。

【讨论】:

  • 您能否提供将其放入超类以及子类如何使用它的示例?
  • @trusktr 我创建了一个RootController 类并创建了子类protected 所需的字段......这种方法很容易工作,我能够将每个控制器所需的功能整合到这个RootController 这样我就不必在子类中重复相同的代码(“DRY”)
【解决方案3】:

您可以使用带有 @ControllerAdvice 注释的控制器类

“@ControllerAdvice 是在 3.2 中引入的,用于在所有或部分控制器之间共享的 @ExceptionHandler、@ModelAttribute 和 @InitBinder 方法。”

有关它的一些信息,请查看 SpringOne2GX 2014 期间录制的这部分视频 http://www.youtube.com/watch?v=yxKJsgNYDQI&t=6m33s

【讨论】:

  • @ControllerAdvice 是一个不错的选择
  • 这应该是最好的答案,如果您需要多个控制器的相同模型属性而不是控制器建议注释是您所需要的。 spring docs
  • 很好的答案!只是一个小说明:使用@ControllerAdvice 注解的类可以被任何类使用,不需要是控制器类。我在服务类上使用它。
【解决方案4】:

like @blank 为我回答这项工作:

@ControllerAdvice(annotations = RestController.class)
public class AnnotationAdvice {

    @Autowired
    UserServiceImpl userService;

    @ModelAttribute("currentUser")
    public User getCurrentUser() {
       UserDetails userDetails = (UserDetails) 
       SecurityContextHolder.getContext().getAuthentication().getPrincipal();
       return userService.findUserByEmail(userDetails.getUsername());
}
}

【讨论】:

    【解决方案5】:

    在使用 @ModelAttribute 或 HandlerInterceptor 方法时,重定向会出现一个问题。 当处理程序返回重定向视图时,以这种方式添加的模型属性将作为查询参数附加。

    另一种处理这种情况的方法是创建会话范围的 bean,它可以在基本应用程序控制器中自动装配,或者在需要访问的每个控制器中显式装配。

    有关可用范围和用法的详细信息,请参阅here

    【讨论】:

    • 避免该问题的另一种方法是不直接将字符串值作为属性返回,而是将它们放在 HashMap 或 List 中,然后将该映射或列表作为属性返回:@ModelAttribute(name="mymap") public Map&lt;String, String&gt; getSettings(),然后在您的视图通过为地图名称添加前缀(“mymap.key”)来访问这些值。
    【解决方案6】:

    如果你需要添加一些全局变量,每个视图都可以解析这些变量, 为什么不定义成属性或映射? 然后使用spring DI,参考视图解析器bean。 它非常有用,例如静态可验证,例如resUrl

    <property name="viewResolvers">
            <list>
                <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="viewClass"
                        value="org.springframework.web.servlet.view.JstlView" />
                    <property name="attributes" ref="env" />
                    <property name="exposeContextBeansAsAttributes" value="false" />
                    <property name="prefix" value="${webmvc.view.prefix}" />
                    <property name="suffix" value="${webmvc.view.suffix}" />
                </bean>
            </list>
        </property>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-04
      • 2016-02-15
      • 1970-01-01
      • 2016-12-13
      • 1970-01-01
      • 2018-10-11
      • 1970-01-01
      相关资源
      最近更新 更多