Перейти к основному содержимому

· 8 мин. чтения

1. Введение

В этой статье мы рассмотрим Netty — асинхронную среду сетевых приложений, управляемую событиями.

Основная цель Netty — построение высокопроизводительных протокольных серверов на основе NIO (или, возможно, NIO.2) с разделением и слабой связью компонентов сети и бизнес-логики. Он может реализовывать широко известный протокол, такой как HTTP, или ваш собственный протокол.

2. Основные концепции

Netty — это неблокирующий фреймворк. Это приводит к высокой пропускной способности по сравнению с блокирующим вводом-выводом. Понимание неблокирующего ввода-вывода имеет решающее значение для понимания основных компонентов Netty и их взаимосвязей.

· 6 мин. чтения

1. Введение

В этом руководстве мы рассмотрим операции CRUD с ресурсами Kubernetes с использованием официального Java API.

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

Как правило, развертывания Kubernetes в основном статичны. Мы создаем некоторые артефакты (например, файлы YAML), описывающие то, что мы хотим создать, и отправляем их в конвейер DevOps. Затем части нашей системы остаются неизменными, пока мы не добавим новый компонент или не обновим существующий.

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

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

· 7 мин. чтения

1. Введение

В этом руководстве мы обсудим определение индексов с помощью аннотации JPA @Index . На примерах мы узнаем, как определить наш первый индекс с помощью JPA и Hibernate. После этого мы собираемся изменить определение, показав дополнительные способы настройки индекса.

2. @Индексная аннотация

Давайте начнем с краткого обзора. Индекс базы данных — это структура данных, которая повышает скорость операций извлечения данных из таблицы за счет дополнительных операций записи и места для хранения. В основном это копия выбранных столбцов данных из одной таблицы. Мы должны создать индексы, чтобы повысить производительность на нашем уровне сохраняемости.

JPA позволяет нам добиться этого, определяя индексы из нашего кода с помощью @Index . Эта аннотация интерпретируется процессом генерации схемы, автоматически создавая артефакты. Обратите внимание, что нет необходимости указывать какой-либо индекс для наших сущностей.

· 12 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим использование MapStruct , который, проще говоря, является картографом Java Bean.

Этот API содержит функции, которые автоматически сопоставляются между двумя компонентами Java Bean. С MapStruct нам нужно только создать интерфейс, и библиотека автоматически создаст конкретную реализацию во время компиляции.

2. MapStruct и передача шаблона объекта

В большинстве приложений вы заметите много стандартного кода, преобразующего POJO в другие POJO.

Например, общий тип преобразования происходит между сущностями, поддерживаемыми постоянством, и DTO, которые выходят на сторону клиента.

Итак, вот проблема, которую решает MapStruct: ручное создание модулей сопоставления компонентов требует много времени. Но библиотека может автоматически генерировать классы сопоставления компонентов.

3. Мавен

Давайте добавим следующую зависимость в наш Maven pom.xml :

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>

Последний стабильный выпуск MapStruct и его процессор доступны в центральном репозитории Maven.

Давайте также добавим раздел annotationProcessorPaths в часть конфигурации плагина maven-compiler- plugin.

Обработчик mapstruct-processor используется для генерации реализации картографа во время сборки:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

4. Базовое картографирование

4.1. Создание POJO

Давайте сначала создадим простой Java POJO:

public class SimpleSource {
private String name;
private String description;
// getters and setters
}

public class SimpleDestination {
private String name;
private String description;
// getters and setters
}

4.2. Интерфейс картографа

@Mapper
public interface SimpleSourceDestinationMapper {
SimpleDestination sourceToDestination(SimpleSource source);
SimpleSource destinationToSource(SimpleDestination destination);
}

Обратите внимание, что мы не создавали класс реализации для нашего SimpleSourceDestinationMapper, потому что MapStruct создает его за нас.

4.3. Новый картограф

Мы можем запустить обработку MapStruct, выполнив mvn clean install .

