Skip to the content.

Технологии программирования

Назад на главную

Лекция 1. Сборка приложения Java. Клиент-серверная архитектура. Трехслойная архитектура бэкенда.

Содержание

  1. Запуск приложения на Java: компиляция и утилита javac
  2. Как скомпилировать приложение из нескольких классов?
  3. Можно ли сделать один исполняемый файл?
  4. Системы сборок.
  5. Структура проекта
  6. Gradle
  7. Task-и в системе Gradle
  8. Клиент-серверная архитектура
  9. Архитектура бэкенда
  10. Балансировка нагрузки
  11. Трехслойная архитектура бэкенда

Запуск приложения на Java: компиляция и утилита javac

Мы привыкли собирать и запускать приложение через Idea. Но что же происходит, когда мы нажимаем на зеленую кнопку “Запустить”? Запуск приложения на Java — это процесс, который включает компиляцию исходного кода программы и запуск скомпилированного файла. Этап компиляции в Java нужен для преобразования исходного текста программы в байт-код. Компиляция позволяет получить более эффективный код за счёт оптимизации.

Байт-код в Java — это набор инструкций, выполняемых виртуальной машиной Java. Он используется для оптимизации производительности и безопасности, поскольку машинный код может быть разным для разных платформ. Байт-код — это промежуточный уровень между исходным кодом Java и машинным кодом, который позволяет выполнять Java-приложения на разных платформах без необходимости компиляции кода каждый раз. Это обеспечивает переносимость и гибкость в разработке и развёртывании приложений. Больше информации про байт-код можно найти в статье Основы Java Bytecode на хабре.

Компиляция выполняется с помощью утилиты javac и генерирует файлы с расширением .class.

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

Возьмем для примера простую программу, состоящую из одного класса:


class Test {
   public static void main(String[] args) {
      System.out.println("This is our test compilation");
   }
}

Поместим этот код в файл Test.java. В этом примере есть объявление класса Test и объявление основного метода main, который является точкой входа в программу. Чтобы скомпилировать нашу программу, воспользуемся утилитой javac. Для этого в командной строке необходимо выполнить команду:

javac Test.java

После этого мы получим файл Test.class и сможем его запустить при помощи команды:

java Test

Как скомпилировать приложение из нескольких классов?

Расширим наш пример. Добавим класс Student, написав его в файле Student.java. Также добавим использование класса Student в методе main у класса Test. Код нашего примера:

class Test {
    public static void main(String[] args) {
        Student st = new Student();
        st.name = "Petya";
        st.sayHello();
        System.out.println("This is our test compilation");
    }
}

class Student {
    public String name;
    
    public void sayHello() {
        System.out.println("Hello, this is " + name);
    }
}

Создадим структуру файлов и папок в нашем проекте. Разместим все файлы с разрешением .java в папку src. Создадим папку bin, в которой мы в последствии разместим скомпилированный код в виде файлов .class. В командной строке перейдем в корневую папку вашего проекта и выполним команду:

javac -d bin ./src/*

Здесь bin — это папка, в которую будут сгенерированы скомпилированные классы, а src — путь к папке «src», содержащей все ваши классы. Теперь у вас есть скомпилированные классы в папке «bin». Чтобы запустить программу, используйте команду:

java -classpath ./bin Test

Здесь «Test» — это полное имя класса, содержащего метод main(), а «bin» — путь к сгенерированным классам.

Можно ли сделать один исполняемый файл?

Да, можно создать файл .jar. Создание исполняемого файла.jar при помощи командной строки позволяет быстро и легко упаковать вашу программу на Java в один файл, без использования специальных инструментов разработки. Это упрощает распространение и использование вашей программы на различных платформах и системах.

Для создания файла .jar нам потребуется создать файл манифеста. Файл манифеста (MANIFEST.MF) находится в каталоге META-INF в JAR-архиве. Он содержит метаданные, описывающие аспекты вашего JAR-приложения, такие как версии пакетов, класс приложения для выполнения, путь к классам и материал подписи. Файл манифеста обязателен при сборке JAR-файла. В манифесте указывается главный класс, который будет запускаться при выполнении JAR-файла, classpath, а также много другой информации. Без файла манифеста JAR-файл не будет корректно запускаться.

Файл манифеста для сборки JAR представляет собой список пар ключей и значений, сгруппированных в разделы, размещённый в каталоге META-INF. Формат файла манифеста — текстовый, каждая строка должна начинаться с символа «ключ: значение»:

main-class: Test
class-path: bin/

Поместим этот файл в root папку проекта и выполним команду:

jar -cmf manifest.mf spbu.jar  -C bin .

Мы получили файл spbu.jar. Теперь мы можем легко транспортировать нашу программу, просто скопировав наш jar файл. Чтобы запустить его нужно выполнить команду:

java -jar spbu.jar

Чуть подробнее этот пример разбирается в статье

Системы сборок.

Системы сборки используются для автоматизации процесса сборки и управления зависимостями в Java-проектах. Они упрощают процесс разработки, обеспечивают лучшую структуру проекта и снижают вероятность ошибок.

Отличие использования системы сборки от компиляции программы с помощью javac и запуска с помощью утилиты java заключается в том, что сборка представляет собой комплексный процесс, который включает тестирование, упаковку и другие этапы, благодаря инструментам автоматизации сборки, таким как Maven или Gradle.

Системы сборки приложения на Java:

Также существуют и другие системы сборок. Крупные компании иногда используют собственные системы сборки. Например, Google разработала свою систему Bazel, а компания Facebook разработала систему Buck. Из российских систем, в открытом доступе по лицензии Apache 2 доступна система сборки от Яндекса ya make

Мы в нашем курсе будем работать с системой сборки gradle.

Структура проекта

Структура проекта в Gradle основана на модульной системе и управляется файлом build.gradle. В этом файле указываются зависимости, плагины и другие настройки проекта.

Структура папок в проекте Gradle может быть следующей:

Файлы build.gradle располагаются в корне проекта и могут содержать настройки сборки, плагинов и зависимостей.

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>ru.javarush.testmaven</groupId>
  <artifactId>spbu_arts</artifactId>
  <version>1.0.0</version>

  <build>
     <defaultGoal>compile</defaultGoal>
     <sourceDirectory>src</sourceDirectory>
     <outputDirectory>bin</outputDirectory>
     <finalName>${project.artifactId}-${project.version}</finalName>
  </build>
</project>

Gradle

Файл build.gradle состоит из нескольких обязательных частей, которые определяют основные аспекты сборки проекта и управления зависимостями. Вот основные разделы и элементы этого файла:

Плагины

В начале файла укажите плагин java, который будет использоваться для компиляции и выполнения кода.

plugins {
 id 'java'
}

Репозитории

Укажите репозитории для поиска и установки зависимостей, например, Maven Central.

repositories {
    mavenCentral()
}

Репозиторий Maven Central — это центральный репозиторий для хранения и распространения артефактов (файлов с кодом, ресурсами и метаинформацией) проектов, использующих систему управления зависимостями Maven. Он является официальным репозиторием Maven и содержит миллионы предварительно упакованных артефактов, доступных для использования в проектах.

Для поиска и добавления зависимостей в ваш проект с использованием репозитория Maven Central, вам нужно указать идентификаторы (groupId, artifactId, version) в разделе dependencies файла build.gradle (рассмотрим это далее)

Чтобы найти нужную зависимость в репозитории Maven Central, используйте поисковую систему сайта Maven Central search.maven.org или на сайте mvnrepository.com. Введите идентификаторы группы и артефакта в соответствующие поля поиска, и вы получите список доступных версий зависимости. Выберите нужную версию и добавьте её в свой проект.

Зависимости

Опишите зависимости вашего проекта, включая библиотеки и зависимости для тестов.

dependencies {
    implementation group: 
      'org.springframework', name: 'spring-core', version: '4.3.5.RELEASE'
    implementation 'org.springframework:spring-core:4.3.5.RELEASE',
            'org.springframework:spring-aop:4.3.5.RELEASE'
    implementation(
        [group: 'org.springframework', name: 'spring-core', version: '4.3.5.RELEASE'],
        [group: 'org.springframework', name: 'spring-aop', version: '4.3.5.RELEASE']
    )
    testImplementation('org.hibernate:hibernate-core:5.2.12.Final') {
        transitive = true
    }
    runtimeOnly(group: 'org.hibernate', name: 'hibernate-core', version: '5.2.12.Final') {
        transitive = false
    }
}

Локальные зависимости:

implementation files('libs/joda-time-2.2.jar', 'libs/junit-4.12.jar')
implementation fileTree(dir: 'libs', include: '*.jar')

Виды объявления зависимостей:

Дока тут

Основная структура проекта Укажите источники кода (например, src/main/java, src/main/resources, src/test/java, src/test/resources) и настройки сборки (например, mainClass для запуска приложения).

sourceSets {
   main {
       java {
           srcDirs 'src'
       }
   }
}
sourceSets.main.output.classesDir = file("bin")

mainClassName = "src.BoxMachine"

defaultTasks 'compileJava', 'run'

Task-и в системе Gradle

Task в системе Gradle — это способ декларативного описания действий, которые нужно выполнить для сборки, тестирования или других целей проекта. Они используются для автоматизации процессов и упрощения работы с Gradle.

Задачи могут быть зависимыми друг от друга, что позволяет создавать сложные цепочки выполнения действий. Задачи также могут иметь параметры, которые можно настраивать в процессе выполнения.

Для создания своей task в Gradle, используйте DSL (Domain-Specific Language) на языке Groovy. Вот пример создания задачи для сборки проекта:

task hello {
    doLast {
        println 'Hello!'
    }
}

task fromSpbu(dependsOn: hello) {
    doLast {
        println "I'm from spbu"
    }
}

Можно добавить поведение к задаче

task helloSpbu {
    doLast {
        println 'I will be executed second!'
    }
}

helloSpbu.doFirst {
    println 'I will be executed first!'
}

helloSpbu.doLast {
    println 'I will be executed last!'
}

Некоторые tasks из плагина java

Плагин java для Gradle — это инструмент, который помогает в разработке Java-приложений. Он устанавливает конфигурацию, необходимую для сборки Java-проектов, такую как src/main/java как директория источника и задачи, такие как compileJava, jar и test.

Список всех задач смотри в доке здесь. Основные таски, которые мы часто будем использовать:

Клиент-серверная архитектура

Основные особенности

Клиент-серверная разработка — это метод разделения функций между клиентом (пользователем или приложением) и сервером. Клиентские приложения запрашивают информацию или услуги у сервера, а сервер отвечает на запросы, предоставляя данные или функциональность.

External Image

Клиент-серверная архитектура состоит из следующих компонентов:

В клиент-серверной разработке клиенты делятся на толстые и тонкие.

Толстые клиенты: работают независимо от сервера, используя свои вычислительные ресурсы.

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

Особенности клиент-серверной архитектуры:разделение функций:

Клиент-серверная архитектура может применяться в веб-разработке, мобильных приложениях, играх, базах данных и облачных вычислениях.

Архитектура бэкенда

Существует несколько подходов к архитектуре бэкенда. Наиболее распространены - монолит и микросервисы. Монолит — это единая монолитная программа, в которой все компоненты интегрированы и тесно связаны между собой. Микросервисы — это небольшие, независимые сервисы, каждый из которых отвечает за определённую функциональность. Монолит использует централизованные базы данных и обладает простотой разработки и развёртывания, но страдает от сложности масштабирования и поддержки. Микросервисы же более гибкие, масштабируемые и поддерживаемые, но требуют больше времени на разработку и внедрение.

External Image

Микросервисы — это небольшие, независимые и автономные единицы функциональности в приложении, которые работают на бэкенде. Они представляют собой полноценные версии бэкенда, так как способны обрабатывать запросы и предоставлять данные. Каждый микросервис обращается к своей собственной базе данных для хранения и обработки информации. Принципы разделения приложения на микросервисы включают:

Микросервисы взаимодействуют друг с другом через различные механизмы и протоколы, такие как:

Пример разделения приложения на микросервисы:

Предположим, у нас есть веб-приложение, которое предоставляет услуги бронирования авиабилетов, оплаты и управления заказами. Мы можем разделить это приложение на три микросервиса:

Такое разделение позволяет легко управлять и развивать каждый микросервис, а также обеспечивает более гибкое взаимодействие между ними.

Балансировка нагрузки

External Image

Балансировщик нагрузки — это сервис, который распределяет запросы между серверами кластера, чтобы равномерно распределить нагрузку и повысить отказоустойчивость и производительность IT-проектов. Балансировка может производиться по разным критериям. Балансировщик нагрузки нужен для того, чтобы сохранить доступность ресурса при аномальной нагрузке, предотвратить перегрузку отдельных серверов и потерю трафика. Устроен балансировщик нагрузки просто: он принимает запросы, распределяет их по серверам и собирает ответы обратно.

Один из популярных балансировщиков - это NGINX. Балансировщик NGINX — это инструмент, который позволяет распределить нагрузку между несколькими серверами приложений. В NGINX есть несколько методов балансировки, таких как round-robin, least-connected и ip-hash. Балансировщик использует эти методы для распределения запросов между серверами, обеспечивая равномерную нагрузку и высокую производительность. Балансировщик NGINX может быть использован для HTTP, HTTPS, FastCGI, uwsgi, SCGI, memcached и gRPC. Он также поддерживает возможность настройки сессии (sticky sessions) и контроля веса серверов.

Самый простой способ балансировки нагрузки — циклическая балансировка. Она распределяет клиентские запросы по серверам приложений в порядке ротации, например, при использовании трёх серверов первый запрос отправляется на первый сервер, второй — на следующий и так далее. Это эффективно, когда серверы имеют схожие возможности обработки и ресурсы.

Трехслойная архитектура бэкенда

Трёхслойная архитектура бэкенда состоит из трёх основных слоёв:

Разделение классов по слоям помогает:

см доску Miro