1. Обзор
Spring Boot-это самоуверенное, ориентированное на конфигурацию дополнение к платформе Spring, которое очень полезно для начала работы с минимальными усилиями и создания автономных приложений производственного уровня.
Этот учебник является отправной точкой для загрузки – способ начать работу простым способом, с помощью базового веб-приложения.
Мы рассмотрим некоторые основные конфигурации, интерфейс, быструю обработку данных и обработку исключений.
Дальнейшее чтение:
Как изменить порт по умолчанию в Spring Boot
Вступление к стартерам весенней загрузки
2. Настройка
Во-первых, давайте использовать Spring Initializr для создания базы для нашего проекта.
Созданный проект зависит от родительского загрузчика:
org.springframework.boot spring-boot-starter-parent 2.4.0
Начальные зависимости будут довольно простыми:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa com.h2database h2
3. Конфигурация приложения
Далее мы настроим простой класс main для нашего приложения:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Обратите внимание, как мы используем @SpringBootApplication в качестве основного класса конфигурации приложения; за кулисами это эквивалентно @Configuration , @EnableAutoConfiguration и @ComponentScan вместе.
Наконец, мы определим простой файл application.properties , который на данный момент имеет только одно свойство:
server.port=8081
server.port изменяет порт сервера с 8080 по умолчанию на 8081; конечно, доступно еще много свойств загрузки Spring .
4. Простое представление MVC
Теперь давайте добавим простой интерфейс, используя Thymeleaf.
Во-первых, нам нужно добавить зависимость spring-boot-starter-thymeleaf в ваш pom.xml :
org.springframework.boot spring-boot-starter-thymeleaf
Это включает Thymeleaf по умолчанию – дополнительная настройка не требуется.
Теперь мы можем настроить его в нашем application.properties :
spring.thymeleaf.cache=false spring.thymeleaf.enabled=true spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.application.name=Bootstrap Spring Boot
Далее мы определим простой контроллер и базовую домашнюю страницу – с приветственным сообщением:
@Controller public class SimpleController { @Value("${spring.application.name}") String appName; @GetMapping("/") public String homePage(Model model) { model.addAttribute("appName", appName); return "home"; } }
Наконец, вот наш home.html :
Home Page Hello !
Welcome to Our App
Обратите внимание, как мы использовали свойство, которое мы определили в наших свойствах, а затем ввели его, чтобы мы могли показать его на нашей домашней странице.
5. Безопасность
Далее, давайте добавим безопасность в наше приложение – сначала включив стартер безопасности:
org.springframework.boot spring-boot-starter-security
К настоящему времени вы, надеюсь, заметили закономерность – большинство библиотек Spring легко импортируются в наш проект с помощью простых загрузочных стартеров .
После того, как spring-boot-starter-security зависимость от пути к классу приложения – все конечные точки защищены по умолчанию, используя либо httpBasic , либо formLogin на основе стратегии согласования содержимого Spring Security.
Вот почему, если у нас есть стартер на пути к классу, мы обычно должны определить нашу собственную пользовательскую конфигурацию безопасности, расширив класс WebSecurityConfigurerAdapter :
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .permitAll() .and().csrf().disable(); } }
В нашем примере мы разрешаем неограниченный доступ ко всем конечным точкам.
Конечно, весенняя безопасность – это обширная тема, и ее нелегко охватить в нескольких строках конфигурации, поэтому я определенно призываю вас углубиться в эту тему .
6. Простая Настойчивость
Давайте начнем с определения нашей модели данных – простой Книги сущности:
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Column(nullable = false, unique = true) private String title; @Column(nullable = false) private String author; }
И его репозиторий, хорошо используя данные Spring здесь:
public interface BookRepository extends CrudRepository{ List findByTitle(String title); }
Наконец, нам, конечно, нужно настроить наш новый уровень персистентности:
@EnableJpaRepositories("com.baeldung.persistence.repo") @EntityScan("com.baeldung.persistence.model") @SpringBootApplication public class Application { ... }
Обратите внимание, что мы используем:
- @EnableJpaRepositories для сканирования указанного пакета на наличие репозиториев
- @EntityScan чтобы забрать наши объекты JPA
Чтобы все было просто, мы используем здесь базу данных H2 в памяти, чтобы при запуске проекта у нас не было никаких внешних зависимостей.
Как только мы включим зависимость H2, Spring Boot автоматически обнаружит ее и настроит нашу сохраняемость без необходимости дополнительной настройки, кроме свойств источника данных:
spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:mem:bootapp;DB_CLOSE_DELAY=-1 spring.datasource.username=sa spring.datasource.password=
Конечно, как и безопасность, настойчивость-это более широкая тема, чем этот базовый набор здесь, и вы, безусловно, должны изучить ее дальше .
7. Веб и контроллер
Далее давайте рассмотрим веб – уровень-и начнем с настройки простого контроллера – BookController .
Мы реализуем основные операции CRUD, раскрывающие Книгу ресурсы с помощью простой проверки:
@RestController @RequestMapping("/api/books") public class BookController { @Autowired private BookRepository bookRepository; @GetMapping public Iterable findAll() { return bookRepository.findAll(); } @GetMapping("/title/{bookTitle}") public List findByTitle(@PathVariable String bookTitle) { return bookRepository.findByTitle(bookTitle); } @GetMapping("/{id}") public Book findOne(@PathVariable Long id) { return bookRepository.findById(id) .orElseThrow(BookNotFoundException::new); } @PostMapping @ResponseStatus(HttpStatus.CREATED) public Book create(@RequestBody Book book) { return bookRepository.save(book); } @DeleteMapping("/{id}") public void delete(@PathVariable Long id) { bookRepository.findById(id) .orElseThrow(BookNotFoundException::new); bookRepository.deleteById(id); } @PutMapping("/{id}") public Book updateBook(@RequestBody Book book, @PathVariable Long id) { if (book.getId() != id) { throw new BookIdMismatchException(); } bookRepository.findById(id) .orElseThrow(BookNotFoundException::new); return bookRepository.save(book); } }
Учитывая, что этот аспект приложения является API, мы использовали здесь аннотацию @ RestController , которая эквивалентна @Controller вместе с @ResponseBody , чтобы каждый метод маршалировал возвращенный ресурс прямо в ответ HTTP.
Только одно замечание, на которое стоит обратить внимание – мы выставляем нашу Книгу сущность в качестве нашего внешнего ресурса здесь. Это хорошо для нашего простого приложения здесь, но в реальном приложении вы, вероятно, захотите разделить эти две концепции .
8. Обработка ошибок
Теперь, когда основное приложение готово к работе, давайте сосредоточимся на простом централизованном механизме обработки ошибок с помощью @ControllerAdvice :
@ControllerAdvice public class RestExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler({ BookNotFoundException.class }) protected ResponseEntity
Помимо стандартных исключений, которые мы здесь обрабатываем, мы также используем пользовательское исключение:
Исключение BookNotFoundException :
public class BookNotFoundException extends RuntimeException { public BookNotFoundException(String message, Throwable cause) { super(message, cause); } // ... }
Это должно дать вам представление о том, что возможно с помощью этого глобального механизма обработки исключений. Если вы хотите увидеть полную реализацию, ознакомьтесь с подробным учебником .
Обратите внимание, что Spring Boot также по умолчанию предоставляет отображение /error . Мы можем настроить его вид, создав простой error.html :
Error Occurred Error Occurred!
[status] errormessage
Как и большинство других аспектов загрузки, мы можем управлять этим с помощью простого свойства:
server.error.path=/error2
9. Тестирование
Наконец, давайте протестируем наш новый API книг.
Мы можем использовать @SpringBootTest для загрузки applicationcontext и проверки отсутствия ошибок при запуске приложения:
@RunWith(SpringRunner.class) @SpringBootTest public class SpringContextTest { @Test public void contextLoads() { } }
Далее, давайте добавим тест JUnit, который проверяет вызовы API, который мы написали, используя Rest Assured:
public class SpringBootBootstrapLiveTest { private static final String API_ROOT = "http://localhost:8081/api/books"; private Book createRandomBook() { Book book = new Book(); book.setTitle(randomAlphabetic(10)); book.setAuthor(randomAlphabetic(15)); return book; } private String createBookAsUri(Book book) { Response response = RestAssured.given() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(book) .post(API_ROOT); return API_ROOT + "/" + response.jsonPath().get("id"); } }
Во-первых, мы можем попытаться найти книги, используя различные методы:
@Test public void whenGetAllBooks_thenOK() { Response response = RestAssured.get(API_ROOT); assertEquals(HttpStatus.OK.value(), response.getStatusCode()); } @Test public void whenGetBooksByTitle_thenOK() { Book book = createRandomBook(); createBookAsUri(book); Response response = RestAssured.get( API_ROOT + "/title/" + book.getTitle()); assertEquals(HttpStatus.OK.value(), response.getStatusCode()); assertTrue(response.as(List.class) .size() > 0); } @Test public void whenGetCreatedBookById_thenOK() { Book book = createRandomBook(); String location = createBookAsUri(book); Response response = RestAssured.get(location); assertEquals(HttpStatus.OK.value(), response.getStatusCode()); assertEquals(book.getTitle(), response.jsonPath() .get("title")); } @Test public void whenGetNotExistBookById_thenNotFound() { Response response = RestAssured.get(API_ROOT + "/" + randomNumeric(4)); assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode()); }
Далее мы протестируем создание новой книги:
@Test public void whenCreateNewBook_thenCreated() { Book book = createRandomBook(); Response response = RestAssured.given() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(book) .post(API_ROOT); assertEquals(HttpStatus.CREATED.value(), response.getStatusCode()); } @Test public void whenInvalidBook_thenError() { Book book = createRandomBook(); book.setAuthor(null); Response response = RestAssured.given() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(book) .post(API_ROOT); assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode()); }
Обновление существующей книги:
@Test public void whenUpdateCreatedBook_thenUpdated() { Book book = createRandomBook(); String location = createBookAsUri(book); book.setId(Long.parseLong(location.split("api/books/")[1])); book.setAuthor("newAuthor"); Response response = RestAssured.given() .contentType(MediaType.APPLICATION_JSON_VALUE) .body(book) .put(location); assertEquals(HttpStatus.OK.value(), response.getStatusCode()); response = RestAssured.get(location); assertEquals(HttpStatus.OK.value(), response.getStatusCode()); assertEquals("newAuthor", response.jsonPath() .get("author")); }
И удалить книгу:
@Test public void whenDeleteCreatedBook_thenOk() { Book book = createRandomBook(); String location = createBookAsUri(book); Response response = RestAssured.delete(location); assertEquals(HttpStatus.OK.value(), response.getStatusCode()); response = RestAssured.get(location); assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode()); }
10. Заключение
Это было быстрое, но исчерпывающее введение в Spring Boot.
Мы, конечно, едва поцарапали поверхность здесь – в этой структуре есть гораздо больше, что мы можем охватить в одной вводной статье.
Именно поэтому у нас на сайте нет ни одной статьи о загрузке .
Полный исходный код наших примеров здесь, как всегда, на GitHub .