Это сгенерирует класс реализации в /target/generated-sources/annotations/ .

Вот класс, который автоматически создает для нас MapStruct:

public class SimpleSourceDestinationMapperImpl
implements SimpleSourceDestinationMapper {
@Override
public SimpleDestination sourceToDestination(SimpleSource source) {
if ( source == null ) {
return null;
}
SimpleDestination simpleDestination = new SimpleDestination();
simpleDestination.setName( source.getName() );
simpleDestination.setDescription( source.getDescription() );
return simpleDestination;
}
@Override
public SimpleSource destinationToSource(SimpleDestination destination){
if ( destination == null ) {
return null;
}
SimpleSource simpleSource = new SimpleSource();
simpleSource.setName( destination.getName() );
simpleSource.setDescription( destination.getDescription() );
return simpleSource;
}
}

4.4. Прецедент

Наконец, со всем сгенерированным, давайте напишем тестовый пример, показывающий, что значения в SimpleSource соответствуют значениям в SimpleDestination :

public class SimpleSourceDestinationMapperIntegrationTest {
private SimpleSourceDestinationMapper mapper
= Mappers.getMapper(SimpleSourceDestinationMapper.class);
@Test
public void givenSourceToDestination_whenMaps_thenCorrect() {
SimpleSource simpleSource = new SimpleSource();
simpleSource.setName("SourceName");
simpleSource.setDescription("SourceDescription");
SimpleDestination destination = mapper.sourceToDestination(simpleSource);

assertEquals(simpleSource.getName(), destination.getName());
assertEquals(simpleSource.getDescription(),
destination.getDescription());
}
@Test
public void givenDestinationToSource_whenMaps_thenCorrect() {
SimpleDestination destination = new SimpleDestination();
destination.setName("DestinationName");
destination.setDescription("DestinationDescription");
SimpleSource source = mapper.destinationToSource(destination);
assertEquals(destination.getName(), source.getName());
assertEquals(destination.getDescription(),
source.getDescription());
}
}

5. Сопоставление с внедрением зависимостей

Затем давайте получим экземпляр преобразователя в MapStruct, просто вызвав Mappers.getMapper(YourClass.class) .

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

К счастью, MapStruct имеет надежную поддержку как Spring, так и CDI ( контексты и внедрение зависимостей ).

Чтобы использовать Spring IoC в нашем картографе, нам нужно добавить атрибут componentModel в @Mapper со значением spring , а для CDI это будет cdi .

5.1. Изменить картограф

Добавьте следующий код в SimpleSourceDestinationMapper :

@Mapper(componentModel = "spring")
public interface SimpleSourceDestinationMapper

5.2. Внедрить компоненты Spring в Mapper

Иногда нам нужно использовать другие компоненты Spring внутри нашей логики сопоставления. В этом случае мы должны использовать абстрактный класс вместо интерфейса :

@Mapper(componentModel = "spring")
public abstract class SimpleDestinationMapperUsingInjectedService

Затем мы можем легко внедрить нужный компонент, используя известную аннотацию @Autowired, и использовать его в нашем коде:

@Mapper(componentModel = "spring")
public abstract class SimpleDestinationMapperUsingInjectedService {

@Autowired
protected SimpleService simpleService;

@Mapping(target = "name", expression = "java(simpleService.enrichName(source.getName()))")
public abstract SimpleDestination sourceToDestination(SimpleSource source);
}

Мы должны помнить, что не следует делать инжектируемый bean-компонент приватным! Это связано с тем, что MapStruct должен получить доступ к объекту в сгенерированном классе реализации.

6. Сопоставление полей с разными именами полей

В нашем предыдущем примере MapStruct смог автоматически сопоставить наши bean-компоненты, потому что они имеют одинаковые имена полей. Итак, что, если bean-компонент, который мы собираемся сопоставить, имеет другое имя поля?

В этом примере мы создадим новый компонент с именами Employee и EmployeeDTO .

6.1. Новые POJO

