Курс "Промышленное программирование на Java". Набор лекций 1. "Сборка и компоновка приложения".
Сборка проекта. Apache Maven. Компоновка и верхнеуровневый дизайн приложения. Depencency Injection. Spring Framework. Spring Boot. Обзор популярных утилитарных библиотек. Логгинг, slf4j, log4j.
МФТИ, 2014 год. Лектор - Лаврентьев Федор Сергеевич
7. Инициализация
Service service = new ServiceImpl();
FooHandler handler = new FastFooHandler(HANDLE_SPEED);
service.setHandler(handler);
int importantParameter = Integer.parseInt(args[1])
service.setImportantParameter(importantParameter);
Listener l1 = new LazyListener();
Listener l2 = new DilligentListener();
service.setListeners(Arrays.asList(l1, l2));
service.init();
8. Инициализация
class ServiceImpl implements Service {
@Autowired
Handler handler;
@Autowired
List<Listener> listeners;
@Value(“#{args[1]}”)
int importantParameter;
@PostConstruct
void init() { … }
}
9. У Java очень большое сообщество
Все ваши задачи кто-то уже решил
К любому сервису кто-то уже написал API
К каждому багу кто-то уже настругал
костыль workaround
11. Google Guava
Обертки над объектами и исключениями
Новые коллекции, Immutable-коллекции
Функциональщина
Многопоточность
Кеширование
Работа со строками
Хеш-функции
Работа с потоками и файловой системой
Волшебные Reflection
12. Apache Commons
Расширения для java.lang
Новые коллекции
Потоки и файловая система
Алгоритмы кодирования и хеширования
Сжатие и распаковка
Connection Pooling
…
16. Apache Maven
Maven - “фреймворк для автоматизации
сборки проектов...” (с) Wikipedia
Конфигурируется декларативно на XML-
языке POM (Project Object Model)
Проект Maven ~= java-модуль
“Соглашение превыше конфигурации”
17. Maven репозиторий
Многие java-библиотеки публично
опубликованы как проекты Maven
Maven Project ~= java-библиотека + POM-
файл + ресурсы
Проекты доступны в центральном
репозитории Maven
http://search.maven.org/
18. Maven проекты
Каждый опубликованный maven-проект
имеет уникальный идентификатор
Идентификатор состоит из имени группы,
имени артефакта, версии.
Внутри проекта смежные артефакты
различаются т.н. классификатором
23. Жизненные циклы проекта Maven
Сборка состоит из этапов - жизненных циклов
(lifecycles)
Циклы выполняются последовательно, с
первого до целевого
Краткий список жизненных циклов - verify,
compile, test, install, deploy
Сборочная задача может быть присвоена
какому-то этапу или выполняться отдельно
24. Работа с Maven
$ mvn dependency:tree # Показать дерево зависимостей
$ mvn dependency:copy-dependencies # Скачать все зависимости
$ mvn compile # Скомпилировать код
$ mvn test # Запустить юнит-тесты
$ mvn package # Собрать (сжать и т.д.) артефакты
$ mvn install # Загрузить артефакт в локальный репозиторий
$ mvn deploy # Загрузить артефакт в удаленный репозиторий
$ mvn clean # Убрать за собой всякое
$ mvn clean install # Пересобрать проект
$ mvn install # = mvn validate compile test package install
...
25. Зависимости
Зависимости проекта перечисляются в
блоке <dependencies>
Maven умеет отслеживать транзитивные
зависимости
Во время сборки maven автоматически
скачает все необходимые файлы
Задача dependencies:copy-dependencies
сложит зависимости в директорию target/
26. Scopes
Scope - ситуация или этап, в котором
потребуется зависимость
provided - только для компиляции
runtime - только в рантайме
test - только в тесте
compile (default) - на стадии компиляции и в
рантайме
system - предоставляется окружением
28. Плагины
Задачи в maven поставляются плагинами
Плагин содержит в себе несколько целей
Плагины содержатся в тех же
репозиториях, что и другие проекты
Плагины также “достаются” автоматически
Плагин конфигурируется в pom-файле
Ряд плагинов подключен по умолчанию -
clean, compile, install, surefire и др.
32. JAR-файлы
По окончании сборки проект будет
упакован в jar-файл
jar - это обычный zip-архив, содежащий
скомпилированные классы и метаданные
При желании, в аналогичные jar-файлы
можно сохранить исходники, ресурсы и пр
На последней стадии (деплое) jar-файлы
заливаются в репозиторий
33. Наследование проектов
Для поддержки принципа DRY maven
поддерживает наследование проектов
Основная конфигурация производится в
“родительском” проекте
Дочерние проекты подключают его с
помощью тега <parent>
Часто родительский проект является
корнем “многомодульного” проекта
34. Многомодульные проекты
Группа близких проектов может быть
объединена в многмодульный проект
Все модули перечисляются в корневом
проекте в теге <modules/>
Также в корневом проекте указывается
<packaging>pom</packaging>
35. Пример - корневой проект
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>ru.fizteh.java2</groupId>
<artifactId>parent-pom</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<modules>
<module>example-jdbc</module>
<module>example-spring</module>
</modules>
<build/> ...
<dependencies/> ...
</project>
36. Пример - дочерний проект
<project>
<parent>
<groupId>ru.fizteh.java2</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0</version>
</parent>
<groupId/> <version/> … # Унаследованные параметры можно перезаписывать
<artifactId>example-jdbc</artifactId>
<packaging>jar</packaging>
<dependencies/> ...
</project>
40. Больше кода - сложнее работать
Со временем число сущностей растет
Число связей растет на порядок быстрее
Если не предпринять мер, быстро
наступает коллапс разработки
43. Логическая компонента
Компонента - автономная логическая
единица кода
Внутри компоненты происходит
контролируемое тесное взаимодействие
Наружу предоставляется API (контракт)
Компонента взаимодействует с соседями
через их API
48. Связность (сцепление) компоненты
Компонента должна быть осмысленна
Ее части должны иметь что-то общее
Связность - мера взаимосвязи между
составными частями одной компоненты
Принцип наименьшего удивления
опирается на связность
50. Фрактальная природа компоненты
Компонента создана на основе маленьких
составных частей
Их разработчик рассматривал каждую
такую часть как отдельную компоненту
Ваша сложная система - на самом деле,
одна из компонент в бизнес-процессе
53. Модульность
Модульность - принцип разработки ПО,
согласно которому код разделяется на
отдельные функционально законченные
сущности - модули
Это позволяет переиспользовать код
Это упрощает проектирование
Это упрощает дистрибуцию (см. Maven)
54. SPI - набор интерфейсов, предлагаемый к
реализации пользователями
API - набор моделей, интерфейсов и т.п.,
реализованный модулем
Контракт - формальное соглашение об
использовании модуля
Contract-first Development - парадигма
разработки “от контракта”
SPI, API, контракт
55. Интерфейс и реализация
Как правило, API модуля не содержит
сложных конструкций и зависимостей
Внешние интерфейсы (API) модуля можно
выделить в отдельный легкий модуль
Реализация остается в отдельном тяжелом
модуле, о котором пользователи могут не
знать
56. Зависимости между модулями
Переизбыток зависимостей в системе
вызывает непредсказуемые проблемы
Разделение реализации и интерфейса
сильно сокращает объем знаний и
зависимостей, необходимый для
использования модуля
61. Встроим зависимости
class FourLimbedMonster implements Animal {
Brain brain;
TeethHolder teeth;
Tail tail;
public FourLimbedMonster(DependencyInjector di) {
brain = di.get(Brain.class);
teeth = di.get(TeethHolder.class);
tail = di.get(Tail.class);
}
}
62. Встроим зависимости
void main() {
DependencyInjector di = new DependencyInjectorImpl();
di.put(new MonkeyBrain());
di.put(new FishTail());
di.put(new SharkJaws());
Animal animal = new FourLimbedMonster(di);
animal.doHunt();
}
63. Выделим зависимости в модули
class ModuleWithBodyParts {
public static void fillBodyParts(DependencyInjector di) {
di.put(new MonkeyBrain());
di.put(new FishTail());
di.put(new SharkJaws());
}
}
class ModuleWithAnimal {
public static Animal buildAnimal(DependencyInjector di) {
return new FourLimbedMonster(di);
}}
64. Выделим зависимости в модули
void main() {
DependencyInjector di = new DependencyInjectorImpl();
ModuleWithBodyParts.fillBodyParts(di);
Animal animal = ModuleWithAnimal.buildAnimal(di);
animal.doHunt();
}
65. Обобщим еще немного
interface DependenciesSource {
void fill(DependencyInjector di);
}
void main(List<DependenciesSource> sources) {
DependencyInjector di = new DependencyInjectorImpl();
for (DependenciesSource source : sources) {
source.fill(di);
}
di.get(Animal.class).doHunt();
}
68. Spring Framework
Мощная инфраструктурная платформа для
создания сложного JVM-based ПО
Интеграция со множеством популярных
фреймворков, внешних сервисов и JSR
Сквозная концепция программирования
Огромное сообщество
Де-факто стандарт Java-платформы
69. Из коробки:
Application Context - DI и каталог ресурсов
Интеграция с БД (JDBC, ORM, транзакции)
Web - Servlets, MVC, JSP, REST, ...
XML и JSON биндинги
SpEL - язык для управления ресурсами
AOP, Instrumentation и прочий сатанизм
Тестирование с JUnit и TestNG
...
70. Пример из жизни
public interface Brain {
String getIdea();
}
public interface Animal {
String doHunt();
}
71. Пример из жизни
public class MonkeyBrain implements Brain {
public String getIdea() {
return "Banana";
}
}
72. Пример из жизни
public class Kitten implements Animal {
private Brain brain;
public void setBrain(Brain brain) {
this.brain = brain;
}
public String doHunt() {
return String.format("Meow, %s!",
brain.getIdea());
}
}
73. Пример из жизни
import org.springframework.context.ApplicationContext;
void main() {
ApplicationContext ctx = ...; // Magic
Animal animal = ctx.getBean(Animal.class);
animal.doHunt(); // Meow, Banana!
}
87. Bean Lifecycle
1.Расчитывается граф (DAG) зависимостей
2.Инициализируются зависимости бина
3.Выставляются ссылки на зависимости
4.Запускаются инициализаторы
5.Бин живет, контекст запущен
6.Запускаются деструкторы
7.Граф зависимостей уничтожается в
обратном порядке
97. Как передать коэффициент в бин?
class CalibratingOne {
@Autowired // Any double?!
double coefficient;
@Autowired // Other any double?!
double scale;
}
101. System properties
java …
-Dru.fizteh.app.mode=yarr
-Dru.fizteh.app.count=133
…
ru.fizteh.app.MainClass
command line args
102. @Resource
@Service
class CheapCalculator implements BillCalculator {
Bill calcCost(Order order) {
return new Bill(order.getCosts());
}
}
@Service
class CostyCalculator implements BillCalculator {
Bill calcCost(Order order) {
return new Bill(order.getCosts() * 2);
}
}
103. @Resource
class OrderProcessor {
@Autowired // which one?
BillCalculator billCalculator;
public BigDecimal processOrder(Order order) {
return billCalculator.calcCost(order).getPrice();
}
}
104. @Resource
class OrderProcessor {
@Resource(name = “cheapCalculator”)
BillCalculator billCalculator;
public BigDecimal processOrder(Order order) {
return billCalculator.calcCost(order).getPrice();
}
}
105. @Resource
@Service // = @Service(“cheapCalculator”) // Default name
class CheapCalculator implements BillCalculator {
Bill calcCost(Order order) {
return new Bill(order.getCosts());
}
}
@Service(“ohohoCalculator”) // Custom name
class CostyCalculator implements BillCalculator {
Bill calcCost(Order order) {
return new Bill(order.getCosts() * 2);
}
}
class OrderProcessor {
106. Взятие бинов из контекста
class Cook {
@Autowired // like ApplicationContextAware
ApplicationContext ctx;
public void cook() {
Pan pan = ctx.getBean(Pan.class)
Map<String, Food> namedFood =
ctx.getBeansOfType(Food.class);
Food fish = ctx.getBean(“fish”, Food.class);
}
}
107. Взятие параметров из контекста
class Accountant{
@Autowired // like ApplicationContextAware
ApplicationContext ctx;
public void report() {
Environment env = ctx.getEnvironment();
String cardHolder = env.getProperty(“cardHolder”);
double tax = env.getPropertyAsClass(“tax”,
Double.class)
}
}
108. Взятие файлов из контекста
class Reader {
@Autowired // like ResourceLoaderAware
ResourceLoader loader; // вообще-то, это тот же
контекст
public void read() {
Resource resource =
loader.getResource(“file.txt”);
URI uri = resource.getURI();
String body = IOUtils.toString( // apache commons-
io
resource.getInputStream());
}
}
109. Non-required wiring
class Logger {
@Autowired(required = false)
Printer printer;
public void log(String s) {
if (printer != null) {
printer.print(s);
}
…
}
}
119. Conversion Pattern explained
%d{ISO8601} - дата в формате ISO-8601
%p - приоритет (уровень логгинга)
%t - thread, имя потока
%c - имя логгера (имя класса)
%с{1} - последнее слово в имени логгера
%m - логируемое сообщение
%n - перевод строки
120. Conversion Pattern explained
printf-like formatting
До точки - минимальное число знаков
После точки - максимальное число знаков
С минусом - слева направо
Без минуса - справа налево
%-10.20c = поле от 10 до 20 символов,
заполнять слева направо, имя класса
121. log4j.xml root
<root>
<!-- Прочие категории интересуют нас от INFO и выше --
>
<priority value="INFO"/>
<!-- Льем все в console -->
<appender-ref ref="console"/>
</root>
122. %-d{ISO8601} [%t] %-5p %20.20c %m%n
2014-09-14 00:47:42,912 [main] INFO .java2.ServerStarter Starting web server
2014-09-14 00:47:42,913 [main] DEBUG eh.java2.util.Reader Reading config.xml
2014-09-14 00:47:42,913 [main] DEBUG eh.java2.util.Reader Reading pages.xml
2014-09-14 00:47:42,913 [main] WARN eh.java2.util.Reader Cannot read trololo.xml
2014-09-14 00:47:42,914 [main] ERROR .java2.ServerStarter Failed to open socket
java.net.BindException: Permission denied
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:382)
at java.net.ServerSocket.bind(ServerSocket.java:375)
at java.net.ServerSocket.<init>(ServerSocket.java:237)
at java.net.ServerSocket.<init>(ServerSocket.java:128)
at ru.fizteh.java2.ServerStarter.openSocket(ServerStarter.java:30)
at ru.fizteh.java2.ServerStarter.main(ServerStarter.java:23)
124. Что нужно сделать для запуска?
Допустим, компиляция уже завершена
Собрать библиотеки зависимостей,
собственные классы и файлы ресурсов
Построить из них classpath
Запустить JVM* с нужными аргументами
Иногда вместо отдельной JVM можно
запустить приложение в общем контейнере
126. Упаковка в Web App Archive (.war)
# Нужны правки в pom.xml, смотрите их в следующих сериях
mvn clean compile war:war
# Копируем war-файл в Application Server
copy target/myapp.war $TOMCAT_HOME/webapps/
# При должной настройке приложение запустится само
# Подробнее о веб-приложениях в следующих сериях
chrome http://localhost:8080/myapp
127. Spring Boot
Набор библиотек, бесстыдно облегчающий
запуск Spring-based приложений
Автоматически конфигурирует Spring,
manifest.mf, Application Server, etc…
Тесно интегрирован с Maven и Gradle
129. Spring Boot в коде
@Configuration
@EnableAutoConfiguration // Опционально
class AppConfig {
...
public static void main(String[] args) {
SpringApplication.run(AppConfig.class);
}
}
130. Запуск с помощью Spring Boot
mvn clean package # если в конфиге указан goal repackage
mvn clean package spring-boot:repackage # если не указан
# запуск прямо из maven
mvn spring-boot:run
# В repackaged jar упакованы зависимости и настроен main class
# Его можно распространять отдельно и запускать напрямую
java -jar target/app.jar