Background
Using tiles in a Spring 3.x application. The application uses a standard.jsp file that acts as the web site template, which defines typical elements: doctype, navigation, header, body, etc.
The standard.jsp file should dynamically include page-specific customizations. For example, if the URL was "http://localhost/account", then the <head>
element should contain:
<link rel="stylesheet" href="styles/account.css" />
Each tiles definition can be written as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions ...>
<tiles-definitions>
<definition ...>
<put-attribute name="viewName" value="account" />
</definition>
</tiles-definitions>
Then, in the standard.jsp file:
<link rel="stylesheet" href="<tiles:insertAttribute name="viewName" />.css"/>
Problem
This produces the desired result, but the tiles attributes are hard-coded, effectively duplicated for every tiles.xml file.
Question
How would you avoid duplicating the put-attribute
definition everywhere, yet still import specific CSS files that correspond to the view being rendered?
Idea
Using an Interceptor only works for the /
URI, and nothing else. For example:
@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("intro");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
registry.addInterceptor(new BaseInterceptor()).addPathPatterns("/**");
}
}
Configuring additional view controllers won't fire the interceptor code:
registry.addViewController("/account").setViewName("account");
If that worked, then we could iterate over all the JSP files by scanning the classpath (see here and here). The tiles code is probably interfering with the view controller registry for the JSPs:
@Bean
public TilesViewResolver tilesViewResolver() {
TilesViewResolver viewResolver = new TilesViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
@Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();
tilesConfigurer.setDefinitions("/WEB-INF/**/tiles.xml");
return tilesConfigurer;
}
The interceptor is only triggered for /
and can be used to inject the view name:
public class BaseInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LoggerFactory.getLogger(BaseInterceptor.class);
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("BASE INTERCEPTOR POST HANDLE");
if (modelAndView != null) {
modelAndView.addObject("stylesheet", modelAndView.getViewName() + ".css");
}
super.postHandle(request, response, handler, modelAndView);
}
}
But because of tiles, the interceptor is never triggered for anything other than /
.