public class EmployeeDTO {
private int employeeId;
private String employeeName;
// getters and setters
}
public class Employee {
private int id;
private String name;
// getters and setters
}

6.2. Интерфейс картографа

При сопоставлении разных имен полей нам нужно будет настроить его исходное поле на целевое поле, и для этого нам нужно будет добавить аннотацию @Mappings . Эта аннотация принимает массив аннотаций @Mapping , которые мы будем использовать для добавления целевого и исходного атрибутов.

В MapStruct мы также можем использовать точечную нотацию для определения члена компонента:

@Mapper
public interface EmployeeMapper {
@Mappings({
@Mapping(target="employeeId", source="entity.id"),
@Mapping(target="employeeName", source="entity.name")
})
EmployeeDTO employeeToEmployeeDTO(Employee entity);
@Mappings({
@Mapping(target="id", source="dto.employeeId"),
@Mapping(target="name", source="dto.employeeName")
})
Employee employeeDTOtoEmployee(EmployeeDTO dto);
}

6.3. Прецедент

Опять же, нам нужно проверить, совпадают ли значения исходного и целевого объекта:

@Test
public void givenEmployeeDTOwithDiffNametoEmployee_whenMaps_thenCorrect() {
EmployeeDTO dto = new EmployeeDTO();
dto.setEmployeeId(1);
dto.setEmployeeName("John");

Employee entity = mapper.employeeDTOtoEmployee(dto);

assertEquals(dto.getEmployeeId(), entity.getId());
assertEquals(dto.getEmployeeName(), entity.getName());
}

Больше тестовых случаев можно найти в проекте GitHub .

7. Сопоставление бинов с дочерними бинами

Далее мы покажем, как сопоставить компонент со ссылками на другие компоненты.

7.1. Изменить POJO

Давайте добавим новую ссылку на bean-компонент к объекту Employee :

public class EmployeeDTO {
private int employeeId;
private String employeeName;
private DivisionDTO division;
// getters and setters omitted
}
public class Employee {
private int id;
private String name;
private Division division;
// getters and setters omitted
}
public class Division {
private int id;
private String name;
// default constructor, getters and setters omitted
}

7.2. Изменить картограф

Здесь нам нужно добавить метод для преобразования Division в DivisionDTO и наоборот; если MapStruct обнаружит, что тип объекта необходимо преобразовать, а метод для преобразования существует в том же классе, он будет использовать его автоматически.

Давайте добавим это в маппер:

DivisionDTO divisionToDivisionDTO(Division entity);

Division divisionDTOtoDivision(DivisionDTO dto);

7.3. Изменить тестовый пример

Давайте изменим и добавим несколько тестовых случаев к существующему:

@Test
public void givenEmpDTONestedMappingToEmp_whenMaps_thenCorrect() {
EmployeeDTO dto = new EmployeeDTO();
dto.setDivision(new DivisionDTO(1, "Division1"));
Employee entity = mapper.employeeDTOtoEmployee(dto);
assertEquals(dto.getDivision().getId(),
entity.getDivision().getId());
assertEquals(dto.getDivision().getName(),
entity.getDivision().getName());
}

8. Сопоставление с преобразованием типов

MapStruct также предлагает несколько готовых неявных преобразований типов, и для нашего примера мы попытаемся преобразовать дату String в фактический объект Date .

Дополнительные сведения о неявном преобразовании типов см . в справочном руководстве по MapStruct .

8.1. Изменить бобы

Добавляем дату начала для нашего сотрудника:

public class Employee {
// other fields
private Date startDt;
// getters and setters
}
public class EmployeeDTO {
// other fields
private String employeeStartDt;
// getters and setters
}

8.2. Изменить картограф

Мы модифицируем маппер и предоставляем dateFormat для нашей даты начала:

