Derik Lima's Blog

How to Start a Java Web Project with Spring MVC

By Derik Lima on Mar 31, 2020

In this post, we will create a Java Web project with a bunch of perks: Maven, Spring MVC configured with Annotations-only (which means no web.xml) and Cargo Maven Plugin to make it easier for us to use Apache Tomcat without having to manage it separately. We will not be using Spring Boot in this project though.

The complete source code is available on my github on the following link.

Creating a Web Project (WAR) with Maven

So, let’s start by using the command-line to create our project skeleton:

mvn archetype:generate
  -DarchetypeGroupId=org.apache.maven.archetypes
  -DarchetypeArtifactId=maven-archetype-webapp
  -DgroupId={project-packaging}
  -DartifactId={project-name}
  -DinteractiveMode=false

After maven has finished creating the project, we can clean up the pom.xml file a bit and configure the Maven Compiler Plugin to use Java 8:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.deriklima.springmvc</groupId>
  <artifactId>springmvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>Spring MVC Web App</name>
  <url>https://deriklima.com/how-to-java-web-maven-spring-mvc-annotations</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
  </dependencies>

  <build>
    <plugins>
      <!-- Maven Compiler Plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
        </configuration>
      </plugin>

      <!-- Maven WAR Plugin -->
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.2</version>
      </plugin>
    </plugins>
  </build>
</project>

Adding Maven Dependencies

We will only need two dependencies for this project: Spring MVC and Java Servlets. Just a reminder that the Java Servlets dependency may have the scope set to provided as Tomcat already has it available by default.

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.4.RELEASE</version>
  </dependency>
  <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
  </dependency>
</dependencies>

Configuring Maven to not Fail When Missing web.xml

Maven might complain about the lack of a web.xml file when building the project, so we have to ask maven-war-plugin to not fail when it happens. Just add the configuration part of the block below to your maven-war-plugin declaration:

<plugin>
  <artifactId>maven-war-plugin</artifactId>
  <version>3.2.2</version>
  <configuration>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </configuration>
</plugin>

Using Cargo to Deploy App into Tomcat

The Cargo Configuration is a bit longer than usual, but I’ll try to make it very easy for you to use it.

First off, we need to add two properties to our properties section in our pom.xml:

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <java.version>1.8</java.version>

  <app.context>springmvc</app.context>
  <app.war.location>target/springmvc.war</app.war.location>
</properties>

These properties will later be used by cargo to define the context of your app (E.g. http://localhost:8080/springmvc, where springmvc is the context defined by you), and the war location. Cargo will use this location to copy your packaged war to Tomcat so it’s important to get it right. You can check the generated war file after running mvn package. The file should be located in the target/ folder.

Now we add the Cargo plugin. The declaration itself is quite self-explanatory, so I’ll not take too long here explaining every line. It basically downloads Apache Tomcat 9 to your machine and deploys the war that has been passed as argument. Later in this blog post I’ll explain which commands to use to have cargo run and deploy your app.

<plugin>
  <groupId>org.codehaus.cargo</groupId>
  <artifactId>cargo-maven2-plugin</artifactId>
  <version>1.7.10</version>
  <configuration>
    <container>
      <systemProperties>
      </systemProperties>
      <containerId>tomcat9x</containerId>
      <zipUrlInstaller>
        <url>
          https://repo.maven.apache.org/maven2/org/apache/tomcat/tomcat/9.0.29/tomcat-9.0.29.zip
        </url>
      </zipUrlInstaller>
    </container>
    <deployables>
      <deployable>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>simple-war</artifactId>
        <type>war</type>
        <location>${app.war.location}</location>
        <properties>
          <context>${app.context}</context>
        </properties>
      </deployable>
    </deployables>
  </configuration>
</plugin>

The basic boilerplate

Let’s start by configuring the ViewResolver Bean. This bean will define where your pages (the ones that will be rendered to HTML) will be located and which file type you will use. For this example, I’m going to add my pages to /WEB-INF/views folder and will append the files with .jsp. However, it’s up to you really, just keep this in mind when you are creating new pages.

Also notice that we have a few annotations on this class. I’ll not go in-depth about each of them but will just give a quick overview of what they do:

  • @Configuration: This annotation tells spring that this class declares one or more **@Bean**s so the Spring container can process it and make the beans available.
  • @EnableWebMvc: This annotation imports the Spring configuration for MVC projects. It has to be used in conjunction with @Configuration.
  • @ComponentScan: This annotation will tell Spring which package Spring container should scan in order to look for beans and configurations. If no package is declared (basePackages attribute), it will scan based on the package where the config is located. Another important feature is that the scanning is recursive, which means that any package that is a child of the base package will also be scanned.
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.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.deriklima.springmvc")
public class SpringMvcConfig {

  @Bean
  public ViewResolver viewResolver() {

    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

    viewResolver.setPrefix("/WEB-INF/views/");
    viewResolver.setSuffix(".jsp");

    return viewResolver;
  }
}

The last piece of the boilerplate is to actually create a Java version of the web.xml file. All you have to do is implement AbstractAnnotationConfigDispatcherServletInitializer and change two methods: First one is called getServletConfigClasses and you need to set which class defines your Spring configuration. In my case, I called it SpringMvcConfig. The second one is called getServletMappings and will need to define which mappings are gonna be processed by this Servlet Config that we declared on getServletConfigClasses.

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpringMvcDispatcherServletInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {

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

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

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

Creating your Controller

Let’s add a very simple Controller just to open a page called index.jsp that we will create next. I’ll not dive into the annotations here. Just keep in mind that @Controller is just a syntax-sugar for the annotation @Component and @RequestMapping basically defines the request URL to access that page. The first part of the url will be the DNS or IP address, then the port number, than the context (in our case, springmvc) and then what we’ve set on @RequestMapping, which in this case, is just the root (/). So the full URL to open the index page, in my case, would be http://localhost:8080/springmvc/

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomeController {

  @RequestMapping("/")
  public String showIndex() {
    return "index";
  }
}

Adding your Page

So now we should just add a page to our /WEB-INF/views folder. I’ll call it index.jsp as that’s what I called it on the Controller. I’ll not make anything fancy here as it’s not this articles’ goal, but feel free to improve it.

<html>
<body>
  <h2>Hello World!</h2>
</body>
</html>

How to Run and Deploy it with Cargo

In order to run the project, we first need to create the package:

mvn clean package

We can, then, run cargo (which in turn, will start Tomcat and deploy the package created before)

mvn cargo:run

And that’s it! You’ve made it!

Your project should be up and running by now. You can check it out by accessing http://localhost:8080/springmvc. If you have set a different context, you should use it instead.

If you make changes, you don’t need to shut down Cargo, package your war file again and then run Cargo once more. You can just open another command-line and run the following command:

mvn package cargo:deploy

This will first package the new changes and then deploy it to the already running Tomcat. It might take a few seconds though, so bear this in mind.

If something is not working for you, you can check out the complete source code on my github on the following link.

That will do it for this one. I hope it helps!

© Copyright 2024 by Derik Lima.