Java Crib

Опыт

Элерон. Разработка системы для системы безопасности для посольств РФ, на C++. Standalone приложение, устанавливаемое локально на территории заказчика.

Panasonic. Получение данных из SAP и построение отчетов на под требования. Monolith на Java 7. Сервер JBoss. В качестве БД - Db2.

Ventra. попытка в управлении сервисами. Система управления эл. очередями в отделениях Сбера. Сбор и анализ информации от терминииалов. Работа только с логами вендора и Excel

iteco

АИС Проход-Питание (ИСПП). Переданная системам. учет действий, получаемых от терминалов и касс, установленных в ОУ

Monolith
Java 7
Spring + Tomcat
iBatis
Для отчетов POI
Elastic Logstash Kibana

ИС Сверки (АМПП). Собственная система. сравнение финансовых операций между выписками из банков и выписок из 1С. все в формате csv. в качестве буфера использовалась MySQL.

Monolith
Java 7
Spring + Tomcat
iBatis
MySQL
Apache POI - для генерации отчетов
ELK

Электронный дневник. Собственная система. Впервые Event Channel. Хранение успеваимости, посещений и олимпиад

Monolith
Java 11
OracleDb
EGK - Graylog
Rabbit MQ - для доставки событий из ИСПП
Jenkins

Зачисление в ОУ. Переданная системам. Хранение карточек учеников и распределение по ОУ и ДОУ города. В основном, нагрузка на БД. Особенно, в момент распределения

Monolith
Java 11
EE фреймворк, thymeleaf
OracleDb
EGK - Graylog
Active MQ - для общения между связанными сервисами
Jenkins
Интеграция с сервисами КриптоПРО

RussPass. Собственный бэк, переданный UI. сервис для бронирования туров, отелей, билетов и прочее. Алгоритмы построения маршрутов

4 Microservice
Java 11
Quarkus
MariaDB
Kibana
Grafana, Prometheus
UI на Flutter

ООО РЖД. отслеживание и приобретение билетов, постороение маршрутов с использованием стыковок. Fail

Microservice
Java 11
Spring Boot
ClickHouse
RabbitMQ
EGK

Система учета персонала. внутренний проект для компании. собственный jira - учета выполнения задач с календарем. впоследствии учитывалось при подсчете зп и премий

Monolith
Языки и версии-Java 11
Фреймворки-Spring Boot
Серверы приложений-Tomcat Container
Базы данных-PostgreSQL
Система сборки-Maven
Аутентификация MS Active Directory Auth

финтех

nda. проект для compliance, контроль исполнения 115 фз. получение транзакций из внешних источников и мануальный учет сотрудниками. Система очень старая. Ежедневный поток - около 1 млн. фин операций

Monolith
Java 11
Wilfly 17
Oracle -> Postgre
Множество интеграций по REST и Kafka
TeamCity

nda. вспомогательный родительской системе - сбор и аккумуляция транзакций из различных источников, их сохранение, выявление статусных кодов и передача родительской системе. Около 20 источников с отдельным сервисом. Пиковые нагрузки - до 7 миллионов целевых операций в сутки, из потока в 24 миллиона

Joined-at-the-database
Java 17
Spring boot 2.7
Redis - Sentinel
liquibase
Kuber
Postgre
Kafka - subscriber и publisher
REST для импорта и экспорта
Actuator

претиум. jira для банка - выставление всевозможных задач на 3 лицо

Microservice
Java 21
Spring boot 3
Postgre
REST
liquibase

Java

Память

Run-Time Data Areas - память для Java. Cуществует несколько:

  • Method Area - содержит информацию о классах, интерфейсах, методах, полях и других метаданных
  • Heap - хранит объекты. Содержит 2 вида:
    • Young Generation — содержит новые объекты. при заполнении переносит в Old
    • Old Generation — долгоживущие объекты. GC пробегает по нему и ищет объекты без ссылок для удаления
  • Java Stack - локальных переменные и стек вызовов для каждого потока + примитивы
  • Native Method Stack - хранение нативных данных
  • Program Counter Register - управляет потоками в JVM
  • Direct Memory - для работы с прямой буферизацией данных

Basic

Consutructor (все пункты поочередно, сверху-вниз):

1.Все указанные ниже действия для родительских классов
2.static
3.Instance переменные
4.super конструкторы
5.Конструктор
Для п. 1 последовательность: Object -> Number -> Integer -> MyCustomInteger

Операции

1. <exp>++, <exp>--
2. ++<exp>, --<exp>
3. !, ~, (type-cast), -, +
4. *, \, %
5. +, -
6. <<, >>, >>>
7. <, >, <=,  >=, instanceof
8. ==, !=
9. &, ^, |
10. &&, ||
11. <exp? <exp1: <exp2>
12. =, +=, -=, *=, \=, %=, &=, ^=, |=, <<=, >>=, >>>=
13. ->

if(number instanceof Integer intNumber && intNumber > 1) { intNumber++; }

Switch yield

int output = switch (sign) {
    case '+', 's' -> { yield a + b };
    case '-' -> a-b;
    case '^', 'x' -> {
        int tmp = 1;
        for (int i = 1; i <= b; i++) {
            tmp *= a;
        }
        yield tmp;
    }
    default -> 0;
};

Сравнение массивов, возвращает 0 если полностью равны - есть все элементы и все они равны:

Arrays.compare(T[], T[])

Использует Comparable при сравнении. Ошибка компиляции при сравнении несовместимых типов

Поиск позиции расхождений:

Arrays.missmatch(T[], T[])

String

Обрезка строк: strip предпочтительнее (учитывает) чем trim

indent добавлят пробелы вперед и назад. stripIndent убирает равное количество пробелов спереди и сзади


В String pool хрантся только литералы (на момент компиляции). Добавление в pool принудительно:

myString.intern();

Аннотации

Константы как и у Interface - public static final.

default у параметров не может быть высичляемым

Области:

- Классы, интерфейсы, enum, модули;
- Переменные (static, instance, local);
- Методы и конструкторы; так же, любые их параметры;
- Другие аннотации;
- Наложение аннотаций при type-cast:

@Target

TYPE - class, interface, enum, @interface;
FIELD - параметры класса, статические переменные, значения в enum;
METHOD - методы (только);
PARAMETER - параметры методов, конструкторов и лямбд;
CONSTRUCTOR - только конструкторы;
PACKAGE - аннотации на пакет;
TYPE_PARAMETER - параметризованные типы, ;
MODULE - модули;
ANNOTATION_TYPE - где угодно, где может участвовать Java тип. Может быть довольно противоречивым;
LOCAL_VARIABLE - аннотация доступа только на этапе компиляции. В runtime не будет найдена и прочитана.

@Retention - срок жизни

SOURCE - только в исходном коде, при компиляции удаляется;
CLASS - хранится в скомпилированном *.class, в runtime недоступна;
RUNTIME - всегда. По умолчанию

@Inherited - Наследование аннотаций
@Repeatable - аннотация для повторяемой аннотации
@Deprecated - указывает на устаревание кода
@SuppressWarnings - отключение жалоб компилятора
@SafeVarargs - снимает жалобы на использование varargs

Collections

  • List - списки. ArrayList, LinkedList, Vector, Hashtable
  • Set - уникальные списки. HashSet
  • Queue - fifo очереди. ArrayBlockingQueue - ограничение по количеству, ждет места, LinkedBlockedQueue - связанные. Есть 2 ожидающих метода: put и take.

fail-fast кидает ConcurrentModificationException. Можно обходить через Iterator и модифицировать через (но не многопоток). ListIterator ходит в обе стороны
fail-safe конкурентная, CopyOnWriteArrayList, CopyOnWriteArraySet, ConcurrentHashMap


List через фабрики: Arrays.asList(varargs) - неизменная длинна, только set(i,T); List.of(varargs) - связана с исходным, immutable; List.ofCopy(Col) - копия исходного, immutable.

Set через фабрик: Set.of - связана с исходным, imutable; Set.copyOf(Col) - копия исходного, immutable


list.toArray() лучше использовать T[0]


Map методы

forEach листает все
merge - объединение по ключу
putIfAbsent - не вставляет, если K есть
computeIfAbsent - вычисляет и вставляет если нет K
computeIfPresent - вычисляет и вставляет если есть K
compute - вычисляет всегда по K, T-страрое, T-новое

ConcurrentHashMap - конкурентный и может хранить огромное количество

reduce, reduceKeys, reduceValues - сведение всей Map в одно значение
mappingCount - для из подсчета
search - выполняет поиск по Predicate

IdentityHashMap - проверяет ключи в bucket по ==, а не по equals

NavigableMap - реализует TreeMap. помимо сортировки по ключам, может:

lower/floor/higher/ceilingKey - меньший/больший ключ
lower/floor/higher/ceilingValue - меньшее/большее значение
tail/headMap - вытаскивает кусочек Map в одну из сторон
subMap - вытаскивает кусочек в груницах

LinkedHashMap - элементы связаны друг с другом, можно реализовать простой LRU кэш

Lambda

Паттерны Функционального Программирования

Pure Function - только действие - не меняет объект; нет статусов; не использует внешние объекты, только аргументы; вызывает только Pure Function)
Immutable для входного аргумента, изменение через новый экземпляр;
High-order Function
Recursion - допускает рекурсию
Closure - имеет доступ к созданным ей объектам; сама функиця передается по ссылке для использования
Laziness - выполняется только  при вызове;
Currying and Partial Application - конвейер функций
Filter-Map-Reduce

Вечные стримы

Stream.generate(Math::random);
Stream.iterate(1, i -> i+1);

Фабрики

Stream.empty();
Stream.of("a", "b", "c");
Stream.generate(Supplier<T>);
Stream.iterate(int, UnaryOperation<Integer>);
Stream.iterate(int, Predicate, UnaryOperation<Integer>);

reduce - в один объект

Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9")
  .reduce(5, (current, next) -> current + Integer.parseInt(next), Integer::sum);

flatMap - сливает несколько Stream в 1. collect собирает в итоговую коллекцию

Stream.iterate(0, i -> i<10, i -> i+1)
    .collect(Collectors.groupingBy(i -> i%2, TreeMap::new, Collectors.toList()));

, где 
    i%2     будет ключом;
    TreeMap::new     исплементация для итоговой Map;
    Collectors.toList()     функция для сборки

Stream -> Map

Collectors.partitioningBy - разделяет по true/false
Collectors.groupingBy ключ управляемый
myStream.toMap - аналог Collectors.groupingBy, но попороще.

Stream.of("1", "2", "3", "2", "1", "4")
    .collect(key -> key, val -> val, (val1, val2) -> val1.concat(val2));

Stream -> Pair<String, List>

var stream = Stream.of("1", "2", "3", "4", "5", "6");
Pair<String, List<String>> x = stream.collect(
      Collectors.teeing(Collectors.joining(","), 
                        Collectors.toList(), 
                        Pair::of)
);

Function конвейер

Function<String, Integer> func = /*0*/;
func = func
    .applyThen(/*1*/)
    .applyThen(/*2*/)
    .compose(/*1*/)
    .applyThen(/*3*/)
    .compose(/*2*/)
    .applyThen(/*4*/);

Predicate

сheck = check
    .negate()
    .and(str -> str.startsWith("Hello"))
    .or(str -> str.endsWith("Bye"));

Исключения

try-catch-finally требует минимум 2 блока; try-with-resources достаточно 1, если Closeable не кидает исключение.

Блок finally всегда имеет приоритет (может выкинуть исключени или обновить result).

try-with-resources создает effective-finally переменные, которые закрываются между точкой ошибки и catch. Последовательность:

try
закрыть ресурсы
catch
finally

Закрывать можно переменные, созданные вне try:

FileInputStream fis = ...;      //  <<<< закрывается
final var closeableRef = ...;   //  <<<< закрывается
try(fis; FileReader reader = ...; var closeableDocument = ...; closeableRef;) {
}

Основные checked исключения:

IOException;
FileNotFoundException;
DataFormatException;
InterruptedException;
ParseException;
SQLException;

Основные Error:

AssertionError;
ExceptionInInitializerError;
StackOverflowError;
NoClassDefFoundError;

Последовательность

try(TestCloseable tc1 = new TestCloseable("tc1"); 
    var tc2 = new TestCloseable("tc2")) {
    out.println("try block");
} finally {
    out.println("finally block");
}

будет такой:

init tc1 -> init tc2 -> try block -> close tc2 -> close tc1 -> finally block

Concurrency

Запуск через:

(new Thread(() -> System.out.println("thread started")).start());
(new Thread(() -> {
    @Override
    public void run() {
        System.out.println("thread started");
    }
}).start());

Проверка ожидания для Thread: holdsLock(Object)


ExecutorService

ExecutService svc = null;
Runnable task1 = () -> out.println("task 1");
Runnable task2 = () -> for(...) out.println("task 2");
try {
    svc = Executor.newSingleThreadExecutor();
    svc.execute(task1);
    svc.execute(task2);
    svc.execute(task1);
} finally {
    if(svc != null) scv.shutdown();
}

Метод shutdown не завершает все задачи, но не дает пулу принимать новые; после этого пул будет кидать RejectedExecutionException. Проверить закрытость - isShutdown; проверка завершения всех задач - isTerminated; не дает запускать задачи в очереди и вытается завершить активные (но гарантии не дает) метод shutdownNow, при этом возвращает список Runnable, которые даже не были начаты.


Известные пулы:

newCachedThreadPool - для ровной работы, количество плавает
newFixedThreadPool - для длинных работы или рваной работы
newScheduledThreadPool - по расписанию

