JavaRush /جاوا بلاگ /Random-UR /Завоевание Spring Boot
Surplus
سطح
Москва

Завоевание Spring Boot

گروپ میں شائع ہوا۔
Доброго времени суток, уважаемый Читатель! И приятно познакомиться, даже если основной причиной заглянуть в скромную тему о первом знакомстве с разработкой на Spring Boot послужило помпезное название. Хотелось бы поделиться опытом выполнения вступительного задания для стажировки на портале JavaRush, излагая обозрение со стороны вполне обыкновенного студента технического университета, желающего проверить на прочность накопленные знания. Завоевание Spring Boot - 1Никоим образом не отрицаю возможное наличие грубости в приложенном codeе or методике мышления, и приветствую конструктивную критику, ведь именно благодаря “шишкам и ссадинам” удается развиваться в профессиональном направлении. Более того, совершенно не претендую на звание “панацеи” в решении поставленных условий и намеренно упускаю отдельные фрагменты программы, оставляя ключевым meaningм вхождение в относительно сложную тему без малейших последствий для нервной системы. Верно, опрометчиво отрицать очевидное: мне было тяжело и совершенно ничего не понятно до определенного момента. И если Вы испытываете схожие ощущения от первой встречи с заданием, то “Добро пожаловать!”. Напишем web-приложение на Spring Boot по упрощенной аналогии вступительного теста на стажировку с использованием двигателя шаблонов Thymeleaf и query-requestами на локальный server MySQL для фильтрации входящего массива информации. Итак, начнем же!

Spring Boot. What за зверь и How его готовить?

Если кратко и лаконично, — великолепный инструмент от компании Pivotel для сохранения драгоценного времени в процессе создания applications, исключающий потребность прямого подключения сторонних библиотек, написания внушительного полотна мапирования и сервлетов. Достаточно воспользоваться сборщиком Spring Initializr, интегрированном в IntelliJ IDEA Ultimate Edition (File - New - Project... - Spring Initializr) or расположенном на web-сервисе start.spring.io, указывая пакеты для включения из широкого перечня предложений.
Завоевание Spring Boot - 2
Следуя выдвинутому техническому заданию, воспользуемся джентльменским набором, стандартным для создания простого web-applications с учетом использования базы MySQL:
  • WEB — основной компонент для разработки web-applications, включающий локальный server Apache Tomcat по стандартному addressу localhost:8080 и универсальный фреймворк Spring MVC.

  • DevTools — используется для быстрого перезапуска applications в горячей JVM при обнаружении изменений в скомпorрованном codeе or шаблонах; более того освобождает от очистки cache у Thymeleaf, если выбранный движок включен в проект.

  • JPA — технология требуется для работы с базами данных и обеспечивает an objectно-реляционное отображение Java an objectов, предоставляет API (Hibernate в нашем случае) для управления, сохранения и получения сущностей.

  • Thymeleaf (Mustache, AngularJS, Vaadin и далее) — двигатель шаблонов для визуализации applications; благодаря относительному знакомству с принципами html остановил выбор на Thymeleaf, выдвинувшему язык на краеугольный столп мира.

  • MySQL — подключает драйвера Java Database Connectivity для выполнения SQL-requestов к базе данных.
После окончательного выбора компонентов и создания получаем обыкновенную архитектуру web-applications с готовыми для дальнейшего наполнения директориями. Фрагменты для взаимодействия с визуальной частью, будь то графические стor CSS, стандартные pages HTML or функционал JavaScript, должны располагаться в “resources”, а back-end составляющая, соответственно, подразумевается для размещения в “java”. Отдельно стоит обратить внимание на файл pom.xml в корневом диапазоне, хранящий структуру проекта и зависимости между компонентами. Если требуется в дальнейшем расширить функционал дополнительными пакетами or удалить лишнее, то следует провести манипуляции между тегов <dependencies></dependencies> согласно схожего метода.
Завоевание Spring Boot - 3

Первые шаги в большое будущее

