36

I have, what I would consider a pretty simple Spring MVC setup. My applicationContext.xml is this:

<mvc:annotation-driven />
<mvc:resources mapping="/css/**" location="/css/" />
<context:property-placeholder location="classpath:controller-test.properties" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:prefix="/WEB-INF/views/" p:suffix=".jsp" />

My web.xml is currently this:

  <servlet>
   <servlet-name>springDispatcherServlet</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- Map all requests to the DispatcherServlet for handling -->
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

I am trying to convert this set up to pure Java-based config. I've searched the web and so far, I've come up with stuff that explains (some what) how to do the Java config but doesn't explain how to register that Java config with the environment, i.e., the web context.

What I have so far in terms of @Configuration is this:

 @Configuration
 @EnableWebMvc
 @PropertySource("classpath:controller.properties")
 @ComponentScan("com.project.web")
 public class WebSpringConfig extends WebMvcConfigurerAdapter {

 @Override
 public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/css/**").addResourceLocations("/css/");
 }

 @Bean
 public ViewResolver configureViewResolver() {
     InternalResourceViewResolver viewResolve = new InternalResourceViewResolver();
     viewResolve.setPrefix("/WEB-INF/views/");
     viewResolve.setSuffix(".jsp");

     return viewResolve;
 }

 @Override
 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
   configurer.enable();
 }
}

How do I register this with the web container? I am using the latest spring (4.02).

Thanks!

user1902183
  • 3,203
  • 9
  • 31
  • 48

2 Answers2

51

You need to make following changes to web.xml in order to support java based configuration. This will tell the the DispatcherServlet to load configuration using the annotation based java configuration AnnotationConfigWebApplicationContext. You only need to pass the location of your java config file to the contextConfigLocation param, as below

<servlet>
  <servlet-name>springDispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
   </init-param>
   <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/*path to your WebSpringConfig*/ </param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

Update: Doing the same without making changes to web.xml

You can even do this without web.xml as Servlet Specification 3.0 makes the web.xml optional. You only need to implement/configure WebApplicationInitializer interface to configure the ServletContext which will allow you to create, configure, and perform registration of DispatcherServlet programmatically. The good thing is that WebApplicationInitializer is detected automatically.

In summary, one needs to implement WebApplicationInitializer to get rid of web.xml.

 public class MyWebAppInitializer implements WebApplicationInitializer {

 @Override
 public void onStartup(ServletContext container) {
  // Create the 'root' Spring application context
  AnnotationConfigWebApplicationContext rootContext =
                       new AnnotationConfigWebApplicationContext();
  rootContext.register(WebSpringConfig.class);

  // Manage the lifecycle of the root application context
  container.addListener(new ContextLoaderListener(rootContext));

  // Create the dispatcher servlet's Spring application context
  AnnotationConfigWebApplicationContext dispatcherContext =
                     new AnnotationConfigWebApplicationContext();
  dispatcherContext.register(DispatcherConfig.class);

  // Register and map the dispatcher servlet
  ServletRegistration.Dynamic dispatcher =
    container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/");
  }
}

Update: from comments
A slightly more convoluted explanation is also included in the official Spring reference Spring 4 Release

Reference:

http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/web/WebApplicationInitializer.html

knueser
  • 357
  • 3
  • 14
Santosh Joshi
  • 3,290
  • 5
  • 36
  • 49
  • Are you saying there is no way inside the servlet 3.0 container to not have web.xml at all? You're saying Spring forces you to have web.xml anyway? I just find it hard to believe, especially that I find on the web some ways of doing this without web.xml (at least claiming their code does it). I can't get their code to work and it seems outdated, so that's why I am asking, but it seems it should be possible without web.xml altogether. Besides, I am configuring the default Spring servlet in my WebSpringConfig already, so why configure it again in web.xml? – user1902183 Mar 11 '14 at 13:24
  • @user1902183, you pasted the `web.xml` so provided the solution using the `web.xml` approach. Yes we can get rid of `web.xml` completly. With `Servlet spec 3.0` and `Spring` 3.1 and above provides good support for that. I have updated the answer with without `web.xml` approach. – Santosh Joshi Mar 11 '14 at 13:42
  • 1
    Thanks! Turns out my previous attempts failed because my maven was pointing to servlet spec 2.5 jar. Either way, your answer was spot on. – user1902183 Mar 11 '14 at 17:20
  • 3
    A slightly more convoluted explanation is also included in official Spring reference now: http://docs.spring.io/spring/docs/4.0.5.RELEASE/spring-framework-reference/htmlsingle/#beans-java-instantiating-container-web – billc.cn Jun 19 '14 at 18:16
  • i need some reference please to help me !! am stuck in java config – Yassine Abdul-Rahman Aug 20 '14 at 18:12
  • what are the problems you are facing – Santosh Joshi Aug 21 '14 at 02:12
  • The value of contextConfigLocation should be the fully qualified java class that contains you main @Configuration (or a list of them). e.g. nz.co.mycompany.myapp.conf.MyWebAppConfig you don't need to include any path information, it will just scan the webapp classpath. – dan carter Oct 21 '15 at 03:18
  • @dancarter, Yes true, i meant the same. It is obviously a fully qualified class name. – Santosh Joshi Oct 21 '15 at 17:58
1

Java-based configuration without adding any elements to web.xml. WebApplicationInitializer is a perfect fit for use with Spring's code-based @Configuration classes

WebApplicationInitializer « Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically -- as opposed to (or possibly in conjunction with) the traditional web.xml-based approach. Implementations of this SPI will be detected automatically by SpringServletContainerInitializer, which itself is bootstrapped automatically by any Servlet 3.0 container. Using Servlet Spec 3.0 of Tomcat 7

From Spring 3.2 some Abstract class were listed which implemented WebApplicationInitializer which will be detected automatically by the SrevletContainer.

AbstractAnnotationConfigDispatcherServletInitializer extends
AbstractDispatcherServletInitializer extends
AbstractContextLoaderInitializer implements WebApplicationInitializer

Using Spring 4.1.6.RELEASE version with the modules core, web, webmvc, beans.

public class WebXML_DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { MvcServletXMLConfigurer.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

Java-based configuration to Serve Static Resources with Spring. Spring Boot

@Configuration
@EnableWebMvc // <mvc:annotation-driven />
@ComponentScan(value = {"com.github.yash777.controllers"})
// <context:component-scan base-package="com.github.yash777" />
public class MvcServletXMLConfigurer extends WebMvcConfigurerAdapter implements WebMvcConfigurer {

    /**
     * <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
     * p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
     * 
     * @return InternalResourceViewResolver as a bean configuration.
     */
    @Bean
    public InternalResourceViewResolver getInternalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        System.out.println("WebMvcConfigurer - addResourceHandlers() function get loaded...");

        // <mvc:resources mapping="/styles/**" location="/css/" />
        registry
            .addResourceHandler("/styles/**") 
            .addResourceLocations("/css/") // webapp/css/
            .setCachePeriod(3600)
            .resourceChain(true) // Spring 4.1
            .addResolver(new GzipResourceResolver()) // Spring 4.1
            .addResolver(new PathResourceResolver()); // Spring 4.1

        // <mvc:resources mapping="/static/**" location="/static/" />
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/", "classpath:/static/") // src/main/resources/static/
                .setCachePeriod(3600)
                .resourceChain(true)
                .addResolver(new PathResourceResolver());
    }
}

Listed a sample controller:

@Controller
@RequestMapping(value = { "/controller", "/c" })
public class Test extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @RequestMapping(value = {"/message", "/m"}, method = RequestMethod.GET )
    public void message(HttpServletRequest request, HttpServletResponse response ) throws IOException {
        System.out.println("@Controller Get method called.");
    }

    @RequestMapping(value = "/getView", method = RequestMethod.GET )
    public ModelAndView setViewName( Model model ) {
        System.out.println("GET... /getView");
        ModelAndView mav = new ModelAndView();
        mav.setViewName("test");
        return mav;
    }
}

WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>
Yash
  • 9,250
  • 2
  • 69
  • 74