cherry.png

Building sample web-application with Spring Boot

07.08.2018
checkmark3

Java, Spring Boot, Servlet

As you know, many Java technologies such as JSP, JSF, JAX-RS are based on regular servlets, and Spring Boot is no exception. In our web-application, we'll create one REST Controller, several servlets, and our own error handling mechanism - all of which, one way or another, under the hood contains regular servlets. So we're writing Hello World on Spring Boot..
1. Project structure:
spring-boot-sample
│
├── src
│   └── main
│       ├── java
│       │   └── sample
│       │       ├── . . .
│       │       ├── . . .
│       │       ├── . . .
│       │       └── Application.java
│       └── resources
│           ├── templates
│           │   ├── . . .
│           │   ├── . . .
│           │   └── . . .
│           └── application.properties
└── pom.xml
2. Required Maven dependencies:pom.xml
<?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>spring-boot-sample</groupId>
    <artifactId>spring-boot-sample</artifactId>
    <name>spring-boot-sample</name>
    <version>0.1.0</version>
    <packaging>jar</packaging>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.plugin.vercion>3.8.0</maven.compiler.plugin.vercion>
        <spring.boot.version>2.0.4.RELEASE</spring.boot.version>
    </properties>

    <build>
        <defaultGoal>package</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.vercion}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
    </dependencies>
</project>
2. Application settings:application.properties
server.port=8080
server.compression.enabled=true
Now let's create the main application file. Spring Boot under the hood has an Embedded Servlet Container (Tomcat by default) and no additional Stand Alone Application Server is needed, so our web-application will be built into ajarfile with all necessary dependencies and run with thejava -jar spring-boot-sample-0.1.0.jarcommand.
The main feature of Spring Boot is that it looks at those beans that we have configured or overridden, looks for what we have missed and adds these beans on its own. In order to view all of those beans, which Spring Boot loads during initialization process of our web-application in the methodpublic static void main(String[] args),we'll add beanCommandLineRunner
The standard error handler in Spring Boot, which we'll need to override is the beanBasicErrorController
3. The main class of the web-application:Application.java
package sample;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import sample.controller.CustomErrorController;

import java.util.Arrays;

@SpringBootApplication
@ServletComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return new CommandLineRunner() {
            @Override
            public void run(String... args) {

                System.out.println("Inspecting the beans provided by Spring Boot:");
                System.out.println("---------------------------------------------");
                String[] beanNames = ctx.getBeanDefinitionNames();
                Arrays.sort(beanNames);
                int i = 0;
                for (String beanName : beanNames) {
                    System.out.println(++i + ". " + beanName);
                }
            }
        };
    }

    @Bean
    public BasicErrorController basicErrorController() {
        return new CustomErrorController(new DefaultErrorAttributes(), new ErrorProperties());
    }
}
Annotation@ServletComponentScanis needed for Embedded Servlet Container to create beans marked with the@WebServlet,@WebFilter,@WebListener.annotations. This is a specific of Spring Boot, because Stand Alone Application Server does it automatically.
4. Crearing custom error handler class:CustomErrorController.java
package sample.controller;

import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;
import sample.constructor.PageConstructor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomErrorController extends BasicErrorController {
    public CustomErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        super(errorAttributes, errorProperties);
    }

    @Override
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        String value = String.valueOf(status.value());
        String reasonPhrase = status.getReasonPhrase();

        try {
            response.getWriter()
                .write(PageConstructor.constructErrorPage("/templates/error.html", value, reasonPhrase));
        } catch (IOException e) {
            return super.errorHtml(request, response);
        }
        return null;
    }
}
5. Now let's create a REST Controller:MainController.java
package sample.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sample.servlet.HelloSpace;

import java.io.*;

@RestController
public class MainController {
    @RequestMapping({"/", "/index.html"})
    public String index() throws IOException {
        InputStream is = HelloSpace.class.getResourceAsStream("/templates/index.html");
        InputStreamReader isr = new InputStreamReader(is, "UTF-8");

        StringWriter sw = new StringWriter();

        int read;
        while ((read = isr.read()) != -1) {
            sw.write(read);
        }

        return sw.toString();
    }
}
Let's create servlets - we'll have three of them, which will greet the world, the space and the universe.. Therefore the logical beginning for this triad is an abstract class that unites the common methods of our servlets.
6. Abstract class:AbstractServlet.java
package sample.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public abstract class AbstractServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doResponse(response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doResponse(response);
    }

    abstract void doResponse(HttpServletResponse response) throws IOException;
}
Let's focus on thedoResponse(...)method and compare it to the methodindex()of our REST controller. In response, it will return one greeting line, so there will be one template for all three servlets. The error handler servlet template is built in the same way.
7. Servlet template:hello.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello <--greeted--></title>
</head>
<body>
    <p>Hello <--greeted-->!!</p>
</body>
</html>
Accordingly, it would be logical to have a constructor for all three servlets and for errors handling.
8. Constructor:PageConstructor.java
package sample.constructor;

import sample.servlet.HelloSpace;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;

public class PageConstructor {

    public static String constructHelloPage(String template, String greeted) throws IOException {
        InputStream is = HelloSpace.class.getResourceAsStream(template);
        InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);

        StringWriter sw = new StringWriter();

        int read;
        while ((read = isr.read()) != -1) {
            sw.write(read);
        }

        return sw.toString().replace("<--greeted-->", greeted);
    }

    public static String constructErrorPage(String template, String value, String reasonPhrase) throws IOException {
        InputStream is = HelloSpace.class.getResourceAsStream(template);
        InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);

        StringWriter sw = new StringWriter();

        int read;
        while ((read = isr.read()) != -1) {
            sw.write(read);
        }

        return sw.toString()
                 .replace("<--value-->", value)
                 .replace("<--reasonPhrase-->", reasonPhrase);
    }
}
For example, I will give only one servlet - the others look similar.
9. Servlet:HelloWorld.java
package sample.servlet;

import sample.constructor.PageConstructor;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

@WebServlet(urlPatterns = "/hello-world.html")
public class HelloWorld extends AbstractServlet {
    @Override
    void doResponse(HttpServletResponse response) throws IOException{
        response.getWriter()
                .write(PageConstructor.constructHelloPage("/templates/hello.html", "World"));
    }
}
Everything is ready - you can run. The entire code of our web-application can be found here:view code

facebookvkontaktetwitterodnoklassnikimailrulivejournal

Comments

O0O0O0O0
Commentator
01.01.1970 03:00 (MSK)
There are no comments yet.. You can be the first..