Далее возникает довольно интересный и вполне логично обоснованный вопрос: “А что делать теперь? Как это будет работать?”. Программа строится на принципах Model-View-Controller: организует считывание сущностей из подключенной базы данных (Model) и отображается в пользовательском интерфейсе с elementми управления (View); связь между компонентами и выполнение действий согласно переданных requestов выполняется благодаря Controller. Именно создание ключевых элементов служит опорной точкой для продолжения разработки. Дабы избежать скользкой дорожки и сохранять уважение товарищей на трудовом поприще, следует располагать компоненты в соответствующих директориях (скажем, поместить файл Controller в папку controllers в ветку “java”) и бережно хранить порядок на рабочем месте.

Сущность — маленькая деталь в большом механизме

Или иначе — наша Модель согласно поставленных в задаче условий. Отступая от тематики обсуждения и возвращаясь к вступительному проекту, можно уверенно утверждать о минимальных различиях между заданиями и придерживаться в дальнейшем обозрении усредненной концепции. Скажем, заметок в записной книжке, включающих:
  • Идентификационный номер для определения расположения в общем потоке;
  • Текстовое сообщение из определенного количества символов;
  • Дату добавления пользователем в общей перечень;
  • Логическую переменную для определения “Сделано or не сделано” (“Прочитано or не прочитано”).
Следовательно, создадим класс Note в директории с названием “entity” и добавим соответствующие поля:

@Entity
public class Note {

   @Id
   @GeneratedValue
   private int id;
   private String message;
   private Date date;
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}
Очередное отклонение от темы обсуждения для большего понимания происходящего с теоретической позиции. Связь между компонентами в Spring задается annotationми, - специальными указателями перед an objectми, каждый из которых выполняет определенную роль в механизме и начинается символом “@”. Аннотация @Entity указывает Spring Boot на принадлежность последующих данных класса к “Сущности”, а @Id и @GeneratedValue задают выбранное поле идентификатором с автоматической генерацией итератора при обработке массива информации. Специально упускаю добавление стандартных Getter and Setter для увеличения компактности визуального формата. Далее, принимая во внимание использование базы данных для хранения записей, переходим на следующую ступень в разработке applications: создадим в директории “repository” интерфейс NoteRepository, - связующий элемент в цепочке обмена, - и унаследуем наиболее подходящий для дальнейшей работы репозиторий с указанием хранимой сущности и целочисленного итератора для обращения.

public interface NoteRepository extends JpaRepository<Note, Integer> {
}
Собственно, всё. Кратко и лаконично. Теперь Spring Boot будет использовать созданный компонент для организации взаимодействий с базой данных. Присутствует относительно много типов наследуемых репозиториев с различным потенциалом возможного действия. JpaRepository находится на вершине лестницы и обладает самым большим потенциалом, включая нижестоящие CrudRepository и PageAndSortingRepository. Более не будем углубляться и отходить от темы, ведь отдельные тонкости можно узнать на сайте Pivotel в технической documentации. Теперь, после воплощения образа данных и указания способов связи на стороне applications, требуется обратить внимание на создание базы MySQL в соответствующей внешней среде “MySQL Workbench”, заранее установленной на настольную платформу в сборке от официального разработчика с дополнительными пакетами для создания локального serverа:
Завоевание Spring Boot - 4
Далее, следуя за указаниями среды после нажатия на иконку с текущим локальным serverом в главном окне, создаем схему таблицы согласно полей нашей сущности (Note) и заполняем ее соответствующими данными. Следует отдельно уточнить тонкости диалекта MySQL, настойчиво требующие внимания для успешного достижения желаемого результата:
  • Отдельного булева типа How такового не существует. Любые действия с обработкой requestов преобразуют “true” or “false” в битовое meaning “1” or “0” соответственно;
  • Сохранение даты fully происходит в типе Timestamp. Если использовать привычный до мозга костей Date, то придется ограничиться лишь положением в календаре.
