You can check out the complete source of this simple project from github. If you want to set up with XML configuration, check my earlier post.

Starting a new project, and irritated by xml configuration, I thought I'd try Spring @MVC (annotation configured MVC) with Jetty-8 embedded using the no xml servlet 3.0 configuration approach.

Initializing the Servlet Context

The Servlet 3 spec introduces the ability to configure the servlet context from code, via implementations of ServletContainerInitializer. You can dynamically configure Servlets and Filters here.

Spring @MVC provides an implementation of ServletContainerInitializer (SpringServletContainerInitializer) which tells the container to scan for classes which implement WebApplicationInitializer, so when using @MVC we need to provide an implementation of WebApplicationInitializer.

Here's a simple one that gets us up and running with a Spring DispatcherServlet mapped to "/" and JSP processing for *.jsp requests (including those forwarded from Controllers):

public class WebAppInitializer implements WebApplicationInitializer
{
    private static final String JSP_SERVLET_NAME = "jsp";
    private static final String DISPATCHER_SERVLET_NAME = "dispatcher";

    @Override
    public void onStartup(ServletContext aServletContext) 
    throws ServletException
    {       
        registerListener(aServletContext);
        registerDispatcherServlet(aServletContext);
        registerJspServlet(aServletContext);
    }

    private void registerListener(ServletContext aContext)
    {
        AnnotationConfigWebApplicationContext _root = 
            createContext(ApplicationModule.class);
        aContext.addListener(new ContextLoaderListener(_root));
    }

    private void registerDispatcherServlet(ServletContext aContext)
    {
        AnnotationConfigWebApplicationContext _ctx = 
            createContext(WebModule.class);
        ServletRegistration.Dynamic _dispatcher = 
            aContext.addServlet(
                DISPATCHER_SERVLET_NAME, new DispatcherServlet(_ctx));
        _dispatcher.setLoadOnStartup(1);
        _dispatcher.addMapping("/");
    }

    private void registerJspServlet(ServletContext aContext) {
        ServletRegistration.Dynamic _dispatcher = 
            aContext.addServlet(JSP_SERVLET_NAME, new JspServlet());
        _dispatcher.setLoadOnStartup(1);
        _dispatcher.addMapping("*.jsp");
    }

    private AnnotationConfigWebApplicationContext createContext(
        final Class<?>... aModules)
    {
        AnnotationConfigWebApplicationContext _ctx = 
            new AnnotationConfigWebApplicationContext();
        _ctx.register(aModules);
        return _ctx;
    }
}

Notice here that I am registering two "Modules" (a naming convention I've adopted for my Spring @Configuration classes) - ApplicationModule and WebModule. I like to configure the various layers of the application separately.

In ApplicationModule I'll put things like scheduled operations and any dependencies those operations need, while anything that is only needed during web request handling I'll put in WebModule.

ApplicationModule for a simple web-app might be unnecessary.

@Configuration
public class ApplicationModule
{
    // Declare "application" scope beans here (ie., 
    // beans that are not _only_ used by the web context)
}

WebModule will be used to configure Spring MVC, and for a simple web-app might look like this:

@EnableWebMvc
@Configuration
@ComponentScan(basePackages={"com.sjl"})
public class WebModule extends WebMvcConfigurerAdapter
{
    @Override
    public void addViewControllers(ViewControllerRegistry aRegistry)
    {
        aRegistry.addViewController("/").setViewName("index");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry aRegistry)
    {
        ResourceHandlerRegistration _res = 
            aRegistry.addResourceHandler("/WEB-INF/view/**/*");
        _res.addResourceLocations(
            "classpath:/META-INF/webapp/WEB-INF/view/");
    }

    @Bean
    public ViewResolver viewResolver() 
    {
        UrlBasedViewResolver _viewResolver = 
            new UrlBasedViewResolver();
        _viewResolver.setViewClass(JstlView.class);
        _viewResolver.setPrefix("WEB-INF/view/");
        _viewResolver.setSuffix(".jsp");
        return _viewResolver;
    }
}

I'm extending Spring's WebMvcConfigurerAdapter which provides a host of conveniences. Note that this WebModule sets the annotations @EnableWebMvc and @ComponentScan which are equivalent to the xml configuration you're probably familiar with:

<mvc:annotation-driven/>   
<context:component-scan base-package="com.sjl" />

The ResourceHandlerRegistration provides a mapping from requests forwarded to /WEB-INF/view/ onto the classpath location of the actual files. Without this, for example, Jetty won't be able to find your jsp files when Controller's forward requests to View's (the ViewResolver's prefix must be matched by the ResourceHandler's path-pattern).

What remains is to instantiate Jetty and have it find its configuration from the classpath. I won't list that here as its quite long, and the full working example is in github.

An important thing to point out is that there is a problem with current versions of Jetty (8.1.7) where Jetty won't find your WebApplicationInitializer classes unless they are either inside a Jar or in the WEB-INF/classes. When running embedded from your IDE neither of these will be true.

This results in log output like "No Spring WebApplicationInitializer types detected on classpath" and is why, in my WebServer class, I set a subclass of AnnotationConfiguration which overrides the default Jetty behaviour to also search for non-jar'd classes on the classpath (see the codefrom around line 75).

blog comments powered by Disqus