Issue
I have a SpringMVC application with Thymeleaf for UI running on Tomcat 9.0.10 server.
Gradle config,
apply plugin: 'java'
apply plugin: 'war'
group = 'io.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compileOnly('org.projectlombok:lombok:1.18.0')
compileOnly('javax.servlet:javax.servlet-api:4.0.1')
compile('org.apache.logging.log4j:log4j-core:2.11.0')
compile('javax.mail:javax.mail-api:1.6.1')
compile('org.springframework:spring-webmvc:5.0.7.RELEASE')
compile('org.thymeleaf:thymeleaf:3.0.9.RELEASE')
compile('org.thymeleaf:thymeleaf-spring5:3.0.9.RELEASE')
compile('org.springframework.data:spring-data-jpa:2.0.8.RELEASE')
compile('org.hibernate:hibernate-core:5.3.2.Final')
compile('com.zaxxer:HikariCP:3.2.0')
testCompile('org.springframework:spring-test:5.0.7.RELEASE')
testCompile('org.junit.platform:junit-platform-launcher:1.2.0')
testCompile('org.junit.jupiter:junit-jupiter-engine:5.2.0')
testCompile('org.mockito:mockito-junit-jupiter:2.19.1')
}
configurations {
runtime.exclude group: 'org.slf4j'
compile.exclude group: 'org.slf4j'
}
My AppInitializer
class is
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootContextConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
And WebMvcConfig
class
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "io.example" })
@Log4j2
public class WebMvcConfig extends WebMvcConfigurationSupport implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/res/**").addResourceLocations("/resources/");
log.info("Does the registry have mapping {}", registry.hasMappingForPattern("/res/**"));
}
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("Messages");
return messageSource;
}
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(true);
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
}
And Controller
class is,
@Controller
@RequestMapping(path = "/ba")
@Log4j2
public class HelloController {
@RequestMapping(path = "/hello")
public String sayHello(Model model) {
log.info("HelloController.sayHello() called . . . ");
model.addAttribute("name", "John");
return "hello";
}
@RequestMapping(path = "/login")
public String loginLoad(Model model) {
log.info("HelloController.loginLoad() called . . . ");
return "login";
}
}
And when the page tries to access resources, it get following trace logs,
url -
logs,
21:17:29.704 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Bound request context to thread: org.apache.catalina.connector.RequestFacade@3618aa7d
21:17:29.704 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/res/css/bulma.min.css]
21:17:29.704 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@1c8d9b17] in DispatcherServlet with name 'dispatcher'
21:17:29.704 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /res/css/bulma.min.css
21:17:29.706 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Did not find handler method for [/res/css/bulma.min.css]
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@473d0890] in DispatcherServlet with name 'dispatcher'
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/res/css/bulma.min.css]
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@35f4e4ad] in DispatcherServlet with name 'dispatcher'
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@2c70dd07] in DispatcherServlet with name 'dispatcher'
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@ba3770a] in DispatcherServlet with name 'dispatcher'
21:17:29.706 [http-nio-8080-exec-3] WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/res/css/bulma.min.css] in DispatcherServlet with name 'dispatcher'
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.servlet.DispatcherServlet - Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@3618aa7d
21:17:29.706 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Publishing event in WebApplicationContext for namespace 'dispatcher-servlet': ServletRequestHandledEvent: url=[/res/css/bulma.min.css]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[dispatcher]; session=[null]; user=[null]; time=[2ms]; status=[OK]
21:17:29.706 [http-nio-8080-exec-3] TRACE org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Publishing event in Root WebApplicationContext: ServletRequestHandledEvent: url=[/res/css/bulma.min.css]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[dispatcher]; session=[null]; user=[null]; time=[2ms]; status=[OK]
And I don't see any trace logs for ResourceHander being initialized during startup. Any help appreciated.
Solution
Your WebMvcConfig
class should be like this as suggested by @M. Deinum
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "io.example" })
@Log4j2
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/res/**").addResourceLocations("/resources/");
log.info("Does the registry have mapping {}", registry.hasMappingForPattern("/res/**"));
}
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("Messages");
return messageSource;
}
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(true);
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
}
Answered By - MyTwoCents