Завоевание Spring Boot - 5
После окончательного выполнения подготовительных действий, указываем “MySQL Workbench” на отправку данных в локальный server нажатием на значок с “молнией” на панели инструментов. Теперь, если добавление информации прошло корректно, можем уверенно возвращаться в родную IDE для дальнейшего продолжения разработки, добавляя конфигурацию текущей базы в application.properties (обычно, располагается в директории “resources”):

spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
И окончательно привязывая сущность Note к MySQL с помощью аннотаций. @Table указывает на использование таблицы с выбранным именем и схемой, а @Column - принадлежность переменных к определенному полю.

@Entity
@Table(name = "test", schema = "test", catalog = "")
public class Note {

   @Id
   @GeneratedValue
   private int id;
   @Column(name = "message")
   private String message;
   @Column(name = "date")
   private Date date;
   @Column(name = "done")
   private boolean done;

   public Note() {
   }

   public Note(String message) {
       this.message = message;
       this.date = new Date();
       this.done = false;
   }
}

Вид or пользовательский интерфейс

Увы, можно смело утверждать следующее: “Визуализация applications станет основным камнем преткновения без наличия малейших теоретических or практических знаний”. Будучи откровенным, front-end составляющая заняла поразительный объем от общего количества работы и уверенно трепала нервы на протяжение длительного временного периода. Но благодаря удивительной простоте Thymeleaf удалось обрести подходящий компромисс после череды феерических поражений. Дальнейшее обсуждение будет вестись именно о тонкостях использования выбранного движка, хотя общая концепция придерживается схожего положения. Основная методика заключается в возможности использовании чистейшего HTML и сборке конечного отображения из отдельных фрагментов для исключения множественного повторения идентичных участков. Предположим, архитектура пользовательского интерфейса состоит из главной pages, состоящей из навигационной панели с elementми управления (добавление новой записи, возвращение на главную page) и динамической таблицы для отображения сущностей с сортировкой по времени добавления заметки в направлении увеличения (ASC) or уменьшения (DESC) значения. Примем за стандартное положение отображение всех записей по возрастанию. Согласно иерархической политике выбранного движка шаблонов, составные элементы визуализации должны быть расположены на ветви “templates” в директории “resources”. Следовательно, дальнейшие манипуляции с компонентами принимают во внимание выдвинутые условия. Создадим главную page с именем “index” (or любым другим согласно личных предпочтений) на шаблоне html5. Например:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/head :: head"></head>
<body>
<div class="container">
   <div th:replace="fragments/header :: header"></div>
   <div th:if="${not #lists.isEmpty(notes)}">
       <div th:replace="operations/list :: notebook"></div>
   </div>
   <div th:replace="fragments/footer :: footer"></div>
</div>
</body>
</html>
И так, разложим по полочкам ключевую составляющую конечного applications. Thymeleaf использует отдельный синтаксис для указания применения procedures и начинается с ключевого слова “th:”, link на библиотеку с которым подсоединяется в обязательном порядке в открывающем тэге <html>.

<div th:if="${not #lists.isEmpty(notes)}">
Операция “if” совершенно не отличается от привычного образа действий и проверяет входящий атрибут “notes” на наличие an objectов для дальнейшего отображения. Следует отдельно упомянуть о перехлестывании темы с использованием Controller, принимая во внимание применение оного для организации взаимодействия модели и визуализации. Многие туманные моменты обретают очертания в дальнейшем, просто возвратитесь назад при наличии желания.

<head th:replace="operations/list :: notebook"></head>
Операция “replace” указывает на замену “заглушки” or действующего блока на выбранный фрагмент из текущей or отдельной pages - последний случай наглядно наблюдается в примере. Мы копируем фрагмент с названием “notebook” из “list.html” директории “operations” в <div></div> file “index”, fully заменяя содержимое в конечном пункте назначения. Исходящий же обладает следующим содержанием:

<!DOCTYPE html>
<!--suppress ALL -->
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">