@Mappings({
@Mapping(target="employeeId", source = "entity.id"),
@Mapping(target="employeeName", source = "entity.name"),
@Mapping(target="employeeStartDt", source = "entity.startDt",
dateFormat = "dd-MM-yyyy HH:mm:ss")})
EmployeeDTO employeeToEmployeeDTO(Employee entity);
@Mappings({
@Mapping(target="id", source="dto.employeeId"),
@Mapping(target="name", source="dto.employeeName"),
@Mapping(target="startDt", source="dto.employeeStartDt",
dateFormat="dd-MM-yyyy HH:mm:ss")})
Employee employeeDTOtoEmployee(EmployeeDTO dto);

8.3. Изменить тестовый пример

Давайте добавим еще несколько тестовых случаев, чтобы убедиться в правильности преобразования:

private static final String DATE_FORMAT = "dd-MM-yyyy HH:mm:ss";
@Test
public void givenEmpStartDtMappingToEmpDTO_whenMaps_thenCorrect() throws ParseException {
Employee entity = new Employee();
entity.setStartDt(new Date());
EmployeeDTO dto = mapper.employeeToEmployeeDTO(entity);
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);

assertEquals(format.parse(dto.getEmployeeStartDt()).toString(),
entity.getStartDt().toString());
}
@Test
public void givenEmpDTOStartDtMappingToEmp_whenMaps_thenCorrect() throws ParseException {
EmployeeDTO dto = new EmployeeDTO();
dto.setEmployeeStartDt("01-04-2016 01:00:00");
Employee entity = mapper.employeeDTOtoEmployee(dto);
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);

assertEquals(format.parse(dto.getEmployeeStartDt()).toString(),
entity.getStartDt().toString());
}

9. Сопоставление с абстрактным классом

Иногда мы можем захотеть настроить наш преобразователь таким образом, чтобы он превышал возможности @Mapping.

Например, в дополнение к преобразованию типов мы можем каким-то образом преобразовать значения, как в нашем примере ниже.

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

9.1. Базовая модель

В этом примере мы будем использовать следующий класс:

public class Transaction {
private Long id;
private String uuid = UUID.randomUUID().toString();
private BigDecimal total;

//standard getters
}

и соответствующий DTO:

public class TransactionDTO {

private String uuid;
private Long totalInCents;

// standard getters and setters
}

Сложная часть здесь заключается в преобразовании общей суммы BigDecimal в долларах в Long totalInCents .

9.2. Определение картографа

Мы можем добиться этого, создав наш Mapper как абстрактный класс:

@Mapper
abstract class TransactionMapper {

public TransactionDTO toTransactionDTO(Transaction transaction) {
TransactionDTO transactionDTO = new TransactionDTO();
transactionDTO.setUuid(transaction.getUuid());
transactionDTO.setTotalInCents(transaction.getTotal()
.multiply(new BigDecimal("100")).longValue());
return transactionDTO;
}

public abstract List<TransactionDTO> toTransactionDTO(
Collection<Transaction> transactions);
}

Здесь мы реализовали наш полностью настраиваемый метод сопоставления для преобразования одного объекта.

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

9.3. Сгенерированный результат

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

Будет сгенерировано следующее:

@Generated
class TransactionMapperImpl extends TransactionMapper {

@Override
public List<TransactionDTO> toTransactionDTO(Collection<Transaction> transactions) {
if ( transactions == null ) {
return null;
}

List<TransactionDTO> list = new ArrayList<>();
for ( Transaction transaction : transactions ) {
list.add( toTransactionDTO( transaction ) );
}

return list;
}
}

Как видно из строки 12, MapStruct использует нашу реализацию в сгенерированном методе.

10. Аннотации до и после картирования

Вот еще один способ настроить возможности @Mapping с помощью аннотаций @BeforeMapping и @AfterMapping . Аннотации используются для обозначения методов, которые вызываются непосредственно перед и после логики сопоставления.

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

Давайте рассмотрим пример, который сопоставляет подтипы Car ElectricCar и BioDieselCar с CarDTO .

