cherry.png

Строим веб-приложение с использованием Spring Boot

07.08.2018
checkmark3

Java, Spring Boot, Servlet

Как известно, многие технологии Java, такие как JSP, JSF, JAX-RS базируются на обычных сервлетах, и Spring Boot здесь не исключение. В нашем веб-приложении мы создадим один REST Controller, несколько Servlet-ов и собственную обработку ошибок - все это так или иначе под капотом содержит обычные сервлеты. Так что пишем Hello World на Spring Boot..
1. Cтруктура проекта:
spring-boot-sample
│
├── src
│   └── main
│       ├── java
│       │   └── sample
│       │       ├── . . .
│       │       ├── . . .
│       │       ├── . . .
│       │       └── Application.java
│       └── resources
│           ├── templates
│           │   ├── . . .
│           │   ├── . . .
│           │   └── . . .
│           └── application.properties
└── pom.xml
2. Необходимые зависимости Maven: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.properties
server.port=8080
server.compression.enabled=true
Теперь создадим основной файл приложения. Spring Boot под капотом имеет Embedded Servlet Container (по умолчанию Tomcat) и никакой дополнительный Stand Alone Application Server ему не нужен, так что наше веб-приложение будет собираться в файлjarсо всеми необходимыми зависимостями и запускаться командойjava -jar spring-boot-sample-0.1.0.jar
Основная фича Spring Boot заключается в том, что он сам смотрит на те бины, которые мы сконфигурировали или переопределили, ищет то, что мы пропустили и добавляет эти бины самостоятельно. Для того, чтобы посмотреть все бины, которые загружает Spring Boot в процессе инициализации нашего веб-приложения в методеpublic static void main(String[] args),добавим бинCommandLineRunner
Стандартный обработчик ошибок в Spring Boot, который нам надо будет переопределить, это бинBasicErrorController
3. Основной класс веб-приложения: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());
    }
}
Аннотация@ServletComponentScanнужна для того, чтобы Embedded Servlet Container создал бины помеченные аннотациями@WebServlet,@WebFilter,@WebListener.Это особенность Spring Boot, так как Stand Alone Application Server делает это автоматически.
4. Создадим собственный обработчик ошибок: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. Теперь создадим 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();
    }
}
Создаем сервлеты - у нас их будет три, то есть приветствовать будем мир, космос и вселенную.. Поэтому логичное начало для этой триады - это абстрактный класс, объединяющий общие методы наших сервлетов.
6. Абстрактный класс: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;
}
Остановимся на методеdoResponse(...)и сравним его с методомindex()нашего REST контроллера. В ответ он будет возвращать одну строку приветствия, соответственно шаблон для всех трех сервлетов будет один. Шаблон сервлета-обработчика ошибок строится по аналогичной схеме.
7. Шаблон сервлета:hello.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello <--greeted--></title>
</head>
<body>
    <p>Hello <--greeted-->!!</p>
</body>
</html>
Соответственно логично было бы иметь некий конструктор для всех трех сервлетов и для обработки ошибок.
8. Конструктор: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);
    }
}
Для примера приведу только один сервлет - остальные выглядят аналогично.
9. Сервлет: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"));
    }
}
Все готово - можно запускать. Полностью весь код нашего веб-приложения можно посмотреть здесь:смотреть код

facebookvkontaktetwitterodnoklassnikimailrulivejournal

Комментарии

O0O0O0O0
Комментатор
01.01.1970 00:00 (MSK)
Комментариев пока нет.. Вы можете стать первым..