<div th:fragment="notebook">
   <table class="table table-bordered table-hover horizontal-align">
       <thead>
       <tr>
           <th style="width: 5%">#</th>
           <th style="width: 60%">Message</th>
           <th class="dropdown" style="width: 20%">Date
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}"><i class="fa fa-chevron-circle-up"></i></a>
               <a th:href="@{'/sort/{sortDate}' (sortDate = 'DESC')}"><i class="fa fa-chevron-circle-down"></i></a>
           </th>
           <th style="width: 5%">Done</th>
           <th style="width: 5%">Edit</th>
           <th style="width: 5%">Delete</th>
       </tr>
       </thead>
       <tbody>
       <tr th:each="note : ${notes}">
           <td th:text="${note.id}" style="text-align: center">#</td>
           <td th:text="${note.message}">Message</td>
           <td th:text="${#dates.format(note.date, 'EEE, d MMM yyyy HH:mm')}" style="text-align: center">Date</td>
           <td style="text-align: center">
               <i th:if="${note.done} == true" class="fa fa-plus-square-o" style="font-size:20px;color:#337ab7"></i>
               <i th:if="${note.done} == false" class="fa fa-minus-square-o" style="font-size:20px;color:#337ab7"></i>
           </td>
           <td style="text-align: center"><a th:href="@{'/edit/{id}'(id=${note.id})}"><i class="fa fa-edit" style="font-size:20px"></i></a></td>
           <td style="text-align: center"><a th:href="@{'/delete/{id}'(id=${note.id})}"><i class="fa fa-trash" style="font-size:20px"></i></a></td>
       </tr>
       </tbody>
   </table>
</div>
</html>
Вновь обратимся к конструктивному обозрению и пройдем по использованным функциям Thymeleaf в порядке следования, опуская стандартный синтаксис HTML or использованные графические стor, и направляя внимание именно на понимание механизма движка шаблонов.

<div th:fragment="notebook">
Операция “fragment” задает название фрагмента и открывает возможность использования содержимого блока для команды “replace”. Причем! Никоим образом не исключается множественное применение в пределах одной pages, вновь выдвигая аналогию с proceduresами or функциями в языках программирования.

<a th:href="@{'/sort/{sortDate}' (sortDate = 'ASC')}">
Используется вызов аннотации @PostMapping в Controller с маппингом “/sort/{sortDate}”, где {sortDate}, - исходящий атрибут направления сортировки. Нечто схожее можно наблюдать в следующем блоке, добавляющем динамическое изменение в зависимости от положения выбранного пользователем element в цикле итерации:

<a th:href="@{'/edit/{id}'(id=${note.id})}">

<tr th:each="note : ${notes}">
Перечисление значений очень напоминает привычное использование блока for в синтаксисе Java: переменная “note” принимает текущий элемент из массива входящего атрибута ${notes} — массива с сущностями, — и используются в дальнейшем для изменения значений. Будучи откровенным, можно отводить отдельную статью для перечисления широкого спектра возможностей Thymeleaf с приведением примеров практического применения - двигатель шаблонов предельно прост и совершенно не требует изучения внушительного багажа дополнительного синтаксиса. Описанные выше функции изложены в технической documentации на официальном сайте разработчиков и исполняют ключевое meaning в организации связи с back-end. Следовательно, можно уверенно переходить к следующей и завершающей части. Конечно же, приложив остальные составляющие фрагменты визуализации в ссылке на готовое приложение в конце статьи.

Контроллер, администратор в маленькой компании

“Краеугольный камень в архитектуре web-applications”, - пожалуй, никоим образом не удастся подобрать более точное описание значимости компонента Controller в организации работы программы: большинство операций проводится именно связующим элементом между моделью и видом. Благодаря механике действия Spring Boot, можно уверенно использовать маппирование и методы requestа GET/POST без малейших проблем, автоматически подключить репозиторий с данными. Создадим класс NoteController в отдельном файле в директории “controllers”, вновь обращаясь к применению соответствующей аннотации:

@Controller
public class NoteController {

   private NoteService service;

   @Autowired
   public void setNoteService(NoteService service) {
       this.service = service;
   }

   @GetMapping("/")
   public String list(Model model) {
       return "index";
   }
}
Внимательный взгляд может заметить важное изменение в принципах построения архитектуры applications, связанное с добавлением сервиса для изолирования бизнес-логики от работы со службой управления базой данных. Выполненные действия требуются для увеличения универсальности готового продукта и предоставляют широкий простор для изменения функционала пользовательского интерфейса без потребности изменений в методах связи с базой данных. Стандартное представление никоим образом не выделяется из общей массы схожих: интерфейс расположен в отдельной директории и имплементируется классом с аннотацией @Service для обнаружения Spring Boot:

public interface NoteService {
   Note getNoteById(Integer id);
   void saveNote(Note note);
   void updateNote(Integer id, String message, boolean done);
   void deleteNote(Integer id);
   List<Note> findAll();
}

@Service
public class NoteServiceImpl implements NoteService{

   private NoteRepository repository;

   @Autowired
   public void setProductRepository(NoteRepository repository) {
       this.repository = repository;
   }

   @Override
   public Note getNoteById(Integer id) {
       return repository.findOne(id);
   }

   @Override
   public void saveNote(Note note) {
       repository.save(note);
   }

   @Override
   public void updateNote(Integer id, String message, boolean done) {
       Note updated = repository.findOne(id);
       updated.setDone(done);
       updated.setMessage(message);
       repository.save(updated);
   }

   @Override
   public void deleteNote(Integer id) {
       repository.delete(id);
   }

   @Override
   public List<Note> findAll() {
       return repository.findAll();
   }
}
Возвратимся к обозрению контроллера и разберем тонкости организации работы с помощью методов Spring Boot. Аннотация @Autowired сообщает о потребности автоматической привязки сервиса к указанной переменной соответствующего типа и устанавливает связь с базой данных. Следует обратить больше внимания на способ общения с видом, обозначенный аннотацией @GetMapping("/") и возвращающий page с именем “index” при получении вызова localhost:8080. Можно использовать различный подход, конкретизируя расширенное описание @RequestMapping(value = "/", method = RequestMethod.GET) or заменить возвращаемый тип на готовую ModelAndView. Однако, согласно текущего положения опыта практического применения, совершенно не замечаю кардинальных различий в конечном результате и использую привычный вариант. Расширим котроллер методом добавления новых элементов с помощью дополнительной вкладки. После нажатия пользователем на элемент навигационной панели происходит вызов @GetMapping("/new") и перенаправление на page “new” из директории “operations”, возвращающую параметр с именем “message” при подтверждении введенных данных использованием кнопки и выполняющую переaddressцию на главный блок. Отдельного упоминания требует потребность полного совпадения наименования переменной в окне ввода с именем передаваемого значения.

<input type="text" class="form-control" id="message" th:name="message" placeholder="Enter your note." maxlength="100"/>

@GetMapping("/new")
public String newNote() {
   return "operations/new";
}

@PostMapping("/save")
public String updateNote(@RequestParam String message) {
   service.saveNote(new Note(message));
   return "redirect:/";
}
Схожая методика используется для обновления записи. После нажатия на элемент управления происходит вызов мапирования @GetMapping("/edit/{id}") и передача идентификатора из url-строки, добавляется атрибут “note” с записью для дальнейшего редактирования. @RequestParam(value = "done", required = false) boolean done) указание конкретного значения оказывает ключевую роль в использовании checkbox при применении движка шаблонов Thymeleaf и устанавливается по умолчанию в положение “false”.

@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
   Note note = service.getNoteById(id);
   model.addAttribute("note", note);
   return "operations/edit";
}