При сопоставлении мы хотели бы сопоставить понятие типов с полем перечисления FuelType в DTO. Затем, после завершения сопоставления, мы хотели бы изменить имя DTO на верхний регистр.

10.1. Базовая модель

Мы будем использовать следующие классы:

public class Car {
private int id;
private String name;
}

Подтипы автомобилей :

public class BioDieselCar extends Car {
}
public class ElectricCar extends Car {
}

CarDTO с типом поля перечисления FuelType :

public class CarDTO {
private int id;
private String name;
private FuelType fuelType;
}
public enum FuelType {
ELECTRIC, BIO_DIESEL
}

10.2. Определение картографа

Теперь давайте продолжим и напишем наш абстрактный класс преобразователя, который сопоставляет Car с CarDTO :

@Mapper
public abstract class CarsMapper {
@BeforeMapping
protected void enrichDTOWithFuelType(Car car, @MappingTarget CarDTO carDto) {
if (car instanceof ElectricCar) {
carDto.setFuelType(FuelType.ELECTRIC);
}
if (car instanceof BioDieselCar) {
carDto.setFuelType(FuelType.BIO_DIESEL);
}
}

@AfterMapping
protected void convertNameToUpperCase(@MappingTarget CarDTO carDto) {
carDto.setName(carDto.getName().toUpperCase());
}

public abstract CarDTO toCarDto(Car car);
}

@MappingTarget — это аннотация параметра, которая заполняет DTO целевого сопоставления непосредственно перед выполнением логики сопоставления в случае @BeforeMapping и сразу после в случае `` аннотированного метода @AfterMapping .

10.3. Результат

Определенный выше CarsMapper генерирует реализацию : ****

@Generated
public class CarsMapperImpl extends CarsMapper {

@Override
public CarDTO toCarDto(Car car) {
if (car == null) {
return null;
}

CarDTO carDTO = new CarDTO();

enrichDTOWithFuelType(car, carDTO);

carDTO.setId(car.getId());
carDTO.setName(car.getName());

convertNameToUpperCase(carDTO);

return carDTO;
}
}

Обратите внимание, как вызовы аннотированных методов окружают логику сопоставления в реализации.

11. Поддержка Ломбока

В последней версии MapStruct была объявлена поддержка Lombok. Таким образом, мы можем легко сопоставить исходный объект и пункт назначения с помощью Lombok.

Чтобы включить поддержку Lombok, нам нужно добавить зависимость в пути процессора аннотаций. Начиная с версии Lombok 1.18.16, мы также должны добавить зависимость от lombok-mapstruct-binding . Теперь у нас есть mapstruct-processor, а также Lombok в плагине компилятора Maven:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

Давайте определим исходный объект, используя аннотации Lombok:

@Getter
@Setter
public class Car {
private int id;
private String name;
}

И целевой объект передачи данных:

@Getter
@Setter
public class CarDTO {
private int id;
private String name;
}

Интерфейс картографа для этого остается таким же, как в нашем предыдущем примере:

@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO carToCarDTO(Car car);
}

12. Поддержка выражения по умолчанию

Начиная с версии 1.3.0, мы можем использовать атрибут defaultExpression аннотации @Mapping , чтобы указать выражение, определяющее значение поля назначения, если исходное поле имеет значение null . Это в дополнение к существующей функциональности атрибута defaultValue .

Исходный объект:

public class Person {
private int id;
private String name;
}

Объект передачи данных назначения:

public class PersonDTO {
private int id;
private String name;
}

Если поле id исходного объекта равно null , мы хотим сгенерировать случайный идентификатор и назначить его получателю, сохраняя другие значения свойств как есть:

@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

@Mapping(target = "id", source = "person.id",
defaultExpression = "java(java.util.UUID.randomUUID().toString())")
PersonDTO personToPersonDTO(Person person);
}

Давайте добавим тестовый пример для проверки выполнения выражения:

