You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为所有控制器与页面仅绑定一次共享片段的categoryList数据?

全局绑定categoryList到所有请求的解决方案

嗨,针对你想一次性绑定categoryList,让所有控制器和页面都能自动访问到这个数据的需求,我给你推荐几种可行的方案,其中最简洁高效的是Spring官方推荐的全局增强方式:


方案一:使用@ControllerAdvice全局注入(首推)

这种方式不需要修改任何现有控制器,就能自动为所有请求注入categoryList:

  1. 创建全局增强类
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.List;

@ControllerAdvice
public class GlobalCategoryAdvice {

    // 注入你的分类服务类,用来获取数据
    private final PostCategoryService postCategoryService;

    // 构造注入(推荐用构造注入代替@Autowired)
    public GlobalCategoryAdvice(PostCategoryService postCategoryService) {
        this.postCategoryService = postCategoryService;
    }

    @ModelAttribute("categoryList")
    public List<PostCategory> loadCategoryList() {
        // 调用业务方法获取分类列表,和你之前在控制器里写的逻辑一样
        return postCategoryService.getPostCategoryList();
    }
}
  1. 为什么这能生效?

    • @ControllerAdvice标记的类会成为全局的控制器增强器,对所有@Controller和@RestController生效。
    • 带有@ModelAttribute("categoryList")的方法,会在每个控制器处理请求之前被自动调用,返回的列表会被自动添加到模型中,键名就是categoryList——和你之前手动添加的完全一致。
    • 现在你可以删掉所有控制器里的model.addObject("categoryList", postCategoryList);代码,你的nav.jsp依然能正常用${categoryList}渲染分类列表,完全不用改页面代码!
  2. 性能优化小技巧
    如果你的分类数据不是实时更新的,给这个方法加上Spring缓存注解,避免每次请求都查数据库:

import org.springframework.cache.annotation.Cacheable;

// ...其他代码不变

@Cacheable(value = "categoryCache", key = "#root.methodName")
@ModelAttribute("categoryList")
public List<PostCategory> loadCategoryList() {
    return postCategoryService.getPostCategoryList();
}

记得要在你的Spring配置类上加上@EnableCaching注解,开启缓存功能哦。


方案二:使用HandlerInterceptor拦截器注入

如果你需要更精细地控制哪些请求需要注入分类数据,可以用拦截器的方式:

  1. 编写拦截器类
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

public class CategoryInterceptor implements HandlerInterceptor {

    private final PostCategoryService postCategoryService;

    public CategoryInterceptor(PostCategoryService postCategoryService) {
        this.postCategoryService = postCategoryService;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 只给返回视图的请求添加数据(排除API接口等无视图的请求)
        if (modelAndView != null) {
            List<PostCategory> categoryList = postCategoryService.getPostCategoryList();
            modelAndView.addObject("categoryList", categoryList);
        }
    }
}
  1. 注册拦截器
    在Spring MVC的配置类里注册这个拦截器,让它生效:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final CategoryInterceptor categoryInterceptor;

    public WebConfig(CategoryInterceptor categoryInterceptor) {
        this.categoryInterceptor = categoryInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有请求,或者指定特定路径比如"/**"
        registry.addInterceptor(categoryInterceptor).addPathPatterns("/**");
        // 也可以排除不需要的路径,比如registry.addInterceptor(...).excludePathPatterns("/api/**");
    }
}

这种方式的好处是可以灵活控制拦截范围,但配置比第一种方案稍繁琐。


方案三:ServletContext存储静态分类(适合极少更新的数据)

如果你的分类数据几乎不会变化,比如网站的固定导航分类,可以把数据存在全局的ServletContext中,应用启动时只加载一次:

  1. 编写监听器类
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.util.List;

@WebListener
public class CategoryContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        // 获取Spring上下文,从而拿到服务类
        WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
        PostCategoryService postCategoryService = context.getBean(PostCategoryService.class);
        
        // 获取分类列表并存入ServletContext
        List<PostCategory> categoryList = postCategoryService.getPostCategoryList();
        event.getServletContext().setAttribute("categoryList", categoryList);
    }
}
  1. 页面访问
    你的nav.jsp依然可以用${categoryList}访问数据,因为ServletContext的属性会自动暴露给EL表达式。不过这种方式的缺点是:数据更新后需要重启应用,或者手动编写更新ServletContext属性的接口,所以只适合几乎不变化的静态数据。

总结一下,**方案一(@ControllerAdvice)**是最省心的选择,它完全符合Spring MVC的设计,代码简洁,还能轻松结合缓存优化性能,强烈推荐你用这个方案!

内容的提问来源于stack exchange,提问作者Majid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:53:34