如何为所有控制器与页面仅绑定一次共享片段的categoryList数据?
全局绑定categoryList到所有请求的解决方案
嗨,针对你想一次性绑定categoryList,让所有控制器和页面都能自动访问到这个数据的需求,我给你推荐几种可行的方案,其中最简洁高效的是Spring官方推荐的全局增强方式:
方案一:使用@ControllerAdvice全局注入(首推)
这种方式不需要修改任何现有控制器,就能自动为所有请求注入categoryList:
- 创建全局增强类
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(); } }
为什么这能生效?
@ControllerAdvice标记的类会成为全局的控制器增强器,对所有@Controller和@RestController生效。- 带有
@ModelAttribute("categoryList")的方法,会在每个控制器处理请求之前被自动调用,返回的列表会被自动添加到模型中,键名就是categoryList——和你之前手动添加的完全一致。 - 现在你可以删掉所有控制器里的
model.addObject("categoryList", postCategoryList);代码,你的nav.jsp依然能正常用${categoryList}渲染分类列表,完全不用改页面代码!
性能优化小技巧
如果你的分类数据不是实时更新的,给这个方法加上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拦截器注入
如果你需要更精细地控制哪些请求需要注入分类数据,可以用拦截器的方式:
- 编写拦截器类
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); } } }
- 注册拦截器
在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中,应用启动时只加载一次:
- 编写监听器类
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); } }
- 页面访问
你的nav.jsp依然可以用${categoryList}访问数据,因为ServletContext的属性会自动暴露给EL表达式。不过这种方式的缺点是:数据更新后需要重启应用,或者手动编写更新ServletContext属性的接口,所以只适合几乎不变化的静态数据。
总结一下,**方案一(@ControllerAdvice)**是最省心的选择,它完全符合Spring MVC的设计,代码简洁,还能轻松结合缓存优化性能,强烈推荐你用这个方案!
内容的提问来源于stack exchange,提问作者Majid
相关产品推荐
相关产品推荐