@Test
public void givenPersonEntitytoPersonWithExpression_whenMaps_thenCorrect()
Person entity = new Person();
entity.setName("Micheal");
PersonDTO personDto = PersonMapper.INSTANCE.personToPersonDTO(entity);
assertNull(entity.getId());
assertNotNull(personDto.getId());
assertEquals(personDto.getName(), entity.getName());
}

13. Заключение

В этой статье представлено введение в MapStruct. Мы представили большую часть основ библиотеки сопоставления и способы ее использования в наших приложениях.

Реализацию этих примеров и тестов можно найти в проекте GitHub . Это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.

· 3 мин. чтения

1. Введение

В этом кратком руководстве мы покажем преимущества сочетания поддерживающей мощности среды тестирования Spring Boot и выразительности среды Spock , будь то модульные или интеграционные тесты .

2. Настройка проекта

Начнем с простого веб-приложения. Он может приветствовать, изменять приветствие и сбрасывать его до значения по умолчанию с помощью простых вызовов REST. Помимо основного класса, мы используем простой RestController для обеспечения функциональности:

@RestController
@RequestMapping("/hello")
public class WebController {

@GetMapping
public String salutation() {
return "Hello world!";
}
}

· 5 мин. чтения

1. Введение

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

Хотя мы можем прикреплять их к пакетам, классам, интерфейсам, методам и полям, аннотации сами по себе не влияют на выполнение программы.

В этом руководстве мы сосредоточимся на том, как создавать и обрабатывать пользовательские аннотации. Подробнее об аннотациях мы можем прочитать в нашей статье об основах аннотаций .

2. Создание пользовательских аннотаций

· 5 мин. чтения

1. Обзор

Наши службы часто взаимодействуют с другими службами REST для получения информации.

Начиная с Spring 5, мы можем использовать WebClient для выполнения этих запросов реактивным, неблокирующим способом. WebClient является частью нового WebFlux Framework, построенного поверх Project Reactor . Он имеет свободный, реактивный API и использует протокол HTTP в своей базовой реализации.

Когда мы делаем веб-запрос, данные часто возвращаются в формате JSON. WebClient может преобразовать это для нас.

В этой статье мы узнаем, как преобразовать массив JSON в массив объектов Java , массив POJO и список POJO с помощью WebClient . [](/lessons/b/-java-pojo-class) ``

2. Зависимости

· 4 мин. чтения

1. Обзор

В этом руководстве мы покажем, как исправить предупреждение «log4j: WARN Не удалось найти приложения для регистратора» . `` Мы объясним, что такое аппендер и как его определить. Кроме того, мы покажем, как решить предупреждение различными способами.

2. Определение добавления

Давайте сначала объясним, что такое аппендер. Log4j позволяет нам помещать журналы в несколько мест назначения. Каждый пункт назначения, в который он выводит вывод, называется appender . У нас есть приложения для консоли, файлов, компонентов JMS, GUI и других.

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

· 3 мин. чтения

1. Обзор

В этом кратком руководстве мы подробно рассмотрим ошибку Spring Boot « ApplicationContextException: невозможно запустить ServletWebServerApplicationContext из-за отсутствия bean-компонента ServletWebServerFactory ».

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

2. Возможные причины

Во-первых, давайте попробуем понять, что означает сообщение об ошибке. « Невозможно запустить ServletWebServerApplicationContext из-за отсутствия bean-компонента ServletWebServerFactory » говорит само за себя. Он просто говорит нам, что в ApplicationContext нет настроенного bean-компонента ServletWebServerFactory . `` ``

· 3 мин. чтения

1. Весна и Ява

>> Укрощение областей ресурсов [ inside.java ]

Изучение различных подходов к тому, чтобы сделать API доступа к внешней памяти/компоновщику более безопасным — интересный взгляд на будущее нативных Java!

>> Рекомендации по безопасности Spring Microservices [ piotrminkowski.com ]

Рецепты создания безопасных микросервисов с помощью Spring Boot и Spring Security — краткие и в то же время практичные!

>> Дорога к Quarkus 2.0: непрерывное тестирование [ infoq.com ]