Статьи

Spring Boot: мой любимый инструмент, позволяющий сэкономить время — создание автоконфигурации, создание бобов, сотрясение микроконтейнера

Одно из распространенных заблуждений, связанных с Java-приложениями на основе Spring, заключается в том, что они требуют огромного количества настроек, прежде чем можно будет даже начать работу над реальной проблемой домена, которую приложение должно решать. Это происходит главным образом из-за конфигураций XML, которые уже были значительно сокращены с аннотациями. Но все же, если вы хотите настроить веб-приложение как можно быстрее без конфигурационных файлов Spring (XML), вам необходимо загрузить и настроить веб-сервер, настроить соединение с базой данных, а затем написать все необходимые компоненты, постоянство. xml для hibernate, web.xml и т. д. Поскольку на самом деле вы хотели написать код для решения своей собственной проблемы, вы начинаете спрашивать себя, действительно ли это так сложно !?

Spring Boot призван решить проблемы, перечисленные выше, и обеспечить быструю загрузку приложений Spring. Важно понимать, что Spring Boot не является инструментом генерации кода. Скорее, это можно рассматривать как экономящее время соглашение, включающее автоконфигурацию микроконтейнеров с классами путей передачи классов. Например, если у вас есть зависимость Jetty в пути к классам, он создаст экземпляр встроенного сервера Jetty и запустит на нем ваше приложение. Если нет зависимости от веб-сервера, он будет запускаться как стандартное приложение Java.

Как это работает

Spring Boot запускает приложение из класса, аннотируемого  @ EnableAutoConfiguration . Вот один простой пример:

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

Ниже вы можете увидеть жизненный цикл Spring Boot более подробно:

Жизненный цикл Spring Boot

Фаза инициализации

На этапе инициализации Spring Boot проверяет, должно ли приложение работать в веб-среде, на основании зависимостей, найденных в его пути к классам. Он в основном сканирует путь классов приложений для так называемых типов веб-среды, таких как  Servlet  и ConfigurableWebApplicationContext . Если они действительно находятся в пути к классам, Spring Boot предполагает, что разработчик хочет запустить его как веб-приложение. После этого все  реализации ApplicationListener, определенные внутри  META-INF / spring.factories будут созданы экземпляры файлов по всему пути к классам приложения, чтобы они могли быть уведомлены с соответствующими событиями в течение жизненного цикла приложения позже. К ним относятся, по крайней мере, те реализации слушателя, которые уже определены в самом Spring Boot:

  • DelegatingApplicationContextInitializer  — загружает и применяет инициализаторы, указанные в context.initializer.classes в application.properties.
  • ContextIdApplicationContextInitializer  — устанавливает идентификатор контекста приложения на основе различных свойств. Для более подробной информации обратитесь к документации.

После этого   реализации ApplicationContextInitialiser загружаются аналогичным образом. Они также могут быть определены в  файлах META-INF / spring.factories в пути к классам приложения. По крайней мере, те реализации, которые уже определены в самой Spring Boot, включены:

  • ParentContextCloserApplicationListener  — Слушатель, который закрывает контекст приложения, если его родитель закрыт
  • VcapApplicationListener  — Слушатель для приложений VCAP (Cloud Foundry)
  • FileEncodingApplicationListener  —  ApplicationListener, который останавливает запуск приложения, если кодировка системного файла не соответствует ожидаемому значению, установленному в среде. По умолчанию это не имеет никакого эффекта, но если вы установите spring.mandatory_file_encoding  (или какой-либо из вариантов camelCase или UPPERCASE этого) на имя кодировки символов (например, «UTF-8»), тогда этот инициализатор вызывает исключение, когда  файл file.encoding System  свойство не равно ему.
  • ConfigFileApplicationListener  — прослушиватель, отвечающий за загрузку свойств приложения при запуске приложения Spring Boot.
  • DelegatingApplicationListener  — регистрирует все реализации ApplicationListener, определенные в файле application.properties, в  свойстве context.listener.classes . Например, context.listener.classes = mypackage.MyApplicationListener .
  • LiquibaseServiceLocatorApplicationListener  — заменяет liquibase  ServiceLocator  версией, которая работает с исполняемыми архивами Spring Boot.
  • ClasspathLoggingApplicationListener  — регистрирует путь к классам в  ApplicationStartedEvent  и ApplicationFailedEvent .
  • LoggingApplicationListener  — Настраивает структуру ведения журнала и сканирует logback и log4j в пути к классам. Логбэк имеет приоритет и по умолчанию включен в зависимости стартерной загрузки. Итак, чтобы использовать log4j, нужно исключить logback, как показано ниже:
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <exclusions>
  <exclusion>
<groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-logging</artifactId>
  </exclusion>
  </exclusions>
</dependency>
<dependency>
<groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

Приложение запущено

После  вызова SpringApplication # run (..)  будут реализованы реализации  интерфейса SpringApplicationRunListener . Цель этих прослушивателей — вызываться на разных этапах метода run ( запущенenvironmentPrepared , contextPreparedcontextLoaded , закончен), и единственной реализацией этого интерфейса версии 1.1.6 является EventPublishingRunListener . Эта реализация затем регистрирует все   реализации ApplicationListener, загруженные в приложение. Для реализации пользовательского слушателя можно использовать AddListener  из  SpringApplication  или добавить его в Свойство context.listener.classes . Попытка реализовать SpringApplicationRunListener  для прослушивания жизненного цикла Spring Boot не удастся, поскольку реализации этого интерфейса будут загружены только из  META-INF / spring.factories . Таким образом, способ получить доступ к жизненному циклу Spring Boot состоит в реализации  ApplicationListener, в который  EventPublishingRunListener будет публиковать события, соответственно. На этом этапе EventPublishingRunListener  публикует  событие ApplicationStartedEvent, и все зарегистрированные слушатели могут реагировать на него.

Подготовка среды

На этом этапе среда будет создана. Тип среды создается в следующем порядке:

  1. Пользовательская среда будет возвращена, если предоставляется setEnvironment .
  2. StandardServletEnvironment,  если веб-зависимости найдены в пути к классам (это уже было проверено на этапе инициализации).
  3. В противном случае  StandardEnvironment  будет создано.

После этого  EventPublishingRunListener  публикует  событие ApplicationEnvironmentPreparedEvent .

Создание и подготовка контекста приложения

Как и в случае со средой, тип создаваемого контекста приложения имеет следующий порядок:

  1. Пользовательский контекст приложения, если предоставляется с setApplicationContextClass .
  2. ConfigEmbeddedWebApplicationContext,  если в пути к классам найдены веб-зависимости.
  3. AnnotationConfigApplicationContext  будет создан.

После этого будет зарегистрирован хук отключения, если это не указано с помощью  setRegisterShutdownHook (false)Реализации ResourceLoader  и BeanNameGenerator  будут загружены, если они указаны с соответствующими установщиками, и заменят реализации по умолчанию.
В конце этого этапа будут вызваны пользовательские и стандартные инициализаторы и загружены исходные классы. В этот момент контекст приложения подготовлен, и  EventPublishingRunListener публикует   событие ApplicationPreparedEvent .

Обновление контекста

На этом этапе контекст обновляется, и все компоненты из корневого пакета (один класс с  @EnableAutoConfiguration ) будут загружены в контекст. Будут загружены все классы автоконфигурации (определенные в  META-INF / spring.factories ), за исключением классов , явно отключенных с помощью @EnableAutoConfiguration (exlude = ExludedClass.class) . Затем они сортируются на  основе  аннотации @Order . Первыми будут загружены файлы с наивысшим приоритетом: MessageSourceAutoConfiguration , PropertyPlaceholderAutoConfiguration , WebSocketAutoConfiguration , EmbeddedServletContainerAutoConfiguration , DispatcherServletAutoConfiguration,

Образец заявки

Этот пример приложения покажет, сколько можно загрузить с помощью Spring Boot. Мы сделаем простой сервис REST, который показывает различные цитаты. Будут использованы следующие технологии:

  • Spring MVC для сервиса REST
  • Spring Security для проверки подлинности конечных точек
  • Spring Data JPA для доступа к данным
  • Flyway Migration tool для создания и заполнения базы данных
  • H2 встроенная база данных

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>com.comsysto</groupId>
   <artifactId>spring-boot-sample</artifactId>
  <version>1.0.0</version>
   <dependencies>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
  <version>1.1.6.RELEASE</version>
  </dependency>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>1.1.6.RELEASE</version>
  </dependency>
  <dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
  <version>3.0</version>
  </dependency>
  <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
  <version>1.1.6.RELEASE</version>
  </dependency>
  <dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <version>1.4.181</version>
  </dependency>
   </dependencies>
   <build>
   </build>
</project>

