Spring 3+ supports using java-based configuration in replacement of the tradictional web.xml. You can replace web.xml with any class which implments interface org.springframework.web.WebApplicationInitializer ( which only has one onStartup() method).
Usually all you need to provide to a web.xml for a spring mvc application are:
- Spring Config for root context
- Spring config for servlet context (for spring mvc, it's config for org.springframework.web.servlet.DispatcherServlet)
- Where to map spring's DispatcherServlet (ususally "/")
- Filters, if any
To facilitate usage of this WebApplicationInitializer interface, Spring also provide serveral Abstract classes, which has methods directly ask for informations list above. The most used is class org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
In this article, there are 2 demos, first uses WebApplicationInitializer, second uses AbstractAnnotationConfigDispatcherServletInitializer. Spring 4.2 is used in the demos.
This article only show basics when Spring security is not used. For more practical usage with Spring security, please check another article on "Java-based Spring mvc configuration - with Spring security".
0. Java config classes for root and servlet context
Both demos use same Java config classes to config root and servlet context.
The java config for root context is named RootConfig.java.
package com.shengwang.demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import ch.qos.logback.classic.helpers.MDCInsertingServletFilter; @Configuration @EnableWebMvc @ComponentScan({"com.shengwang.demo.service","com.shengwang.demo.model"}) public class RootConfig { // logback's filter for easy logging info. e.g client's IP @Bean public MDCInsertingServletFilter mdcInsertingServletFilter() { return new MDCInsertingServletFilter(); } }
The root context here has service and model beans registerd, but in practical it's really not that strict. Also a filter bean proviced by logback is configured in the root context. This "mdcInsertingServletFilter" bean is actually a filter and will be used later.
The java config for dispatcher servlet is called DispatcherConfig.java.
package com.shengwang.demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @EnableWebMvc @ComponentScan("com.shengwang.demo.controller") public class DispatcherConfig extends WebMvcConfigurerAdapter{ // To use jsp @Bean ViewResolver internalViewResolver() { // the view resolver bean ... InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/"); resolver.setSuffix(".jsp"); return resolver; } // config static resources @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // Don't forget the ending "/" for location or you will hit 404. registry.addResourceHandler("/img/**").addResourceLocations("/static/images/"); registry.addResourceHandler("/js/**").addResourceLocations("/static/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/static/css/"); } }
The servlet context contain controller beans and a view resolver bean to render JSP page, just for demo. Usually it extends WebMvcConfigurerAdapter, so you can configure web mvc specific behaviours, such as static resource mapping or async support by overriding corresponding methods. (WebMvcConfigurerAdapter workds as default web mvc config with a bunch of empty methods)
The content of the RootConfig.java and DispatcherConfig.java are list here for completeness of the demos. Just remember RootConfig.java for root context, DispatcherConfig.java for servlet context. They are used in following demos. For both demos, the hierrarchy looks like below.
1. Demo One - Use WebApplicationInitializer directly
The MyWebApplicatonInitializerWithoutSpringSecurity.java implements interface WebApplicationInitializer. Spring mvc doesn't need this class to be a bean, no @Bean or @Component annotation for this class.
package com.shengwang.demo; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.DispatcherServlet; public class MyWebApplicatonInitializerWithoutSpringSecurity implements WebApplicationInitializer { @Override public void onStartup(ServletContext container) throws ServletException { useRootContext(container); useDispatcherContext(container); addFilter(container); } private void useRootContext(ServletContext container) { // Create the 'root' Spring application context AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(RootConfig.class); // <-- Use RootConfig.java // Manage the lifecycle of the root application context container.addListener(new ContextLoaderListener(rootContext)); } private void useDispatcherContext(ServletContext container) { // Create the dispatcher servlet's Spring application context AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext(); dispatcherContext.register(DispatcherConfig.class); // <-- Use DispatcherConfig.java // Define mapping between <servlet> and <servlet-mapping> ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet( dispatcherContext)); dispatcher.addMapping("/"); dispatcher.setLoadOnStartup(1); } private void addFilter(ServletContext container) { String filterName = "WhatEverYouWantToNameYourFilter"; String filterBeanName = "mdcInsertingServletFilter"; container.addFilter(filterName, new DelegatingFilterProxy(filterBeanName)).addMappingForUrlPatterns(null, false, "/"); } }
2. Demo Two - Use AbstractAnnotationConfigDispatcherServletInitializer for simplicity
Define own class extends from this abstract class.
package com.shengwang.demo; import javax.servlet.Filter; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MyWebApplicationInitializerWithoutSpringSecurity extends AbstractAnnotationConfigDispatcherServletInitializer { // return null, if you don't use both root and servlet context @Override protected Class<?>[] getRootConfigClasses() { return new Class[] {RootConfig.class}; } // return null, if you don't use both root and servlet context @Override protected Class<?>[] getServletConfigClasses() { return new Class[] {DispatcherConfig.class}; } // map dispatcherServlet to "/" @Override protected String[] getServletMappings() { return new String[] { "/" }; } // returned filters will be register to dispatcherServlet @Override protected Filter[] getServletFilters() { // bean defined in RootConfig.java String beanNameOfFilter = "mdcInsertingServletFilter"; return new Filter[] { new DelegatingFilterProxy(beanNameOfFilter) }; } }
There are 4 override mothods, each has a every clear purpose.
3. Misc
To use java-based spring mvc to replace web.xml, don't forget to config maven war plugin to ignore the "web.xml missging error" in the pom.xml.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.2</version> <!-- ignore missing web.xml error --> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin>
See Also
Simplified Web Configuration with Spring by Josh Long
How will you configure Logback with AbstractAnnotationConfigDispatcherServletInitializer ??
ReplyDelete