1

I'm worked with Spring in the past on a big project, but I've never started a Spring MVC web app from scratch.

Well that's what I'm currently doing now for practice as I have a project coming up that will require it.

I was successfully able to make a simple Spring MVC Web App that used .JSP pages (using annotations, no XML). I wanted to use Thymeleaf though and started my conversion process to that.

Well now I'm getting a 404 Error and my HomeController class isn't even being hit it seems like.

I get no errors in the Console output.

I've Google search, read through tutorials, and code samples. Second pair of eyes would be nice. Thanks! :)

Note: going from .JSP to Thymeleaf the only changes made was the addition of the ThymeleafConfig class. I don't see how it went from working to not working.

Here's my code:

WebInit.java

public class WebInit implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { // Creates the root application context AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(ServletConfig.class); appContext.setDisplayName("REPLACE ME"); appContext.setConfigLocation("com.demo.config"); // Creates the Spring Container shared by all Servlets and Filters servletContext.addListener(new ContextLoaderListener(appContext)); // Further configures the servlet context ServletRegistration.Dynamic dispatcher = servletContext.addServlet( "dispatcher", new DispatcherServlet(appContext)); dispatcher.setLoadOnStartup(1); dispatcher.setAsyncSupported(true); dispatcher.addMapping("/"); } } 

ServetConfig.java

@Configuration @Import(WebConfig.class) @ImportResource({/*"classpath:META-INF/spring/persistence-context.xml"*/}) public class ServletConfig { } 

WebConfig.java

@Configuration @ComponentScan("com.illinois.dnr") @EnableAspectJAutoProxy @EnableWebMvc @Import({ThymeleafConfig.class}) public class WebConfig extends WebMvcConfigurerAdapter { // Maps resources path to webapp/resources @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations( "/resources/"); } // Provides internationalization of messages @Bean public ResourceBundleMessageSource messageSource() { ResourceBundleMessageSource source = new ResourceBundleMessageSource(); source.setBasename("messages"); return source; } } 

Thymeleaf.java

@Configuration public class ThymeleafConfig { @Bean public ServletContextTemplateResolver templateResolver() { ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".html"); resolver.setTemplateMode("HTML5"); resolver.setOrder(1); return resolver; } @Bean public SpringTemplateEngine templateEngine() { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.setTemplateResolver(templateResolver()); return engine; } @Bean public ThymeleafViewResolver thymeleafViewResolver() { ThymeleafViewResolver resolver = new ThymeleafViewResolver(); resolver.setTemplateEngine(templateEngine()); return resolver; } } 

HomeController.java

@Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/dnr", method = RequestMethod.GET) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate); return "home"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String loginPage(Locale locale, Model model) { logger.info("Login"); return "login"; } @RequestMapping(value = "/home", method = RequestMethod.POST) public String login(@Validated User user, Model model) { model.addAttribute("userName", user.getUserName()); logger.info("User"); return "user"; } @RequestMapping(value = { "/", "/welcome**" }, method = RequestMethod.GET) public ModelAndView welcomePage() { ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security Hello World"); model.addObject("message", "This is welcome page!"); model.setViewName("hello"); logger.info("Hello"); return model; } @RequestMapping(value = "/admin**", method = RequestMethod.GET) public ModelAndView adminPage() { ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security Hello World"); model.addObject("message", "This is protected page - Admin Page!"); model.setViewName("admin"); logger.info("admin"); return model; } @RequestMapping(value = "/dba**", method = RequestMethod.GET) public ModelAndView dbaPage() { ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security Hello World"); model.addObject("message", "This is protected page - Database Page!"); model.setViewName("admin"); logger.info("dba"); return model; } } 
4
  • Using STS and tomcat7 Commented May 9, 2014 at 20:06
  • Is there a stack trace to speak of? Also, can you put System.out.println()'s in your ThymeleafConfig` methods to make sure they are running. Are all your Thymeleaf templates in WEB-INF/views? Commented May 9, 2014 at 21:04
  • Remember that you should go to localhost:8080/appName/dnr and page is WEB-INF/views/dnr.html Commented May 11, 2014 at 21:10
  • CodeChimp: Thanks for the idea of running System.out's in my methods! Should have thought of that myself. Commented May 12, 2014 at 17:19

2 Answers 2

1

I'm not sure if it helps but you can simplified WebInit by extendsAbstractAnnotationConfigDispatcherServletInitializer instead of implements WebApplicationInitializer. Otherwise Thymeleaf configuration seems ok to me (suppose it's called). I'm giving here working skeleton of config so you can experiment with it.

WebApplicationInitializer

public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } 

WebConfig

@Configuration @EnableWebMvc @ComponentScan("com.kreuzman") public class WebConfig { @Bean public ITemplateResolver templateResolver() { TemplateResolver resolver = new ServletContextTemplateResolver(); resolver.setPrefix("/templates/"); resolver.setSuffix(".html"); resolver.setTemplateMode("HTML5"); resolver.setCacheTTLMs(0l); return resolver; } @Bean public SpringTemplateEngine templateEngine() { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.setTemplateResolver(templateResolver()); return engine; } @Bean public ViewResolver thymeleafViewResolver() { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine()); viewResolver.setCharacterEncoding("UTF-8"); return viewResolver; } } 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! Hopefully this helps other people that want a purely Annotation based Spring Web App. I've found several tutorials and help guides, but they all have their own "style" on how they set things up. I finally found a working example of Spring MVC-Security-WebFlow, Hibernate, and Thymeleaf project. There config files are setup similar to your skeleton. Definitely doing this route. Much cleaner. Thanks
1

What page are you trying to hit? Does that html page exist in /WEB-INF/views/?

Is it a JSP page? you need to exclude all pages that are not going to be resolved by ThymeleafViewResolver. I also noticed you didn't set the ThymeleafViewResolver to first in the order.

Like so

@Bean public ThymeleafViewResolver thymeleafViewResolver() { String[] excludedViews = new String[]{ "login", "logout"}; ThymeleafViewResolver resolver = new ThymeleafViewResolver(); resolver.setTemplateEngine(templateEngine()); /* * This is how we get around Thymeleaf view resolvers throwing an error instead of returning * of null and allowing the next view resolver in the {@see * DispatcherServlet#resolveViewName(String, Map<String, Object>, Locale, * HttpServletRequest)} to resolve the view. */ resolver.setExcludedViewNames(excludedViews); resolver.setOrder(1); return resolver; } 

2 Comments

I tried setting the setOrder(1) and it didn't work. I ended up deleting the project and starting from scratch again. The only difference this time was I used this dependency : javax 6.0
You did help me exclude out .JSP pages if i wanted to support both .JSP and .html pages. So thank you for that

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.