@PostMapping("/update")
public String saveNote(@RequestParam Integer id, @RequestParam String message,
                      @RequestParam(value = "done", required = false) boolean done) {
   service.updateNote(id, message, done);
   return "redirect:/";
}
Удаление элементов из базы данных выполняется предельно просто и совершенно не требует выдающихся манипуляций, вызывая подходящую функцию сервиса с помощью переданного значения:

@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
   service.deleteNote(id);
   return "redirect:/";
}
Теперь внесем маленькие коррективы в готовые фрагменты и перейдем к увлекательному общению с MySQL при помощи query-requestов в Spring Data JPA, отдельно добавляя функцию управления простой фильтрацией перед закрытием Controller.

@Controller
public class NoteController {

   private String sortDateMethod = "ASC";

   @GetMapping("/")
   public String list(Model model) {
       List<Note> notebook = filterAndSort();
       model.addAttribute("notes", notebook);
       model.addAttribute("sort", sortDateMethod);
       return "index";
   }

private List<Note> filterAndSort() {
   List<Note> notebook = null;
   switch (sortDateMethod) {
       case "ASC":
           notebook = service.findAllByOrderByDateAsc();
           break;
       case "DESC":
           notebook = service.findAllByOrderByDateDesc();
           break;
   }
   return notebook;
}

Такой маленький, но такой важный Query.

Неловко признаваться, фильтрация значений вопреки ожиданиям оказалась очередным камнем преткновения в выполнении технического задания, уверенно преодолевая установленный пагинацией - разбиением массива данных на отдельные pages определенной размерности для дальнейшего отображения, - порог сложности. Скорее всего, сказывалась накопившаяся усталость, но… озарение снизошло после совершенно случайного столкновения с Query-requestами.

public interface NoteRepository extends JpaRepository<Note, Integer> {
   List<Note> findAllByOrderByDateAsc();
   List<Note> findAllByOrderByDateDesc();
}
Spring Data JPA предоставляет возможности для создания конкретизированных requestов в базу данных, избавляющих от потребности сортировать информацию после получения и обладающих широким потенциалом для применения. Например:

List<Note> findAllByOrderByDateAsc();
Метод будет преобразован в SQL request и отобразит все записи (findAll) с сортировкой (byOrder) по дате (byDate) в порядке возрастания (Asc). Более того, можно составлять сложные комбинации и проводить выборку по многим полям за единственное требование. Скажем, выбрать все (findAll) выполненные (byDoneTrue) записи в порядке (byOrder) уменьшения (Decs) по значению даты (byDate):

Page<Note> findAllByDoneTrueOrderByDateDesc(Pageable pageable);

Заключение or очередная исповедь начинающего программиста

Всё! Можно смело запускать web-приложение использованием комбинации Shift+F10 or нажатием на соответствующую иконку. Spring Boot соберет программу на Apache Maven и установит локальный server Apache Tomcat по addressу localhost:8080. Теперь достаточно лишь перейти по ссылке в любом браузере.
Завоевание Spring Boot - 6
И, конечно же, разработать методику для выполнения остальных бизнес-требований. Потенциал applications ограничивается приложенными усorями, находчивой инициативой и фантазией разработчика.
Завоевание Spring Boot - 7
Будучи откровенным и обращая внимание на пройденный путь, вновь и вновь убеждаюсь в верности выбранного направления и осознаю пользу от обучения на образовательном портале JavaRush. Благодаря множеству практических задач удалось возвратить манящий интерес к изучению программирования, окончательно забитый в устаревающей и удивительно скучной программе высшего учебного заведения схожего направления. Четыре месяца активного изучения материала в back-end стеке технологий вложor гораздо больше знаний по сравнению с целыми годами посещения лекций и лабораторных занятий. Хотите верьте, а хотите — нет. Желаю не пасовать перед трудностями вхождения в сложный материал, ведь именно благодаря преодолению препятствий мы становимся лучше и развиваемся в профессиональном и личностном плане. Надеюсь, маленький рассказ способствовал открытию свежих идеи для применения восхитительного инструмента под названием SpringBoot. P.S. Github.
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION