Секреты Java
رفتن به کانال در Telegram
Обсуждаем Java, архитектуру, фреймворки и всё, о чем должны говорить разработчики. Владелец: @Alexey_Morl Реклама на бирже: https://telega.in/c/java_secrets
نمایش بیشتر7 991
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
اطلاعاتی وجود ندارد30 روز
آرشیو پست ها
7 991
Варианты использования аннотации
@OneToOne
Аннотация JPA @OneToOne необходима нам для установления связи один-к-одному для записей из двух таблиц.
Пример: у нас есть человек-гражданин (пусть это будет Person) и у него есть один действующий паспорт (Passport).
Существует несколько вариантов использования этой аннотации, о которых следует знать.
Рассмотрим на примере Person и Passport:
▫️Внешний ключ
В первом варианте таблица Person имеет внешний ключ passport_id:
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "passport_id", referencedColumnName = "id")
private Passport passport;
}
@Entity
@Table(name = "passports")
public class Passport {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String number;
@OneToOne(mappedBy = "passport")
private Person person;
}
Параметр name в аннотации @JoinColumn служит для указания внешнего ключа в таблице persons, а referencedColumnName - для определения первичного ключа таблицы passports, на который будет ссылаться внешний ключ passport_id.
▫️Общий первичный ключ
В данном варианте у нас нет дополнительного столбца для связи таблиц. Эту роль взял на себе первичный ключ person_id из таблицы passports, который так же является и внешним ключом.
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
private Passport passport;
}
@Entity
@Table(name = "passports")
public class Passport {
@Id
@Column(name = "person_id")
private Long id;
private String number;
@OneToOne
@MapsId
@JoinColumn(name = "person_id")
private Person person;
}
Аннотация @PrimaryKeyJoinColumn указывает на то, что первичный ключ из таблицы persons будет использоваться в качестве внешнего ключа в passports. А аннотация @MapsId говорит о том, что обе таблицы используют один первичный ключ и он будет скопирован из "родительской" таблицы persons в в таблицу passports.7 991
Как управлять выделенной памятью для нашего приложения?
Может случится, что в процессе развития наше приложение выросло на столько, что начинает долго соображать или вовсе падать с
OutOfMemoryError. Если дело не в memory leak, то пора выделить приложению больше памяти.
Параметры -Xms и -Xmx в Java используются для управления выделением и использованием памяти при выполнении приложений.
Пример:
java -Xms512m -Xmx2g MyApp
▫️Тут мы запускаем приложение MyApp стандартной командой (при запуске из Idea идем в "Run" -> "Edit Configurations..." и у выбранного приложения прописываем параметры в "VM options")
▫️Параметр -Xms задает начальный размер выделенной памяти для JVM. В примере выше JVM начинает работу, выделяя 512 мегабайт. Установка оптимального значения -Xms может помочь ускорить запуск JVM и улучшить производительность приложения.
▫️Параметр -Xmx указывает максимальный размер памяти, который JVM может использовать. На примере - 2ГБ. Установка правильного значения -Xmx поможет нам предотвратить переполнение памяти.
▫️Виды значений, которые можно установить для параметров -Xms и -Xmx, указываются в килобайтах (k), мегабайтах (m) или гигабайтах (g).
▫️Без указания параметров, дефолтные значения рассчитываются JVM в зависимости от операционной системы и характеристик компьютера.
Обычно имеет смысл устанавливать значение -Xms равным -Xmx, это позволит приложению запускаться быстрее, не тратить время на выделение дополнительной памяти и не конкурировать с другими приложениями за свободную память компьютера.7 991
Интерфейс (interface)
Интерфейс (
interface) — это специальный тип, который содержит только абстрактные методы (до Java 8) и константы. Интерфейсы служат для задания контракта, который классы обязаны реализовать, если они его используют. В интерфейсе нельзя создать реализацию методов (до Java 8), и все методы по умолчанию являются public и abstract.
С версии Java 8 в интерфейсах можно использовать методы с реализацией (default и static), а с версии Java 9 — private методы. Интерфейсы позволяют реализовать множественное наследование, поскольку класс может реализовать несколько интерфейсов.7 991
new vs valueOf
Старайтесь всегда использовать метод valueOf вместо конструктора в стандартных классах-оболочках примитивных типов, кроме тех случаев, когда вам нужно конкретно выделить память под новое значение.
Все оболочки, кроме чисел с плавающей точкой, от Byte до Long, имеют кэш. По умолчанию этот кэш содержит значения от -128 до 127. Следовательно, если ваше значение попадает в этот диапазон, то значение вернется из кэша. Значение из кэша достается в 3.5 раза быстрее, чем при использовании конструктора, плюс идёт экономия памяти.
Помимо этого, наиболее часто используемые значения могут также быть закэшированы компилятором и виртуальной машиной.
7 991
Паттерн Builder или Строитель
Этот паттерн является порождающим и используется для создания объектов с большим количеством необязательных полей.
Паттерн Строитель инкапсулирует конструирование продукта и позволяет разделить его на этапы"Паттерны проектирования" Э. Фриман Давайте на примере: У нас есть класс
Person с обязательным полем name и необязательными полями age и address.
public class Person {
private final String name; // Обязательное
private int age; // Необязательное
private String address; // Необязательное
private Person(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.address = builder.address;
}
// Геттеры
public static class Builder {
private final String name;
private int age;
private String address;
public Builder(String name) {
this.name = name;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Person build() {
return new Person(this);
}
}
}
Вложенный класс Builder используется для "поэтапного и инкапсулированного" конструирования объекта Person. Мы можем установить необходимые поля с помощью методов age() и address(), а затем создать объект Person с помощью метода build():
var person = new Person.Builder("John")
.age(30)
.address("My Street")
.build();
Без паттерна Builder нам бы пришлось делать 4 конструктора для двух необязательных полей, чтобы покрыть возможные варианты создания объекта!
Таким образом, шаблон Builder обеспечивает удобный способ создания объектов с различными составом полей, делая код более читаемым и гибким, но будет плохим решением если все поля обязательные.
Кстати, библиотека lombok предлагает очень удобную аннотацию @Builder.7 991
CompletableFuture
CompletableFuture — это класс в Java, введенный в Java 8, который представляет собой асинхронную задачу, которая будет выполнена в будущем и возвращает результат. CompletableFuture предоставляет множество методов для работы с асинхронными задачами, комбинирования их и управления их выполнением.
Основные возможности CompletableFuture включают:
— Вы можете запустить задачу асинхронно и продолжить выполнение кода без блокировки.
— CompletableFuture позволяет комбинировать результаты нескольких задач, выполняя определенные действия при завершении каждой задачи.
— Вы можете указать обработчики ошибок для обработки исключений, возникающих во время выполнения задачи.
— Вы можете блокировать выполнение кода и ждать завершения задачи.
7 991
Имя класса
В Java у Class существует несколько методов, которые возвращают имя класса, но в разных формах и служат разным целям - это
getSimpleName(), getCanonicalName(), getTypeName() и getName().
Часто они вызывают путаницу и в этом посте мы попытаемся с ними разобраться.
Допустим у нас есть класс:
package com.example;
public class Outer {
public class Inner {
}
}
Вызов метода:
▫️getSimpleName() - возвращает просто название класса без учета пакета и вложенных классов.
Outer.class.getSimpleName();
Inner.class.getSimpleName();
Код в строках выше вернет 'Outer' и 'Inner' соответственно. Метод может быть полезен, например, для логирования, но возвращаемое значение не гарантирует уникальность.
▫️getName() - возвращает полное имя класса, включая пакет, а вложенные классы отделяются символом "$". В рамках определенного ClassLoader этот метод возвращает уникальные имена и эти имена мы можем использовать для динамической загрузки классов через Class.forName().
Outer.class.getName();
Inner.class.getName();
Вернут 'com.example.Outer' и 'com.example.Outer$Inner'.
▫️getCanonicalName() - возвращает каноническое имя, как определено в JLS. Включает пакет и вложенные класса, отделенные точкой. Каноническое имя используется оператором import.
Outer.class.getCanonicalName();
Inner.class.getCanonicalName();
Вернут 'com.example.Outer' и 'com.example.Outer.Inner'.
▫️getTypeName() - возвращает "an informative string for the name of this type" - информативную строку для имени этого типа. Это имя только информативно и не имеет никакой контрактной значимости.
Outer.class.getTypeName();
Inner.class.getTypeName();
Вернут то же, что и getName();7 991
Redis как брокер сообщений
Redis не является брокером сообщений, но всё-таки позволяет реализовать простую модель обмена сообщениями. В отличие от RabbitMQ и Kafka, с помощью Redis не получится настроить сложную маршрутизацию. Сообщения будут отправляться в определенные каналы, из которых подписчики будут получать данные.
Для реализации обмена сообщений необходимо настроить подключение к Redis. Как это сделать, было рассказано в предыдущих статьях.
Создадим класс для отправки сообщений в нужный топик(канал):
@Service
@RequiredArgsConstructor
public class Producer {
private final RedisTemplate<String, String> redisTemplate;
private final ChannelTopic channelTopic;
public void produce(String message) {
redisTemplate.convertAndSend(channelTopic.getTopic(), message);
}
}
Данный класс публикует сообщение, используя RedisTemplate. Метод convertAndSend принимает название топика и само сообщение. В этом примере используется класс ChannelTopic из пакета org.springframework.data.redis.listener. Вместо этого можно передавать сроковое значение, которое будет содержать название очереди.
Создадим класс, в котором будет содержаться логика обработки сообщений:
@Service
public class Receiver {
public void handleMessage(String message) {
System.out.println("Received: " + message);
}
}
В классе конфигурации добавим следующие бины:
@Bean
public ChannelTopic channelTopic() {
return new ChannelTopic("channel");
}
ChannelTopic - класс хранящий название топика для сообщений.
@Bean
public MessageListenerAdapter messageListenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver);
}
MessageListenerAdapter - класс, являющийся подписчиком для топика. Этот класс делегирует обработку сообщения пользовательскому классу, который должен реализовывать интерфейс MessageListener либо содержать метод с названием handleMessage. Название метода можно изменить, но тогда при определении MessageListenerAdapter необходимо указать название метода, который будет получать сообщения.
@Bean
public RedisMessageListenerContainer сontainer(RedisConnectionFactory redisConnectionFactory,MessageListenerAdapter messageListenerAdapter, ChannelTopic channelTopic) {
var container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
container.addMessageListener(messageListenerAdapter, channelTopic);
return container;
}
RedisMessageListenerContainer - это класс, который обрабатывает низкоуровневые детали получения и преобразования сообщений. С помощью метода addMessageListener() указывается слушатель сообщений и название топика, из которого будут читаться данные.
Redis может быть хорем выбор в качестве брокера сообщений из-за высокой скорости работы. Но если важна надежность, гарантия доставки сообщений и сложная маршрутизация, то лучше сделать выбор в пользу RabbitMQ или Kafka.7 991
Метод isPowerOfTwo()
Метод isPowerOfTwo() используется для проверки, является ли число степенью двойки.
Этот метод полезен при работе с битовыми операциями и оптимизации алгоритмов.
7 991
StringBuilder
Все мы знаем, что объекты типа String являются immutable (неизменяемыми) и каждое их изменение влечет добавление новой строки в String pool. А, что если в вашей программе строки часто изменяются? Это приведет к расходованию памяти, а этот ресурс не бесконечный. В таком случае лучше использовать
StringBuilder .
StringBuilder - это класс из пакета java.lang, который предоставляет возможность работы с изменяемыми строками. Вместо обычных строк StringBuilder под капотом использует массив, размер(capacity) которого по умолчанию равен 16.
Существует несколько способов объявить переменную типа StringBuilder:
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("Java");
StringBuilder sb3 = new StringBuilder(sb2);
StringBuilder sb4 = new StringBuilder(20);
▫️Использовать пустой конструктор - capacity = 16
▫️Передать в конструктор сроку - capacity = длина строки + 16
▫️Передать в конструктор любой класс, который реализует интерфейс CharSequence - capacity = длина CharSequence + 16
▫️Указать нужный capacity при создании переменной
Модификация значений происходит с помощью встроенных методов:
▫️append("str") - добавление строки в конец
▫️insert(index, "str") - добавление строки в определенную позицию
▫️sb.delete(start, end) - удаление подстроки
Вот пример их использования:
sb.append("Hello");
sb.insert(5, " World");
sb.delete(6, 11);
Важная особенность StringBuilder заключается в том, что его нельзя использовать в многопоточной среде, но для этой цели существует StringBuffer, который предоставляет такой же функционал, но скорость его работы, естественно, ниже.7 991
Классы с поддержкой атомарных операций
Если в вашем коде нужна переменная, к которой будут обращаться разные потоки не только для чтения, но и для записи, можно использовать
volatile + synchronized. Но это влечет за собой потери в производительности.
Для решения этой проблемы в Java есть пакет java.concurrent.atomic, который содержит классы для работы с атомарными операциями.
Атомарная операция - это операция, которая может быть безопасно выполнена в многопоточной среде.
Классы из пакета java.concurrent.atomic используют низкоуровневые операции, такие как CAS (compare-and-swap). Подобные инструкции работают быстрее, чем синхронизация через блокировки потоков.
Вот пример использования класса AtomicInteger:
public class Counter {
private final AtomicInteger counter = new AtomicInteger(0);
int getValue() {
return counter.get();
}
void increment() {
counter.incrementAndGet();
}
}
В данном коде определяется переменная counter типа AtomicInteger, через метод getValue() можно получить текущее значение счетчика, а через increment() - увеличить его значение. В этом примере мы можем быть уверены, что в многопоточной среде этот класс будет иметь предсказуемое поведение!
Класс AtomicInteger содержит множество методов для выполнения атомарных операций:
▫️incrementAndGet() - увеличивает на единицу значение и возвращает значение
▫️getAndIncrement() - возвращает значение и после увеличивает значение на единицу
▫️ decrementAndGet() и getAndDecrement() - работают аналогично increment методам, но уменьшают значение
▫️ set(int value) - присваивает новое значение
А так же множество других методов.
Помимо AtomicInteger, существуют подобные классы для работы с Boolean, Long, массивами и ссылками на объекты.
Использование этих классов позволяет избавить от лишних блоков синхронизации потоков, которые могут вызвать deadlock. Несмотря на высокую производительность этих классов, не стоит их использовать в однопоточных программах, так как накладные расходы на выполнение атомарных операций увеличивают время выполнения кода.7 991
Задачи с собеседований: Сортировка пузырьком (jun)
- Расскажите про сортировку пузырьком и реализуйте её.
Это простой алгоритм сортировки, который использует два вложенных цикла. Внешний цикл отвечает за количество проходов по массиву, а внутренний сравнивает соседние элементы. Если текущий элемент больше следующего, они меняются местами. Так продолжается, пока массив не будет отсортирован.
💡 Ключевые моменты:
▪️ Худший случай: O(n^2)
▪️ Используется строго для небольших наборов данных.
Реализация на картинке 👆
7 991
Java Naming Conventions. Названия констант
Конвенции, изложенные в официальной документации Oracle и Google Java Style Guide помогают поддерживать единый стиль в коде и упрощают его сопровождение.
Начнем с того, что такое константы?
Константами обычно считаются переменные, объявленные как
static final. Они неизменяемы после инициализации и часто используются для хранения значений, которые не должны изменяться на протяжении всего выполнения программы.
Это - начало определения. Его дополнит определение из
Основные правила по соглашениям об именовании:
▫️Oracle :
Константы должны быть написаны в верхнем регистре, а слова разделены символами подчеркивания (_)."Так назваемый
UPPER_SNAKE_CASE.
Пример:
public static final int MAX_WIDTH = 100;
public static final String DEFAULT_LANGUAGE = "EN";
▫️Google дает еще более точное определение:
Но что такое константа, на самом деле? Константы — это статические final-поля, содержимое которых глубоко неизменяемо (deeply immutable), а методы не имеют обнаруживаемых побочных эффектов. Примерами служат примитивы, строки, неизменяемые классы значений и все, что установлено в null. Если какое-либо из наблюдаемых состояний экземпляра может измениться, оно не является константой.И приводит интересные примеры:
java
// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final Map<String, Integer> AGES = ImmutableMap.of("Ed", 35, "Ann", 32);
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final ImmutableMap<String, SomeMutableType> mutableValues =
ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
Т.е. если объект может менять свое состояние - он не является константой и именовать его стоит по конвенциям именования переменных - lowerCamelCase.
Заключение
Конвенции именования констант в Java помогают сделать код более читаемым, структурированным и легким для поддержки. Следуя рекомендациям Oracle и Google, вы улучшаете качество своего кода, делая его понятным для других разработчиков и будущих версий вас самих.7 991
Паттерны проектирования
Шаблон (паттерн) проектирования (design pattern) — это проверенное и готовое к использованию решение. Это не класс и не библиотека, которую можно подключить к проекту, это нечто большее - он не зависит от языка программирования, не является законченным образцом, который может быть прямо преобразован в код и может быть реализован по-разному в разных языках программирования.
ПОРОЖДАЮЩИЕ ПАТТЕРНЫ
✔️ Абстрактная фабрика (Abstract factory) - Класс, который представляет собой интерфейс для создания других классов.
✔️ Строитель (Builder) - Класс, который представляет собой интерфейс для создания сложного объекта.
✔️ Фабричный метод (Factory method) - Делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не специфические классы, а манипулировать абстрактными объектами на более высоком уровне.
✔️ Прототип (Prototype) - Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор.
✔️ Одиночка (Singleton) - Класс, который может иметь только один экземпляр.
СТРУКТУРНЫЕ ПАТТЕРНЫ
✔️ Адаптер (Adapter) - Объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс.
✔️ Мост (Bridge) - Структура, позволяющая изменять интерфейс обращения и интерфейс реализации класса независимо.
✔️ Компоновщик (Composite) - Объект, который объединяет в себе объекты, подобные ему самому.
✔️ Декоратор (Decorator) - Класс, расширяющий функциональность другого класса без использования наследования.
✔️ Фасад (Facade) - Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.
✔️ Приспособленец (Flyweight) - Это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым.
✔️ Заместитель (Proxy) - Объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него.
ПОВЕДЕНЧЕСКИЕ ПАТТЕРНЫ
✔️ Цепочка обязанностей (Chain of responsibility) - Предназначен для организации в системе уровней ответственности.
✔️ Команда (Command) - Представляет действие. Объект команды заключает в себе само действие и его параметры.
✔️ Интерпретатор (Interpreter) - Решает часто встречающуюся, но подверженную изменениям, задачу.
✔️ Итератор (Iterator) - Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из объектов, входящих в состав агрегации.
✔️ Посредник (Mediator) - Обеспечивает взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на друга.
✔️ Хранитель (Memento) - Позволяет, не нарушая инкапсуляцию зафиксировать и сохранить внутренние состояния объекта так, чтобы позднее восстановить его в этих состояниях.
✔️ Наблюдатель (Observer) - Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
✔️ Состояние (State) - Используется в тех случаях, когда во время выполнения программы объект должен менять своё поведение в зависимости от своего состояния.
✔️ Стратегия (Strategy) - Предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
✔️ Шаблонный метод (Template method) - Определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
✔️ Посетитель (Visitor) - Описывает операцию, которая выполняется над объектами других классов. При изменении класса Visitor нет необходимости изменять обслуживаемые классы.
7 991
Захват переменных в лямбда-выражениях
Лямбда-выражения могут захватывать переменные из внешнего окружения, делая их доступными внутри лямбда-функции. При этом переменные могут быть:
1️⃣ Неизменяемыми (effectively final) – Переменная из внешнего контекста, используемая в лямбде, должна быть объявлена как
final или фактически быть неизменяемой (то есть не изменяться после первого присваивания). Например:
int x = 10;
Runnable r = () -> System.out.println(x); // x захвачен в лямбде
2️⃣ Свободными от изменения в лямбде – Лямбда не может изменять захваченные переменные. Это ограничение гарантирует, что нет неоднозначного состояния, когда переменная изменяется из нескольких мест (например, из основного потока и из лямбда-функции одновременно).
3️⃣ Статическими или полями класса – В отличие от локальных переменных, статические поля класса или поля экземпляра могут свободно изменяться внутри лямбда-выражений, поскольку их значения хранятся в куче (heap) и доступны по ссылке.
Пример:
public class Main {
private static int staticVar = 20;
public static void main(String[] args) {
int localVar = 10;
Runnable r = () -> System.out.println(localVar + staticVar);
r.run();
}
}
В этом примере localVar захватывается, так как он effectively final, а staticVar доступен, так как это статическое поле.7 991
Что такое класс Object? Какие в нем есть методы?
Object это базовый класс для всех остальных объектов в Java. Любой класс наследуется от Object и, соответственно, наследуют его методы:
public boolean equals(Object obj) – служит для сравнения объектов по значению;
int hashCode() – возвращает hash код для объекта;
String toString() – возвращает строковое представление объекта;
Class getClass() – возвращает класс объекта во время выполнения;
protected Object clone() – создает и возвращает копию объекта;
void notify() – возобновляет поток, ожидающий монитор;
void notifyAll() – возобновляет все потоки, ожидающие монитор;
void wait() – остановка вызвавшего метод потока до момента пока другой поток не вызовет метод notify() или notifyAll() для этого объекта;
void wait(long timeout) – остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта;
void wait(long timeout, int nanos) – остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта;
protected void finalize() – может вызываться сборщиком мусора в момент удаления объекта при сборке мусора.7 991
Что такое Aspect, Advice, Pointcut и JoinPoint в АОП?
Aspect — класс, который реализует сквозные функции, такие как управление транзакциями.
Advice — методы, которые выполняются, когда в приложении достигается определенный JoinPoint с соответствующим Pointcut.
Pointcut — набор регулярных выражений, которые сопоставляются с JoinPoint, чтобы определить, нужно ли выполнять Advice или нет.
JoinPoint — некая точка во время выполнения программы, например выполнение метода или обработка исключения.
7 991
Методы isLetter() и isDigit()
▶️
Character.isLetter() — метод, который проверяет, является ли символ буквой. Он возвращает значение true, если символ является буквой, и false в противном случае.
▶️Character.isDigit() — метод, который проверяет, является ли символ цифрой. Он возвращает значение true, если символ является цифрой, и false в противном случае.7 991
Spring Profiles
Spring Profiles - это мощный механизм Spring Framework, который позволяет легко настраивать контекст и использовать разные конфигурации для разных сред.
▫️Как определить профили?
Профили определяются с использованием аннотации
@Profile. Каждый профиль может иметь свои собственные настройки и бины:
@Configuration
@Profile("dev")
public class DevConfig {
}
Так же можно повесить эту аннотацию на класс компонента:
@Component
@Profile("dev")
public class DevComponent {
}
или метод создания бина в классе конфигурации:
@Configuration
public class Config {
@Bean
@Profile("dev")
public MyBean myBean() {
return new MyBean();
}
Для xml конфигурации:
<beans profile="dev">
<bean id="myBean" class="org.demo.MyBean" />
</beans>
▫️Как установить активный профиль?
Чтобы установить активный профиль, так же есть несколько различных вариантов:
- Через JVM параметры
-Dspring.profiles.active=dev
- Профили можно активировать через переменные окружения (для Unix/Windows соответственно):
export SPRING_PROFILES_ACTIVE=dev
set SPRING_PROFILES_ACTIVE=dev
- Через файл application.properties (yaml):
spring.profiles.active=dev
- Программно через ConfigurableEnvironment:
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("dev");
▫️Рассмотрим простой пример для понимания:
Есть два класса конфигурации для дева и прода:
@Configuration
@Profile("dev")
public class DevConfig {
// Конфигурация для дев
}
@Configuration
@Profile("prod")
public class ProdConfig {
// Конфигурация для прода
}
В файле application.yml:
spring:
profiles:
active: dev
В этом примере Spring будет использовать конфигурацию из DevConfig , а ProdConfig - только при активации профиля prod.
Профили могут использоваться для конфигурации различных аспектов приложения, таких как базы данных, настройки безопасности, внешние сервисы и многое другое. Например, можно настроить разные подключения к базам данных для разных сред выполнения (тестирование, разработка, продакшн).7 991
Предположим, что класс В наследует класс А. Какое утверждение обязательно верно?