Способы запуска Task

void execute(Runnable) - “вызвал и забыл”;
Future<?> submit(Runnable) - можно дождаться ответа.
List<Future<T>> invokeAll(Collection<? extends Callable<T>>) - аналог submit, только для списка задач; порядок сохраняется
T invokeAny(Collection<? extends Callable<T>>) - после первого, у которого есть результат останавливает пул. Держит текущий поток
List<Future<T>> invokeAny(Collection<? extends Callable<T>>) - ожидает завершения всех. Держит текущий поток

Запуск потоков по расписанию - ScheduledExecutorService svc = Executors.newSingleThreadScheduledExecutor();

Методы:

ScheduledFuture<V> schedule(Callable<V>, long, TimeUnit)- разовый запуск через указанный интервал;
ScheduledFuture<?> scheduleAtFixedRate(Runnable, long*, long**, TimeUnit) - запуск с повторением (+ первичный будет отложен)
ScheduledFuture<?> scheduleWithFixedDelay(Runnable, long*, long**, TimeUnit) - аналогичен, но всегда дожидается завершения предыдущего запуска

Future

isDone() проверяет
boolean isCanceled() - была ли отменена;
boolean cancel() - попытка отменить задачу;
<T> T get() - получение результата. Бесконечное ожидание;
<T> T get(long, TimeUnit) - получение результата с ожиданием

CompletableFuture.supplyAsync - подготавливает CompletableFuture. Может принимать Executor - пул потоков, в котором будет выполняться CF:

data.stream()
    .map(obj -> CompletableFuture.supplyAsync(...))
    .map(CompletableFuture::join)
    .collect(Collectors.toList());

Позволяет строить цепочки выполнения через thenApply, thenCompose, thenApplyAsync, thenComposeAsync:

ExecutorService ex1 = Executors.newSingleThreadExecutor();
ExecutorService ex2 = Executors.newSingleThreadExecutor();
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(() -> {
    log("init", 0, 0);
    return 0;
}, ex1);                                                   // pool-1-thread-1
cf = cf.thenApply(o -> apply(o, 1));                       // pool-1-thread-1
cf = cf.thenCompose(o -> compose(o, 2));                   // pool-1-thread-1
cf = cf.thenApplyAsync(o -> apply(o, 3));                  // ForkJoinPool.commonPool-worker-1
cf = cf.thenComposeAsync(o -> compose(o, 4));              // ForkJoinPool.commonPool-worker-1
cf = cf.thenApplyAsync(o -> apply(o, 5), ex2);             // pool-2-thread-1 => ex2
cf = cf.thenComposeAsync(o -> compose(o, 6), ex2);         // pool-2-thread-1 => ex2
get(cf, ex1);
ex1.shutdown();
ex2.shutdown();

Результат 2х CompletableFuture можно слить в 1:

CompletableFuture<?> firstCF = CompletableFuture.supplyAsync(...);
Future<Object> result = CompletableFuture
    .supplyAsync()
    .thenCombine(firstCF, (result1, result2) -> result1.compareTo(result2));

Либо можно подождать все через статический метод:

CompletableFuture.allOf(futuresArray).join();

Либо дождаться первого и выйти:

CompletableFuture.anyOf(futuresArray).getNow(alternativeValue);

Метод CompletableFuture.supplyAsync(…).thenApply(…).thenCombineAsync().onTimeout(…).thenApply(…).onTimeout(…) ограничивает ожидаение в цепочке по времени и кидает TimeoutException. Но лучше использовать completeOnTimeout - он не кидает исключение, а дает право подставлять альтернативаное значение.


Atomic

AtomicBoolean/Integer/Long/IntegerArray/LongArray - используют принцип CompareAndSwap
AtomicReference - объект
AtomicMarkableReference - объект + boolean маркер
AtomicStampedReference - аналог markable, но хранит int
AtomicReferenceArray - атомарный список объектов

Synchronized

При наложении на метод:

private synchronized void syncMethod() { ... }
=
private void syncMethod() {
  synchronized(this) { ... }
}

при static накладывает синхронизацию на объект класса в ClassLoader:

private static synchronized void staticSync() { ... }
=
class MyClass {
    private void syncMethod() {
        synchronized(MyClass.class) { ... }
    }
}

ограничения synchronized:

не на примитивы;
можно на массив, но элементы будут без sync;
sync наложенный на getClass() опасна из-за extends;

Методы управления wait, notify и notifyAll доступны только из synchronized.

volatile использует shared memory, т.е.:

int y = 1;
volatile int x = 1;
synchronized(...) {
    y = 2;   // shared memory:     y = 1
    x = 2;   // shared memory:               x = 2
    y = 3;   // shared memory:     y = 1
    x = 3;   // shared memory:               x = 3
}            // shared memory:     y = 3,    x = 3

это гарантирует happens-before, а также гарантирует порядок обновлений значений в памяти (а не в локальном кэше)


ReentrantLock наиболее частый замок. Помимо lock и unlock:

boolean tryLock(long, TimeUnit) - пытается наложить замок

ReentrantReadWriteLock имеет 2 замка - read и write


Semaphore - работает как светофор с ограничением на проходы каким-то количеством. Пока работает задача, она вычитает 1 доступное место для работы; по завершении добавляет. Если количество свободных мест =0, то семафор не пропускает новые задачи к запуску.

Semaphore(int permits)

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

CyclicBarrier(int parties, Runnable barrierAction))

CountDownLatch - ожидает пока не завешится определенное количество действий. Скинуть (и переиспользовать) можно только при достижении 0.

CountDownLatch(int count)

Exchanger - синхронизирует обмен между потоками

Exchanger<V>

Phaser

Аналогичен CyclicBarrier, тоже ожидает N снятий, но может работать по фазам.

Phaser(int parties)

Синхронизированные коллекции ConcurrentSkipList/Set/Map - аналог TreeSet, но безопасный

CopyOnWriteArrayList/Set - аналог ArrayList, но безопасный. Каждый раз при изменении создает копию массива.

LinkedBlockingQueue - безопасная очередь с методами: boolean offer(E, long, TimeUnit) - пытается вставить за время; E poll(long, TimeUnit) - пытается вытащить за время


Deadlock - 2 потока находятся в ожидании друг-друга
Starvation - ожидает как-то ресурс и запрещает другим работать
Livelock - вроде бы все работает, но бизнес-функции не выполняются; самое опасное

JDBC

Получение коннекта:

Connection conn = DriverManager.getConnection("jdbc:...", "myDbUser", "myDbPass");

PreparedStatement

Производительности. Готовит и кэширует план на уровне БД;
Безопасность. Защищает от SQL-инъекций;
Читабельность. Запрос отдельно; параметры отдельно;
Отложенное использование

является AutoCloseable:

try(PreparedStatement pstmt = conn.prepareStatement("...")) { ... }

методы:

executeUpdate - только для INSERT, UPDATE, DELETE, возвращает int
executeQuery - для SELECT, возвращает ResultSet
execute - принимает все, возвращает boolean если есть ResultSet. можно достать через getResultSet()

Выполнение batch:

var sql = "...";
try(var pstmt = conn.preparedStatement(sql)) {
    pstmt.setInt(1);
    for(int x=0; i<10; i++) {
        pstmt.setDouble(Double.valueOf(x));
        pstmt.addBatch();
    }
    int[] updates = pstmt.executeBatch();         //  количество = addBatch
}

CallableStatement выполняет хранимую процедуру:

try(CallableStatement cstmt = conn.prepareCall("{call my_proc_with_args(my_arg=>?)}")) {
    cstmt.setString("my_arg", "value");
    try(ResultSet rs = cstmt.executeQuery()) {
    }
}

для получения ответа с результатом + аргументами:

var sql = "{call procedure_name(?)}";
try(var stmt = connection.prepareCall()) {
    stmt.setInt(1, 10);
    stmt.registerOutParameter(1, Types.INTEGER);
    if(pstmt.execute()) {
        int result = stmt.getInt("result_var_name");
    }
}

ResultSet тоже AutoCloseable. При создании PreparedStatement можно указать как будет использоваться RS:

connection.prepareStatement(sql, ResultSet.${param}, ResultSet.${param});

с параметрами:

TYPE_FORWARD_ONLY - чтение только вперед;
TYPE_SCROLL_INSENSITIVE - игнорировать параллельные изменения, данные актуальные на запрос;
TYPE_SCROLL_SENSITIVE - обратное;
CONCUR_READ_ONLY - изменения в ResultSet не будут применимы;
CONCUR_UPDATABLE - изменения в ResultSet допустимы.

Spring


Boot требует @SpringBootConfiguration. Для поиска по кастомным пакетам: scanBasePackages, по ум. ищет от текущего. Запустить можно:

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

Core

@ComponentScan - определяет путь для поиска
@Component создает бин. Либо в @Configuration бине создать через @Bean метод
@Autowired инъекция через метод
@Inject и @Resource для использования бина
@Required - объявляет инъекцию обязательной
@Lazy - помечает инъекцию как ленивую, обязательно @Autowired(required=false). Надо проверять на notnull либо объявить Optional
@DependsOn - выстраивает очередность. Но если есть цикличные ссылки - это ошибка


@Bean
public MyRootBean rootBean() { ... }

@Bean(name = {"beanOne", "secondBean", "thirdAlternativeName"})
public MyChildBean childBean(MyRootBean root) { ... }

@Bean
public static MyChildBean childBean(MyRootBean root) { ... }    //  до @Configuration

Поиск кандидатов для инъекции

по типу
по Qualifier
по имени параметра и имени бина
throw new NoUniqueBeanDefinitionException в случае, если кандидатов несколько
throw new NoSuchBeanDefinitionException в случае, если кандидата найти не удалось

Можно добавлять инъекции с Generics:

@Component
class JdbcUserRepo extends JdbAbstractRepo<User> implements UserRepo {...}

@Component
class JdbcOrderRepo extends JdbAbstractRepo<Order> implements OrderRepo {...}

@Component
class SomeElement {
    @Autowired
    JdbcAbstractRepo<User> userRepo;
    @Autowired
    JdbcAbstractRepo<Order> orderRepo;
}

Жизн. цикл Context:

  1. Создается контейнер
  2. Контейнер считывает конфигурационные данные
  3. Создается описание бинов (BeanDafinition) BeanDefenitionReader
  4. BeanFactoryPostProcessors обрабатывают описание бина
  5. Контейнер создает бины используя их описание
  6. Бины инициализируются — значения свойств и зависимости
  7. BeanPostProcessor запускают методы обратного вызова (callback methods)
  8. Приложение запущено и работает
  9. Инициализируется закрытие приложения
  10. Контейнер закрывается
  11. Вызываются callback methods

Жизнь бина

  1. Создание ApplicationContext
  2. Загрузка/чтение описания бинов
  3. Обработка описания бинов для их последующей инициализации
    a) @PostConstruct или @Bean(initMethod=”…”)
  4. Инициализация зависимых бинов
  5. Инъекция зависимостей
  6. Окончание инициализации бинов
  7. Использование согласно логики приложения
  8. Остановка ApplicationContext, разрушение бинов.
    a)@PreDestroy или @Bean(destroyMethod = “…”)

Scope для бинов выдается через @Scope(“${type}”)

  • Singleton
  • Prototype - на каждое обращение к нему
  • Request - на каждый HTTP запрос
  • Session
  • Application
  • Socket
  • Thread

@Bean
Properties myCustomProperties() {
    Properties props = new Properties();
    props.setProperty("p1", "value1");
    props.setProperty("property2", "second value");
}

@Bean
MyBean myBean(@Value("#{myCustomProperties.p1}") String p1, @Value("#{myCustomProperties.property2}") String p2) {
    ...
}

Бин окружения

@Autowired Environment

WEB

@Controller
@RequestMapping
@Get/Post/Put/../DeleteMapping
@PathVariable - значение из контекстного пути
@RequestParam - get параметр
@RequestHeader
@CookieValue
@SessionAttribute
@RequestAttribute
@ModelAttribute
@ResponseStatus

Взаимодействие осуществляется через:

WebRequest - инфо по запросу
ServletResponse - собственная реализация ответа
HttpSession - сессия
PushBuilder - Push сообщения, если клиент поддерживает HTTP/2
Principal - инфо из spring sec
Reader/InputStream и Writer/OutputStream - читают и записывают тело запроса и ответа
MatrixVariable - матрица из параметров

REST

RESTful считается stateless, т.е. соединение разрывается после обмена.
Для CRUD (Create/Read/Update/Delete) потребуется подключить MVC библиотеки Spring

Для контроллеров потребуется:

@Controller попытается найти View с соответствующим название, если будет возвращена строка. Если его нет, то будет выдана HTTP ошибка. Чтобы избежать:

  • добавить @ResponseBody методу
  • добавить @ResponseBody методу
  • объявить не @Controller, а @RestController

По умолчанию, Spring использует:

StringHttpMessageConverter    -    text/plain  
MappingJackson2HttpMessageConverter    -    application/+json
MappingJackson2XmlMessageConverter    -    application/+xml
AtomFeedHttpMessageConverter    -    application/atom+xml
RssChannelHttpMessageConverter    -    application/rss+xml

Маппинг для методов:

@GetMapping(value="...", produces="application/json;charset=UTF-8", consumes="application/xml")
@RequestMapping(metod="POST")
@PutMapping
@DeleteMapping

Стандартные HTTP коды:

200 / OK
201 / CREATED
204 / NO_CONTENT - успешноре выполнение изменения
404 / NOT_FOUND
403 / FORBIDDEN
405 / METHOD_NOT_ALLOWED
409 / CONFLICT - например, объект запрещен к изменению
415 / UNSUPPORTED_MEDIA_TYPE

Можно определить через @ResponseStatus


Валидация может быть ограничена по группам проверок:

public class MyObject {
    interface ShortName{};
    interface LongName{};
    interface Adult{};
    interface Tall{};
    
    @Size(min = 5, max=20, groups = {ShortName.class})
    @Size(min = 10, max=100, groups = {LongName.class})
    public String name;

    @Min(value = 18, groups = Adult.class)
    public Integer age;

    @NotEmpty
    public String phone;

    @Min(value = 200, groups = Tall.class)
    public int height;
}


@PostMapping
public void addMyObject(
  @Validated({
    Contract.ShortName.class,           //  группы для проверок
    Contract.Adult.class}) 
  MyObject obj) {...}

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

Результаты проверок можно обработать:

public void create(@Validated @RequestBody MyObjectDto obj, 
                   BindingResult result) {                          //  результаты
    if(result.hasErrors()) {
        throw new IllegalArgumentException(“invalid data: ” + result);
    }
    ...
}

Перехват исключений для конкретного контроллера:

@RestController
public class MyObjectController {
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<MyJsonError> onValidationException(Exception e) {
    }
}

И глобальный:

@ControllerAdvice(basePackageClasses={MyObjectController.class})
public class ObjectOnlyControllersExceptionsHandler {...}

По умолчанию, Spring выдает даты в ETC стандарте, но можно поменять:

objectMapper.registerModule(new JavaTimeModule());

Тестирование MVC:

@ExtendWith(MockitoExtension.class)
class UserControllerTest {
    @Mock
    UserService svc;

    @InjectMocks
    UserController controller;

    @Test
    public void listAll() {
        when(svc.findAll()).thenReturn(...);
        controller.listAll();
        //  asserts
    }
}

JUnit4 vs JUnit5

@Before -> @BeforeEach
@After -> @AfterEach
@BeforeClass -> @BeforeAll
@AfterClass -> @AfterAll
@Test(expected=Exception.class) -> @Test { assertThrows(..., Exception.class) }
@Ignore -> @Disabled

JPA

Вытащить 1 запись можно через queryFor*:

Long count = jdbcTemplate.queryForObject("select count(*) from users", Long.class);

Либо в Map:

List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from users");


JdbcTemplate jdbcTemplate = ...
jdbcTemplate.queryForObject("select * from users where name=? and password=?", rowMapper, userName, userPass);

NamedParameterJdbcTemplate jdbcTemplate = ...
jdbcTemplate.queryForObject("select * from users where name=:username and password=:userpass", Map.of("username", name, "userpass", pass), rowMapper);

TransactionalTemplate

Позволяет управлять транзакцией в ручном режиме:

private final TransactionalTemplate tx;

public MyObject findAny() {
    return tx.execute(txStatus -> {
        try {
            return repo.find(...);
        } catch(Exception e) {
            txStatus.setRollbackOnly();
            return null;
        }
    });
}

Менеджер транзакций

Разные имплементации по-разному управляют. HibernateTransactionManager, JpaTransactionManager и их менеджеры: JpaTransactionManager, HibernateTranscationManager, JtaTransactionManager (для распределенных, типа Atomikos).

Определить менеджера:

1 Бин

@Bean
public TransactionManager txManager(DataSource datasource) {
    return new DataSourceTransactionManager(datasource);
}

2 Актив транзакции

@Configuration
@EnableTransactionManager
public class MyConfig {...}

3 Добавить на метод

@Transactional
public MyObject find() { ... }

@Transactional

Параметры:

@Transactional(transactionManager="mySecondTxMgr")

еще параметры:

propagation (REQUIRED, REQUIRES_NEW, NESTED - создается save point, MANDATORY - ошибка если нет tx, NEVER, NOT_SUPPORTED, SUPPORTS)
isolation
    DEFAULT
    READ_UNCOMMITED - доупны изменения в read-only
    READ_COMMITED - доступно после commit
    REPEATABLE_READ - на момент первого чтения
    SERIALIZABLE - провоцирует ошибки параллельного доступа. фиксится повторными запросами
timeout
rollbackFor - откат при классе ошибки
noRollbackFor

Hibernate

@Repository
public class MyRepo {
    @Autowired
    sessionFactory sessionFactory;

    public List<User> findAll() {
        return sessionFactory.getCurrentSession().createQuery(“FROM User”).list();
    }
}

Named Query

@Entity
@NamedQueries({@NamedQuery(name="req1", query="select ..."), @NamedQuery(name="second_req", query="select ...")})
public class User {...}

@Repository
public class MyUserRepo() {
    public List<User> findAll() {
        return entityManager.craeteNamedQuery("second_req").setParameter(1, "").list();
    }
}

JPA

Подключение JPA

@Configuration
@EnableJpaRepositories(basePackages={"...", "..."})
@EnableTransactionManager
public class JdbcConfig {...}

Добавление repo:

@RepositoryDefinition(domainClass=User.class, idClass=Long.class)
public interface UserRepo {...}

или

@Repository
public interface UserRepo extends JpaRepository<User, Long> {
    @Query("select u from User where u.username like ?1")
    Optional<User> findByUsername(String username);

    @Query("select u from User where u.username=:name and u.password=:pswd")
    Optional<User> findByUsername(@Param("name") String username, @Param("pswd") String pass);
}

Паттерны

SOLID

S - Single Responsibility
O - Open-Close - только расширение
L - Liskov Substitution - максимальная абстракция
I - Interface Segregfation - узкая специализация
D - Dependency Invertion - зависимости от абстракций

DRY - Don’t Repeat Yourself

KISS - Keep It Simple, Stupid

TDD - Test-Driven Development

ACID - паттерн БД

A - Atomicity - все или ничего
C - Consistency - консистентность
I - Isolation
D - Durability - гарантированность

12 Factor app

Codebase. в git только код
Depencencies. в git нет библиотек
Configurations. параметры вне и настраиваемы
Backing Services. никаких  зависимостей от внешней среды
Build, Release, Run. можно поднять с 0
Process. 1 сервис - 1 компонент в коде
Port Binding. на каждый сервис уникальный порт
Concurrency. простая масштабируемость
Disposability. сервис гарантирует свою работу и кричит о проблеме
Dev/Prod Parity. разбитие на контура
Logs. дефрагментация логов
Admin Process. бизнес задача превыше всего

Классические

Основные

Delegation - передает выпонение на сторону;
Functional Design
Interface - абстракция
Marker Interface
Immutable Interface - маркировка immutable
Event Channel - общение подпиской

Проектирование

Adapter - сквозное
Bridge - промежуточное, своя абстракция
Composite - объединяет несколько компонентов
Decorator - база компонента + расширение
Facade - внешний ресурс
Proxy

Порождающие

Abstract Factory
Factory Method
Builder
Prototype
Singleton

Поведенческие

Chain of Responsibility - участник цепочки
Command - оборачивает действие в команду
Interpreter - интерпретатор данных
Iterator
Mediator - пропускает все через себя
Memento - снимок данных
Observer - наблюдает за событиями по подписке
State - статусы
Strategy - несколько алгоритмов в 1 месте
Template Method
Visitor - Proxy, который не меняет поведение

АНТИПАТТЕРНЫ

God Object - объединение всего в 1 месте
Big Ball Of Mud - никто ничего не понял;
Introduced Complexity - излишнее усложнение
Magic Pushbutton
Re-Coupling - внедрение ненужной зависимости;

Agile

Принципы:

Уудовлетворение заказчика - главное
Изменения в требованиях приветствуются
Макс частые обновления
Создают мотивированные люди
Передача информации – лично
Основной показатель прогресса - Продукт
Принцип постоянного улучшения
  1. Product owner -
  2. User Story - (описание фичи) выдает аналитик через заказчика
  3. Epic - выделяются на группу подзадач
  4. Story Point - определяет разработчик
  5. Scrum Master - менеджер проекта
  6. Scrum Board - ведет менеджер (TODO, INP, DONE)
  7. Team из 7 бэк-разрабов, 4 теста, 3 аналитика, 1 менеджер
  8. Sprint - 2 нед (вт - вт)
  9. Sprint Review - за 2 дня до окончанием, т.е. в пт
  10. Sprint Retrospective частичная, обзор производительности
  11. Backlog состоит из 20-30 задач, 2.5 задачи на разработчика
  12. Daily 15 мин каждый день. Дополнительный 30 мин 1/2 дня
  13. Code Review выполнялся после Pull Request мной
  14. Defenition Of Done: code review; соответствие требованиям написания кода; approve от тестировщика; approve от заказчика
  15. Velocity (объем кода) нету, но есть test coverage (JaCoCo 70%)
  16. Ведение GIT по GitFlow
  17. Выделялись Spike, в частности, для Spring Kafka Batch
 Comments
Comment plugin failed to load
Loading comment plugin