Вы можете видеть, что есть только 5 зависимостей. Если вам нужно веб-приложение, вам просто нужно предоставить зависимость spring-boot-starter-data-web. Вы хотите начать прямо сейчас с JPA. Нет проблем, просто предоставьте  зависимость spring-boot-starter-data-jpa . Многие зависимости для одного варианта использования больше не нужны. Таким образом, управление зависимостями также упрощается.

Кроме того, нам не нужен  дескриптор web.xml,  потому что приложение работает на встроенном веб-сервере (Tomcat по умолчанию). Для запуска веб-сервиса нам нужно только написать класс контроллера, как показано ниже, и Spring Boot развернет его на встроенном Tomcat:

@RestController
public class QuoteController 
{
  @Autowired
  @Qualifier("quoteRepository")
  private final QuoteRepository repository;




  private final static Quote NONE = new Quote("None");




  @Autowired
  public QuoteController(QuoteRepository repository) 
  {
   this.repository = repository;
  }




  @Secured("USER")
  @RequestMapping (value = "/api", method = RequestMethod.GET)
  public Iterable<Quote> getAll() 
  {
   return repository.findAll();
  }




  @Secured("USER")
  @RequestMapping (value = "/api/{id}", method = RequestMethod.GET)
  public Quote getOne(@PathVariable Long id)
  {
   if (repository.exists(id))
  {
  return repository.findOne(id);
  }
  else
  {
    return NONE;
    }
  }
}

Spring boot добавляет аутентификацию по умолчанию для веб-сервисов, если обнаруживает Spring Security в пути к классам. Хотя это не так практично, это было настроено в другом классе. Добавление   роли «USER» в методы веб-службы отменяет имя пользователя и пароль по умолчанию.

@Configuration
public class SecurityConfiguration extends GlobalAuthenticationConfigurerAdapter
{
  @Autowired
  @Qualifier("restSecurityProperties")
  RestSecurityProperties properties;




  @Override
  public void init(AuthenticationManagerBuilder builder) throws Exception {
  builder.inMemoryAuthentication()
  .withUser(properties.username())
  .password(properties.password())
  .roles("USER");
  }
}

Для правильной работы приложения нам нужно создать таблицу в базе данных и заполнить ее. Для этого используется инструмент миграции Flyway. Он сканирует папку db.migration для файла с именем <префикс> <версия> __ <описание> .sql . В нашем случае это V1__init.sql . Вручную нам нужно было бы создать bean-компонент с классом Flyway и прикрепить  bean-  компонент dataSource в качестве свойства, такого как ниже, и вызвать migrate ():

<bean id="flyway" class="org.flywaydb.core.Flyway" init-method="migrate">
  <property name="dataSource" ref="..."/>
  ...
</bean>

К счастью Spring загрузка создала этот компонент и добавила DataSource  к нему. Таким образом, единственной частью, которая требовалась от разработчика, было создание файла миграции, т.е. не требуется никакой конфигурации.

Что касается доступа к данным, Spring boot автоматически настроит любой найденный им класс репозитория и подключит к  нему  bean-компонент dataSource . Таким образом, разработчику нужно только определить класс Entity и интерфейс репозитория.

@Repository
public interface QuoteRepository extends CrudRepository<Quote, Long>
{
}




@Entity
public class Quote
{
  @Id
  @GeneratedValue
  Long id;
   private String quote;




   protected Quote() {}




   public Quote(String quote) 
   {
    this.quote = quote;
   }




   public Long getId() 
   {
    return id;
   }




   public String getQuote() 
   {
    return quote;
   }




   @Override
   public String toString() 
   {
    return "id: " + id + " Quote: " + quote;
   }
}

Ну, вот как просто запустить и запустить одно веб-приложение с Spring Boot. Тогда, в старые добрые времена корпоративной java, это считалось полноценным трехуровневым J2EE-приложением. С Spring Boot (благодаря нескольким другим превосходным фреймворкам с открытым исходным кодом, таким как Spring, Spring Data и Flyway в этом случае) почти ничего не остается, кроме автоматической настройки;). В следующем посте моего блога будет рассказано о том, как немного более сложное приложение Spring Batch с большим количеством XML-файлов конфигурации может извлечь выгоду из интеграции в сказочную страну Spring Boot.


In case you’ve become curious and would like to discover which potential for innovative software solutions your company holds, get to know us: either in a first conversation, an individually crafted workshop or at one of our numerouscommunity events!