SPRING Flashcards

1
Q

Что такое Spring Framework и какие основные задачи он решает?

A

Spring Framework - это фреймворк для разработки приложений на языке Java, который предоставляет комплексное решение для различных задач, связанных с разработкой приложений.

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

В рамках механизма управления жизненным циклом объектов Spring осуществляет инъекцию зависимостей, конфигурирование бинов (англ. bean configuration), управление состоянием и жизненным циклом бинов (Жизненный цикл объекта состоит из нескольких этапов, включая создание, инициализацию, использование и уничтожение.).

Основные задачи, которые решает Spring Framework, включают в себя:

  • Управление жизненным циклом объектов: Spring позволяет контролировать жизненный цикл объектов приложения, в том числе создание, настройку и уничтожение объектов.
  • Внедрение зависимостей: Spring предоставляет механизм внедрения зависимостей, который позволяет автоматически связывать объекты в приложении. Это уменьшает связность между объектами и повышает гибкость приложения.
  • Транзакции: Spring предоставляет механизм управления транзакциями, который позволяет создавать и управлять транзакциями при работе с базами данных и другими ресурсами.
  • Безопасность: Spring предоставляет механизмы для реализации безопасности приложений, включая аутентификацию, авторизацию, защиту от атак и другие меры.
  • Работа с базами данных: Spring предоставляет удобные средства для работы с базами данных, включая JDBC и ORM-фреймворки, такие как Hibernate и JPA.
  • Веб-разработка: Spring предоставляет механизмы для разработки веб-приложений, включая поддержку MVC-архитектуры, обработку запросов и создание RESTful API.
  • Тестирование: Spring упрощает тестирование приложений, предоставляя механизмы для создания тестовых контекстов и поддержку интеграционного тестирования.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Как происходит конфигурирование Spring?

A

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

Существует несколько способов конфигурирования Spring:

  • XML-конфигурация: в этом способе конфигурации все настройки приложения описываются в XML-файле, который загружается при запуске приложения. Примером такого файла может быть applicationContext.xml.
  • Java-конфигурация: в этом способе конфигурации настройки приложения описываются в виде Java-кода. Этот способ использует аннотации, такие как @Configuration, @Bean, @ComponentScan и др., и может быть предпочтительнее, если требуется более гибкая настройка.
  • Конфигурация на основе аннотаций: в этом способе конфигурации все настройки приложения описываются в аннотациях, которые применяются к классам, методам и полям. Этот способ использует аннотации, такие как @Component, @Autowired, @Value и др., и может быть предпочтительнее, если требуется более компактный и читаемый код.

После того, как файл конфигурации был создан, его необходимо загрузить в Spring контейнер при запуске приложения. Это делается с помощью специального класса ApplicationContext, который загружает конфигурацию и создает бины. Затем приложение может использовать эти бины для выполнения своей работы.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Что такое инверсия контроля (IoC) и внедрение зависимостей (DI)? Как эти принципы реализованы в Spring?

A

Inversion of Control –это принцип в разработке программного обеспечения, при котором управление объектами или частями программы передается контейнеру или фреймворку.принцип для написания слабосвязанного кода

Dependencies -другие объекты (как правило других классов), с которыми работает и чью функциональность использует текущий объект.

Dependency Injection является способом реализации принципа IoC в Spring через рефлексию:
DI механизм передачи классу его зависимостей
Dependency Injection (DI) - это механизм внедрения зависимостей, при котором классы не создают свои зависимости, а получают их из внешних источников, таких как Spring-контейнер.

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

Spring определяет и внедряет зависимости через (на выбор):
* Конструкторы: можно помечать конструкторы аннотацией @Autowired, чтобы Spring автоматически искал подходящие бины для всех параметров конструктора и использовал их для создания экземпляра класса.
* Сеттеры: можно помечать методы-сеттеры аннотацией @Autowired, чтобы Spring автоматически внедрял бины в соответствующие поля.
* Поля: можно также помечать поля класса аннотацией @Autowired, чтобы Spring автоматически внедрял бины в эти поля.
* Методы-фабрики: можно пометить статический метод класса аннотацией @Bean, который возвращает экземпляр класса, чтобы Spring использовал этот метод в качестве фабричного метода для создания бинов.

В Spring Framework инверсия контроля достигается именно внедрением зависимостей. В Spring Framework инверсия контроля и внедрение зависимостей считаются одним и тем же.

IoC-контейнер отвечает за управление жизненным циклом объектов: создание, вызов методов инициализации и конфигурирование объектов через связывание их между собой.
Объекты, создаваемые контейнером, называются beans. Конфигурирование контейнера осуществляется через внедрение аннотаций, но есть возможность, по старинке, загрузить XML-файлы, содержащие определение bean’ов и предоставляющие информацию, необходимую для создания bean’ов.

IoC - аутсорсинг создания и управления объектами. Т.е. передача программистом прав на создание и управление объектами Спрингу
DI - аутсорсинг добавления/внедрения зависимостей. DI делает объекты нашего приложения слабо зависимыми друг от друга.
DI это частный случай IoC
IoC это принцип или методология, а DI это конкретная реализация

  • IoC - (принцип, набор рекомендаций для написания слабосвязанного кода) Принцип
  • DI - (один из способов реализовать принцип) Паттерн
  • IoC - Container ( приложение, реализующее принцип) Фрэймворк
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Что такое IoC контейнер?

A

IoC контейнер - это механизм, используемый в фреймворке Spring для управления жизненным циклом объектов, а также для внедрения зависимостей между объектами.
IoC Container - это объект, который занимается созданием других объектов и внедрением в них зависимостей(DI)

в IOC (Inversion of Control) контейнере объекты, или бины, хранятся в ассоциативном массиве, где ключом обычно является уникальный идентификатор (например, строковый), который позволяет получить доступ к соответствующему объекту. Это позволяет легко настраивать зависимости между бинами и обеспечивает централизованное управление созданием и конфигурированием объектов, что упрощает разработку приложений.

Контейнер Spring представляет собой реализацию принципа Inversion of Control (IoC), который состоит в том, что контроль за жизненным циклом объектов передается контейнеру, который управляет созданием, настройкой и уничтожением объектов.

В Spring Framework контейнер отвечает за создание, настройку и сборку объектов, известных как бины, а также за управление их жизненным циклом. Он (контейнер) представлен интерфейсом ApplicationContext.
Spring Framework предоставляет несколько реализаций интерфейса ApplicationContext:
* ClassPathXmlApplicationContext и FileSystemXmlApplicationContext - для автономныхприложений;
* WebApplicationContext - для веб-приложений;
* AnnotationConfigApplicationContext - для обычной Java-конфигурации, в качестве аргумента которому передается класс, либо список классов с аннотацией @Configuration, либо с любой другой аннотацией JSR-330, в том числе и @Component.

ApplicationContext может быть настроен разными способами:
* через XML-файлы,
* аннотации
* чистый Java-код.

Контейнер автоматически находит классы с аннотациями, такими как @Component, @Service и т.д., и создает объекты-бины на основе их определений. Кроме того, он позволяет внедрять зависимости между бинами с помощью аннотации @Autowired или XML-конфигурации.

Spring BeanFactory Container Это самый простой контейнер, который обеспечивает базовую поддержку DI и который основан на интерфейсе org.springframework.beans.factory.BeanFactory.
BeanFactory обычно используется тогда, когда ресурсы ограничены (мобильные устройства). Поэтому, если ресурсы не сильно ограничены, то лучше использовать ApplicationContext.

Spring ApplicationContext Container является более сложным и более продвинутым Spring Container-ом. Наследует BeanFactory и так же загружает бины, связывает их вместе и конфигурирует их определённым образом. Но кроме этого предоставляет дополнительные возможности, например AOP и транзакции. Этот контейнер определяется интерфейсом org.springframework.context.ApplicationContext.

Контейнер Spring может создавать объекты в различных скоупах, таких как Singleton, Prototype, Session и Request. Singleton обеспечивает создание единственного экземпляра объекта на уровне приложения, тогда как Prototype создает новый экземпляр каждый раз при запросе объекта. Session и Request используются в веб-приложениях и обеспечивают создание объектов на уровне сессии и запроса соответственно.

Контейнер Spring позволяет внедрять зависимости между объектами при помощи автоматического связывания (Autowiring) или явного задания зависимостей. Автоматическое связывание позволяет контейнеру Spring самостоятельно определить зависимости между объектами на основе их типа и имен, тогда как явное задание зависимостей использует аннотации, XML-конфигурацию или Java-код для определения зависимостей.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Что такое Bean в спринге?

A

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

@Bean - это объект со всеми необходымыми зависимостями, который был создан с помощью IoC Container
Еще можно добавить, что Bean это переиспользуемый програмный компонент
Bean Definition - это чертеж
Bean - это деталь, созданная по данному чертежу

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

По умолчанию бин задается как синглтон.
Таким образом все публичные переменные класса могут быть изменены одновременно из разных мест, а значит бин - не потокобезопасен. Однако поменяв область действия бина на request, prototype, session он станет потокобезопасным, но это скажется на производительности.

Существуют несколько требований к бину в Spring:

  • Класс должен иметь конструктор без аргументов.
  • Класс должен быть открытым (public).
  • Класс должен быть не абстрактным (abstract).
  • Класс должен содержать публичные методы, если они используются для внедрения зависимостей через сеттеры.
  • Класс должен быть помечен одной из аннотаций: @Component, @Service, @Controller или другой аннотацией, которая явно указывает на то, что этот класс является Spring bean.
  • Если используется XML-конфигурация, то класс должен быть определен в XML-файле.
  • Если используется аннотационная конфигурация, то класс должен быть сканирован при помощи аннотации @ComponentScan.
  • Если класс реализует интерфейс BeanNameAware, то он должен иметь метод setBeanName(), который будет вызываться при создании бина для передачи ему его имени в качестве аргумента.
  • Если класс реализует интерфейс InitializingBean, то он должен иметь метод afterPropertiesSet(), который будет вызываться после внедрения всех зависимостей.
  • Если класс имеет метод, помеченный аннотацией @PostConstruct, то этот метод будет вызван после внедрения зависимостей и перед тем, как бин будет доступен для использования.
  • Если класс имеет метод, помеченный аннотацией @PreDestroy, то этот метод будет вызван перед уничтожением бина.
  • Если класс реализует интерфейс DisposableBean, то он должен иметь метод destroy(), который будет вызываться перед уничтожением бина.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Расскажите про аннотацию @Bean?

A

Аннотация @Bean используется для указания того, что метод создает, настраивает и инициализирует новый объект, управляемый IoC-контейнером.
Как правило используется в классах с аннотацией @Configuration,
Может использоваться и в классах с аннотацией @Component (или ее наследниках).
Позволяет дополнительно определить у бина:
- name - имя (уникальный идентификатор) бина;
- initMethod - имя метода для вызова во время инициализации бина;
- destroyMethod - имя метода для вызова во время удаления бина из контекста;
- autowireCandidate - является ли этот бин кандидатом на автоматическое внедрение в другой бин.

@Configuration
public class MyConfig {
    @Bean(name = "myBean", initMethod = "init", destroyMethod = "cleanup")
    @Scope("prototype")
    @DependsOn("dependencyBean")
    public MyBean myBean() {
        return new MyBean();
    }
    @Bean(name = "dependencyBean")
    public DependencyBean dependencyBean() {
        return new DependencyBean();
    }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Что такое Bean definition и metadata?

A

Bean definition - это описание бина, которое может содержать информацию о классе бина, его свойствах, зависимостях и других метаданных. В Spring бины создаются и настраиваются в IoC контейнере, на основе Bean definition.
Bean definition может содержать информацию о классе объекта, его свойствах, зависимостях, и других конфигурационных параметрах.

@Configuration
public class MyConfig {

    @Bean
    public MyBean myBean() {
        MyBean bean = new MyBean();
        bean.setMyProperty("myValue");
        return bean;
    }
}

В этом примере метод myBean() является фабричным методом, который возвращает экземпляр класса MyBean. Аннотация @Bean указывает, что метод должен быть обработан IoC контейнером как Bean definition, и результат его выполнения должен быть помещен в контейнер.
Вот несколько примеров аннотаций, которые можно отнести к Bean Definition:
* @Bean - используется для объявления метода, который возвращает объект, который будет управляться контейнером Spring. Этот метод может содержать логику создания, настройки и инициализации бина.
* @Configuration - используется для указания, что класс содержит один или несколько методов, которые возвращают бины, которые нужно создать и настроить. Эта аннотация является альтернативой использованию XML-файлов конфигурации.
* @ComponentScan - указывает Spring-контейнеру, какие пакеты нужно сканировать, чтобы найти все классы, которые могут быть использованы как бины. Эта аннотация может использоваться в классах конфигурации.
* @Import - используется для импорта конфигурационных классов или Java-конфигураций в текущий контекст приложения.
* @Conditional - позволяет задать условия для создания бина. Бин будет создан только в том случае, если указанные условия выполняются.
* @Lazy - используется для указания, что бин должен быть создан только в момент, когда он первый раз используется.

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

Метаданные могут быть использованы для описания различных аспектов бина, таких как область видимости, жизненный цикл, зависимости, аспекты и т.д. Например, для управления областью видимости можно использовать аннотации, такие как @Scope, а для управления жизненным циклом - аннотации, такие как @PostConstruct и @PreDestroy.

@Component
@Scope("prototype")
public class MyBean {
    @Autowired
    private MyDependency dependency;
    
    // ...
}

Вот несколько примеров аннотаций, которые можно отнести к метаданным:
* @Autowired - указывает на необходимость внедрения зависимости для данного бина. Аннотация может использоваться для полей, методов и конструкторов.
* @Qualifier - используется вместе с @Autowired для разрешения неоднозначности, когда в контейнере существует несколько бинов одного типа. Она позволяет указать конкретный бин, который должен быть внедрен.
* @Value - позволяет задать значение свойства бина в виде строки. Аннотация может использоваться для полей, методов и конструкторов.
* @PostConstruct - позволяет указать метод, который должен быть вызван после создания бина, но перед тем, как он будет использован. Это может быть полезно для инициализации объектов.
* @Scope - позволяет указать область видимости бина. По умолчанию, бины создаются в области видимости singleton, но можно использовать и другие области видимости, такие как prototype, request, session и другие.
* @Profile - позволяет определить профиль, которому соответствует данный бин. Бины с определенным профилем будут созданы только в том случае, если данный профиль активен.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Расскажите про аннотацию @Component?

A

Это аннотация Spring Framework, ею мы помечаем класс, если хотим, чтобы из этого класса был создан бин. Именно эту аннотацию ищет Spring Framework, когда сканирует наши классы. Можно указать имя (Id) для создаваемого бина, а можно не указывать, тогда по умолчанию именем будет название класса с маленькой буквы.

Когда класс помечается аннотацией @Component, Spring автоматически создает экземпляр этого класса в качестве бина в контейнере. Это позволяет другим классам в приложении использовать этот компонент через механизм внедрения зависимостей (DI).

Аннотация @Component имеет наследников: @Repository, @Service и @Controller. Все они являются частными случаями использования @Component для слоёв DAO, сервиса и контроллера MVC соответственно.

@Component - Spring определяет этот класс как кандидата для создания bean.

@Service - класс содержит бизнес-логику и вызывает методы на уровне хранилища. Ничем не отличается от классов с @Component.

@Repository - указывает, что класс является слоем, взаимодействующем с ДБ. Задача @Repository заключается в том, чтобы отлавливать исключения персистентности и приводить их к подклассам DataAccessExeption спринга.Для этого Spring оборачивает эти классы в прокси, и в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor.

@Controller - указывает, что класс выполняет роль контроллера MVC. DispatcherServlet просматривает такие классы для поиска @RequestMapping.
@RequestMapping используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика.

@Configuration - означает, что класс будет содержать bean definition методы с аннотацией @Bean. Класс, помеченный этой аннотацией, должен быть открытым и не должен быть final. Конструкторы в таком классе должны быть public или protected, а методы, возвращающие bean, должны быть отмечены аннотацией @Bean.

@RequestMapping используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Чем отличаются аннотации @Bean и @Component?

A

Аннотация @Component (как и @Service и @Repository) используется для автоматического обнаружения и автоматической настройки бинов в ходе сканирования путей к классам.

@Component используется для аннотирования классов, которые должны быть автоматически обнаружены и зарегистрированы в контексте Spring. Эта аннотация может быть использована для любого компонента, такого как сервисы, репозитории, контроллеры и т.д.

@Bean используется для методов, которые должны быть вызваны для создания и инициализации объектов в контексте Spring. Эта аннотация может использоваться в классах, помеченных аннотацией @Configuration, и методы, помеченные этой аннотацией, будут вызываться для создания бинов.

Аннотация @Bean используется для явного объявления бина и позволяет добавить bean, уже реализованного сторонней библиотекой.

Аннотация @Bean используется для явного объявления бина, а не для того, чтобы Spring делал это автоматически в ходе сканирования путей к классам:

  • прописываем вручную метод для создания бина;
  • делает возможным объявление бина независимо от объявления класса, что позволяет использовать классы из сторонних библиотек, у которых мы не можем указать аннотацию @Component;
  • с аннотацией @Bean можно настроить initMethod, destroyMethod, autowireCandidate, делая создание бина более гибким.

Таким образом, основное отличие между @Bean и @Component заключается в том, что первая используется для создания и настройки объектов в контексте Spring, а вторая для обнаружения и регистрации компонентов в контексте Spring.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Расскажите про аннотации @Service и @Repository. Чем они отличаются?

A

Аннотация @Service и @Repository являются специализированными версиями аннотации @Component в Spring Framework, и каждая из них предназначена для использования в конкретных слоях приложения.

Аннотация @Repository предназначена для классов, которые предоставляют доступ к базе данных или другим источникам данных, таким как файловые системы или JMS-очереди. Эта аннотация обычно используется в слое доступа к данным (DAO).
Задача @Repository заключается в том, чтобы отлавливать определенные исключения персистентности и пробрасывать их как одно непроверенное исключение Spring Framework. Для этого в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor.

Мы помечаем бины аннотацией @Service, чтобы указать, что они содержат бизнес- логику. Так что нет никакого другого предназначения, кроме как использовать ее на уровне сервиса.
Аннотация @Service предназначена для классов, которые предоставляют бизнес-логику приложения, такую как обработка данных и выполнение операций над ними. Эта аннотация обычно используется в сервисном слое приложения.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Расскажите про аннотацию @Autowired

A

Аннотация @Autowired в Spring Framework используется для автоматической внедрения зависимостей в поля классов, методы или конструкторы.
Она позволяет автоматически связывать бины (объекты) между собой,
Это означает, что Spring Framework автоматически находит классы, которые удовлетворяют зависимости, объявленные в вашем классе, и создает экземпляры этих классов, которые можно использовать.

Это аннотация Spring Framework, ею помечают конструктор, поле, сеттер-метод или метод конфигурации, сигнализируя, что им обязательно требуется внедрение зависимостей.
@Autowired – автоматическое внедрение подходящего бина:

  • Спринг сканирует все классы, которые могут быть использованы в качестве бинов и создает эти бины на основе определенных аннотаций, таких как @Component, @Service, @Repository и т.д.
  • При создании каждого бина, Spring анализирует его зависимости и пытается автоматически связать их во время запуска приложения. Это называется автосвязыванием (autowiring).
  • Когда Spring встречает аннотацию @Autowired над полем, конструктором или методом, он ищет бины, которые могут удовлетворить тип или имена зависимостей, указанные в аннотации.
  • Если Spring находит только один соответствующий бин, то он будет использован для автосвязывания.
  • Если не найдено ни одного соответствующего бина, то Spring выбросит исключение, , если не указать @Autowired(required = false).
  • Если найдено несколько соответствующих бинов, то Spring использует стратегию выбора подходящего бина на основе алгоритма приоритетов (по умолчанию используется приоритет типа бина, затем имя бина, затем значение аннотации @Primary, и т.д.). Если этот алгоритм не удается разрешить неоднозначность, то Spring выбросит исключение.
  • Если необходимо, можно использовать дополнительные аннотации, такие как @Qualifier или @Primary, чтобы уточнить, какой бин должен быть использован для конкретной зависимости.

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

Начиная со Spring Framework 4.3, аннотация @Autowired для конструктора больше не требуется, если целевой компонент определяет только один конструктор.

Мы также можем указать Spring предоставить все бины определенного типа из ApplicationContext, добавив аннотацию @Autowired в поле или метод с массивом или коллекцией этого типа:
~~~
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs; }
~~~</MovieCatalog>

Даже коллекции типа Map могут быть подключены автоматически, если тип ключа - String. Ключами будут имена бинов, а значениями - сами бины:

private Map<String, MovieCatalog> movieCatalogs;

@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs){
this.movieCatalogs = movieCatalogs; }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Основные правила использования аннотации @Autowired в Spring Framework:

A

Основные правила использования аннотации @Autowired в Spring Framework:

  • Аннотация @Autowired может быть использована для инъекции зависимостей в полях класса, конструкторы, методы(сеттеры) и параметры методов.
  • При использовании аннотации @Autowired для инъекции зависимостей в свойства класса, должен быть настроен bean с соответствующим типом.
  • Если существует более одного подходящего bean-а для инъекции, необходимо использовать аннотацию @Qualifier для выбора конкретного bean-а.
  • Если необходимо использовать аннотацию @Autowired для инъекции зависимостей в конструктор, метод или параметр метода, то должен быть определен только один подходящий bean.
  • Если необходимо, чтобы инъекция зависимостей была необязательной, то можно добавить параметр required=false в аннотацию @Autowired.
  • Аннотация @Autowired может быть использована как для автосвязывания по типу, так и для связывания по имени. Второй вариант возможен, если свойство, метод или конструктор имеет соответствующее имя, как у бина.
  • Аннотация @Autowired также может быть использована для инъекции зависимостей, которые не являются бинами Spring Framework, таких как JDBC-DataSource или EntityManager.
  • Аннотация @Autowired может быть использована в сочетании с аннотациями @Qualifier, @Primary, @Resource и @Value для более тонкой настройки инъекции зависимостей.
  • Аннотация @Autowired может быть использована в сочетании с аннотациями @Transactional и @Scheduled для инъекции зависимостей, которые требуют управления транзакциями и планирования задач.
  • Не рекомендуется использовать аннотацию @Autowired в классах, которые не управляются контейнером Spring Framework, таких как классы модели или службы. Вместо этого следует использовать конструкторы, методы и свойства для внедрения зависимостей.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Расскажите про аннотацию @Qualifier

A

@Qualifier - это аннотация в Spring Framework, которая используется для разрешения конфликтов при инъекции зависимостей.

Когда в приложении есть несколько бинов (компонентов), которые соответствуют типу и классу, то Spring не может определить, какой из них должен быть использован в конкретном месте, где происходит инъекция зависимости. В этом случае @Qualifier позволяет явно указать, какой именно бин должен быть использован.

Пример использования @Qualifier:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    @Qualifier("userRepositoryImpl")
    private UserRepository userRepository;

    // ...
}

В этом примере мы явно указываем, что для инъекции зависимости UserRepository необходимо использовать бин с именем “userRepositoryImpl”. Без использования @Qualifier Spring не смог бы определить, какой именно бин использовать, если в приложении существует несколько классов, реализующих интерфейс UserRepository.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Расскажите про аннотацию @Value

A

Аннотация @Value - это аннотация в Spring Framework, которая используется для инъекции значения свойства из внешнего источника в Spring-управляемый компонент.

@Autowired и @Value могут использоваться вместе, чтобы инъектировать значения свойств в Spring-управляемые компоненты, используя имена свойств, которые указываются в @Value.

@Service
public class MyService {

    @Autowired
    private MyRepository myRepository;

    @Value("${my.property}")
    private String myProperty;

    // ...
}

В этом примере мы использовали @Autowired для инъекции MyRepository в поле myRepository класса MyService, а также использовали @Value для инъекции значения свойства my.property в поле myProperty класса MyService. Значение my.property должно быть определено внутри внешнего источника, такого как application.properties или application.yml.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Расскажите про аннотацию @Resource

A

Java-аннотация @Resource может применяться к классам, полям и методам. Она пытается получить зависимость: сначала по имени, затем по типу, затем по описанию (Qualifier).

Имя извлекается из имени аннотируемого сеттера или поля, либо берется из параметра name.

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

Аннотация @Resource является альтернативой аннотации @Autowired в Spring и имеет несколько отличий:

  • Поиск бина происходит сначала по имени и затем по типу. Если указано имя бина, то оно будет использоваться для поиска в первую очередь.
  • Не нужна дополнительная аннотация для указания имени конкретного бина. Имя бина можно указать непосредственно в аннотации @Resource.
  • Нет возможности указать, что место вставки бина является необязательным. В аннотации @Resource отсутствует параметр required, который есть в аннотации @Autowired.
  • При замене Spring Framework на другой фреймворк, менять аннотацию @Resource не нужно.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Аннотация @Named

A

Аннотация @Named является частью JSR 330 (Dependency Injection for Java), которая используется для указания имени для определенного компонента или бина, который может быть использован в DI контейнере.

@Named является альтернативой аннотации @Component, которая является частью Spring Framework. Она используется для маркировки классов, которые будут создаваться и управляться контейнером Spring.

В отличие от @Component, которая создает бин с именем класса, @Named позволяет указать произвольное имя для создаваемого бина. Это позволяет избежать конфликтов имен при создании бинов и облегчает конфигурирование DI контейнера.

@Inject
@Named("userService")
private UserService userService;

В этом примере мы использовали аннотацию @Inject, которая является частью JSR 330, для внедрения зависимости UserService в наш класс. Мы также использовали @Named для указания имени “userService” для UserService.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Расскажите про аннотацию @Inject

A

@Inject (аннотация java) – аналог @Autowired (аннотация spring) в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени.
Также стоит отметить, что аннотация @Inject похожа на аннотацию @Autowired в Spring Framework. Однако, в отличие от @Autowired, аннотация @Inject является частью стандарта Java EE и может использоваться в любом контейнере, который поддерживает JSR-330.

Аналог @Autowired - пытается получить зависимость в таком порядке:

1) По типу
2) По описанию
3) По имени

В ней нет параметров, поэтому при использовании бина по Id, используем

@Named

@Inject
@Named("someName")
private method1()
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Расскажите про аннотацию @Lookup

A

Аннотация @Lookup в Spring Framework используется для внедрения зависимостей с динамическим созданием объектов. Это позволяет создавать новые экземпляры бинов на лету, вместо того, чтобы внедрять один и тот же экземпляр бина в каждый класс, который его требует.

В основе работы аннотации @Lookup лежит использование прокси-объекта, который создается Spring Framework для класса, помеченного этой аннотацией. Когда вызывается метод, помеченный @Lookup, Spring Framework перехватывает вызов и создает новый экземпляр бина на лету.

@Component
public class Car {

    @Lookup
    public Passenger getPassenger() {
        // This method will be overridden by Spring
        // and return a new instance of Passenger every time it's called
        return null;
    }

    public void drive() {
        Passenger passenger = getPassenger();
        // Use the new instance of Passenger
        // ...
    }
}

@Component
@Scope("prototype")
public class Passenger {
    // ...
}

Аннотация @Lookup в Spring Framework используется для внедрения зависимостей через “look-up” методы. Эти методы определяются в интерфейсах или абстрактных классах, а их конкретная реализация генерируется Spring’ом во время выполнения.

Например, допустим у нас есть интерфейс UserService, который определяет методы для работы с пользователями. В этом интерфейсе мы можем объявить метод getUser, помеченный аннотацией @Lookup, который будет возвращать новый экземпляр User каждый раз, когда он вызывается.

public interface UserService {
    @Lookup
    public User getUser();
}

Затем мы можем создать конкретную реализацию этого интерфейса, помеченную аннотацией @Service:
~~~
@Service
public class UserServiceImpl implements UserService {
public User getUser() {
return new User();
}
}
~~~
При каждом вызове метода getUser() будет создаваться новый экземпляр User. Это может быть полезно в некоторых случаях, например, когда необходимо создавать новый объект для каждого запроса или когда объекты должны быть уникальными для каждого потока выполнения.

Аннотация @Lookup позволяет решать несколько задач и преодолевать некоторые проблемы:

  • Внедрение prototype бина в singleton бин: Когда необходимо внедрить bean с scope “prototype” в singleton bean, то стандартный подход в Spring не работает, т.к. singleton bean создается только один раз во время запуска приложения, и при каждом вызове метода нужно получать новый экземпляр prototype bean. Для этого можно использовать метод с аннотацией @Lookup, который генерирует новый экземпляр bean при каждом вызове метода.
  • Динамическое создание объектов: @Lookup позволяет динамически создавать новые объекты каждый раз, когда это необходимо, вместо того, чтобы использовать один и тот же объект каждый раз.
  • Ручное управление жизненным циклом bean: Используя @Lookup, можно управлять жизненным циклом bean вручную, например, создавать объекты только в тех случаях, когда они нужны, или удалять объекты вручную после использования.

Однако, при использовании @Lookup также есть некоторые проблемы:

  • Необходимость в абстрактных классах или интерфейсах: чтобы использовать @Lookup, необходимо иметь абстрактный класс или интерфейс, который определяет метод, возвращающий нужный bean. Это может быть неудобно, если вы не хотите создавать лишние классы только для того, чтобы использовать @Lookup.
  • Снижение производительности: каждый вызов метода с аннотацией @Lookup приводит к созданию нового экземпляра bean, что может привести к снижению производительности приложения.
  • Ограничения использования: @Lookup не может использоваться, если содержащий класс инициализируется через @Bean. Кроме того, метод с аннотацией @Lookup не может быть статическим или final.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Можно ли вставить бин в статическое поле? Почему?

A

Spring не позволяет внедрять бины напрямую в статические поля. Это связано с тем, что когда загрузчик классов загружает статические значения, контекст Spring еще не загружен.
Если мы попытаемся вставить бин напрямую в статическое поле, как это было описано в предыдущем ответе, то контейнер Spring не сможет управлять этим бином, потому что в момент загрузки класса контекст Spring еще не загружен.
Чтобы исправить это, можно создать нестатический сеттер-метод с @Autowired:
~~~
private static OrderItemService orderItemService;
@Autowired
public void setOrderItemService(OrderItemService orderItemService) {
TestDataInit.orderItemService = orderItemService;
}
~~~

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Расскажите про аннотации @Primary и @Qualifier

A

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

@Primary тоже используется, чтобы отдавать предпочтение бину, когда есть несколько бинов одного типа, но в ней нельзя задать имя бина, она определяет значение по умолчанию.
Аннотация @Primary используется для пометки бина, который будет использоваться по умолчанию в случае, если есть несколько кандидатов на внедрение зависимости одного типа. Например, если у нас есть два бина с одним и тем же типом, то Spring будет использовать тот, который помечен аннотацией @Primary.

при использовании как аннотации @Primary, так и @Qualifier, приоритет будет у @Qualifier. Таким образом, если присутствуют обе аннотации, то Sprin

Например, если есть два бина типа UserService, один помечен аннотацией @Primary, а второй помечен аннотацией @Qualifier(“adminUserService”), то при запросе бина UserService Spring выберет бин с аннотацией @Primary, несмотря на наличие аннотации @Qualifier.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Как заинжектить примитив?

A

Внедрить в поле примитив можно с помощью аннотации @Value на уровне параметров поля или конструктора/метода.

Нам понадобится файл свойств (*.properties), чтобы определить значения, которые мы хотим внедрить аннотацией @Value. Сначала в нашем классе конфигурации нам нужно указать аннотацию @PropertySource с именем файла свойств.
~~~
@Component
public class MyComponent {
@Value(“10”)
private int myInt;
~~~

@Value(“${some.key}”)
public String stringWithDefaultValue;

В эту переменную будет внедрена строка, например, из property или из view.

Кроме того, для внедрения значений можно использовать язык SpEL (Spring Expression Language(
С помощью SpEL можно выполнить арифметические операции, сравнения и логические операции над значениями.
SpEL поддерживает использование переменных, функций и операторов для создания более сложных выражений.).

  • @Value with Map
  • @Value with Constructor
    ~~~
    @Autowired
    public MyComponent(@Value(“${my.property}”) String value) {
    this.value = value;
    }
    ~~~
  • @Value with Setter
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Как заинжектить коллекцию?

A

Если внедряемый объект массив, коллекция или map с дженериком, то, используя аннотацию @Autowired, Spring внедрит все бины, подходящие по типу в этот массив (или другую структуру данных). В случае с map ключом будет имя бина.

Используя аннотацию @Qualifier можно настроить тип искомого бина.

Использование аннотации @Inject:

Использование аннотации @Resource:

можно собрать все бины одного типа(например все имплементации какого-либо интерфейса), находящиеся в контейнере, и внедрить их в коллекцию или массив, используя аннотацию @Autowired с квалификатором @Qualifier, указывающим на нужный тип бина. Например, чтобы собрать все бины типа MyBean в список, можно сделать следующее:

@Autowired
@Qualifier("myBean")
private List<MyBean> myBeans;

Бины могут быть упорядочены, если вставляются в списки (не Set или Map) или массивы. Поддерживаются как аннотация @Order, так и интерфейс Ordered.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Расскажите про аннотацию @Conditional

A

Аннотация @Conditional позволяет определить условия, которые необходимо учитывать при создании бинов и конфигураций в Spring. Если условие выполняется, то бин или конфигурация будет создана, в противном случае - нет.

Для определения условий необходимо создать классы, реализующие интерфейс Condition, и переопределить его метод matches(). В этом методе можно прописать любую логику проверки условий.

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

Класс, помеченный аннотацией @Configuration, может быть также помечен и аннотацией @Conditional. В этом случае, все методы, помеченные аннотациями @Bean, @Import и @ComponentScan, будут создаваться только в том случае, если условие, указанное в аннотации @Conditional, выполняется.

Таким образом, использование аннотации @Conditional позволяет гибко настраивать создание бинов и конфигураций в Spring, в зависимости от различных условий.

@Configuration
public class AppConfig {
  @Bean(name = "myBean")
  @Conditional(MyCondition.class)
  public MyBean myBean() {
    return new MyBean();
  }
}
public class MyCondition implements Condition {
  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // Здесь можно реализовать логику проверки условия.
    // Например, проверка наличия системного свойства с именем "my.property"
    return System.getProperty("my.property") != null;
  }
}
public class MyBean {
  // Код бина
}

В данном примере, при запуске приложения, перед созданием бина “myBean”, будет вызван метод matches() класса MyCondition, где можно проверить выполнение нужного условия. Если условие выполнено, то бин будет создан, иначе - нет.

Класс MyCondition реализует интерфейс Condition и переопределяет его метод matches(). В данном примере, условие проверяет наличие системного свойства с именем “my.property”. Если такое свойство существует, то условие считается выполненным, и бин будет создан. Если свойство не существует, то бин не будет создан.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Расскажите про аннотацию @ComponentScan

A

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

Используется вместе с аннотацией @Configuration.
@ComponentScan без аргументов указывает Spring по умолчанию сканировать текущий пакет и все его подпакеты. Текущий пакет - тот, в котором находится файл конфигурации с этой самой аннотацией @ComponentScan. В данном случае в контейнер попадут:
* бин конфигурационного класса;
* бины, объявленные в конфигурационном классе с помощью @Bean;
* все бины из пакета и его подпакетов.

Аннотация @SpringBootApplication включает в себя аннотации @ComponentScan, @SpringBootConfiguration и @EnableAutoConfiguration, но это не мешает разместить её ещё раз отдельно для указания конкретного пакета.

Если указать с атрибутом exludeFilters, то это позволит использовать фильтр и исключать ненужные классы из процесса сканирования.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Расскажите про аннотацию @Profile

A

Профили — это ключевая особенность Spring Framework, позволяющая нам относить наши бины к разным профилям (логическим группам), например, dev, test, prod.
Профиль (Profile) - это набор конфигурационных параметров, который позволяет настроить приложение для разных сценариев использования, таких как разработка, тестирование или продакшн.

С помощью аннотации @Profile мы можем помечать классы или методы конфигурации, которые должны быть активны только для определенных профилей. Например:
~~~
@Configuration
@Profile(“production”)
public class ProductionConfig {
// …
}

@Configuration
@Profile(“development”)
public class DevelopmentConfig {
// …
}
~~~
В данном примере, классы ProductionConfig и DevelopmentConfig помечены аннотацией @Profile соответствующими профилями. Когда мы запускаем приложение, Spring Framework автоматически выбирает только те конфигурации, которые соответствуют текущему профилю.

Чтобы указать текущий профиль, необходимо использовать параметр spring.profiles.active при запуске приложения. Например, для запуска приложения в профиле “production” необходимо выполнить следующую команду:
java -jar myapp.jar --spring.profiles.active=production

Также можно указывать несколько профилей через запятую:

java -jar myapp.jar --spring.profiles.active=production,db

Аннотация @Profile может быть использована не только для классов конфигурации, но и для других Spring компонентов, таких как бины, контроллеры и т.д.

В качестве быстрого обозначения имена профилей также могут начинаться с оператора NOT, например «!dev», чтобы исключить их из профиля. В приведенном ниже примере компонент активируется, только если профиль «dev» не активен:

@Component
@Profile(\"!dev\")
public class DevDatasourceConfig

По умолчанию, если профиль бина не определен, то он относится к профилю “default”.

Spring также предоставляет способ установить профиль по умолчанию, когда другой профиль не активен, используя свойство «spring.profiles.default».

Мы можем активировать разные профили в разных средах, чтобы загрузить только те бины, которые нам нужны.
1) Ее можно применять на уровне класса или метода.
2) Принимает в качестве аргумента имя одного или нескольких профилей.
3) Ее можно ставить на @Configuration и @Component классы

(Фактически реализована на @Conditional)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

Расскажите про ApplicationContext и BeanFactory, чем отличаются? В каких случаях что стоит использовать?

A

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

BeanFactory легче по сравнению с ApplicationContext.

ApplicationContext
ApplicationContext является наследником BeanFactory и полностью реализует его функционал, добавляя больше специфических enterprise-функций.

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

  • 1) ApplicationContext загружает все бины при запуске, а BeanFactory - по требованию.
  • 2) ApplicationContext расширяет BeanFactory и предоставляет функции, которые подходят для корпоративных приложений: a. поддержка внедрения зависимостей на основе аннотаций; b. удобный доступ к MessageSource (для использования в интернационализации); c. публикация ApplicationEvent - для бинов, реализующих интерфейс ApplicationListener, с помощью интерфейса ApplicationEventPublisher; d. простая интеграция с функциями Spring AOP.
  • 3) ApplicationContext поддерживает автоматическую регистрацию BeanPostProcessor и BeanFactoryPostProcessor. Поэтому всегда желательно использовать ApplicationContext, потому что Spring 2.0 (и выше) интенсивно использует BeanPostProcessor.
  • 4) ApplicationContext поддерживает практически все типы scope для бинов, а BeanFactory поддерживает только два - Singleton и Prototype.
  • 5) В BeanFactory не будут работать транзакции и Spring AOP. Это может привести к путанице, потому что конфигурация с виду будет корректной
  • ApplicationContext поддерживает внедрение зависимостей на основе аннотаций, а BeanFactory нет

Подытожив, можно сказать, что для небольших приложений, где достаточно базовых функции подойдет BeanFactory, для более сложных и навороченных, требующих дополнительный функционал, нужен ApplicationContext

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

BeanFactoryPostProcessor

A

BeanFactoryPostProcessor. Этот интерфейс позволяет изменять метаданные бинов до их создания. Как правило, BeanFactoryPostProcessor используется для изменения настроек фабрики бинов до того, как она начнет создавать объекты бинов.

Интерфейс BeanFactoryPostProcessor имеет один метод:

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException: Вызывается Spring до создания любых бинов в контексте приложения. ConfigurableListableBeanFactory представляет собой фабрику бинов, которая позволяет изменять метаданные бинов.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

BeanPostProcessor

A

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

Интерфейс BeanPostProcessor имеет два метода:

postProcessBeforeInitialization(Object bean, String beanName): Вызывается Spring для всех бинов, после их создания, но перед инициализацией (вызовом метода afterPropertiesSet() для бинов, реализующих InitializingBean, или метода, указанного атрибутом init-method в XML-конфигурации).
postProcessAfterInitialization(Object bean, String beanName): Вызывается Spring для всех бинов, после их инициализации (вызовом метода afterPropertiesSet() для бинов, реализующих InitializingBean, или метода, указанного атрибутом init-method в XML-конфигурации).
BeanPostProcessor часто используется для внедрения дополнительной функциональности в бины, например, для логирования, проверки настроек, кэширования или автоматической регистрации бинов.

Некоторые классы и интерфейсы, связанные с BeanPostProcessor:

BeanPostProcessor: Интерфейс, определяющий методы postProcessBeforeInitialization и postProcessAfterInitialization.
BeanFactoryPostProcessor: Интерфейс, определяющий метод postProcessBeanFactory, который позволяет изменять метаданные бинов до их создания.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q

чем создание бина отличается от инициализации

A

Создание бина и его инициализация - это два этапа жизненного цикла бина в Spring-приложении, которые имеют свои различия.

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

Инициализация бина - это процесс подготовки бина к использованию после создания. На этом этапе, контейнер Spring выполняет ряд дополнительных действий, которые могут включать в себя вызов методов инициализации бина, настройку свойств, регистрацию бина в системе управления жизненным циклом, и т.д. В этом процессе также могут быть использованы BeanPostProcessor и другие механизмы, которые позволяют внести дополнительную логику до или после инициализации бина.

30
Q

Расскажите про жизненный цикл бина.

A

Bean Definition (метаинформация) ->
-> Bean Factory Post Processor ->
-> Sorted Bean Definition (Сначала создаются независимые бины, потом, те, которым нужны другие бины) ->
-> forEach(Bean Definition) : Bean constructor -> Setter ->
-> BeanPostProceccor @BeforeInitialization ->
-> @PostConstruct ->
-> BeanPostProceccor @AfterInitialization ->
-> BEAN IS READY ->
-> @PreDestroy() вызывается только на синглтонах

1) Парсирование конфигурации и создание BeanDefinition
2) Настройка созданных BeanDifinition
3) Создание кастомных FactoryBean (только для XML-конфигурации)
4) Создание экземпляров бинов
5) Настройка созданных бинов
6) Бины готовы к использованию
7) Закрытие контекста
* Парсирование конфигурации и создание BeanDefinition. Когда приложение запускается, Spring считывает конфигурацию и парсит ее для определения бинов, которые необходимо создать. Для каждого бина создается объект BeanDefinition, который содержит информацию о классе, зависимостях и других настройках.
* Настройка созданных BeanDefinition. Затем Spring настраивает созданные BeanDefinition, выполняя валидацию, проверку зависимостей и резолвинг бинов.
* Создание кастомных FactoryBean (только для XML-конфигурации). Если необходимо создать бин, для которого необходим сложный процесс инициализации, можно создать кастомный FactoryBean. Он содержит логику создания бина и может быть определен в XML-конфигурации.
* Создание экземпляров бинов. Затем Spring создает экземпляры бинов, используя информацию из BeanDefinition.
* Настройка созданных бинов. Когда бины созданы, Spring применяет к ним настройки, определенные в конфигурации, такие как инъекция зависимостей, конфигурация бинов и т.д.
* Бины готовы к использованию. Когда все бины настроены, они готовы к использованию.
* Закрытие контекста. Приложение завершает работу, и контекст Spring закрывается, вызывая деструкторы бинов и освобождая ресурсы.

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

1.Bean Definitions после формирования попадают в Bean Factory Post Processors, они нужны в свою очередь для того, чтобы подкрутить Bean Definitions, например внедрить properties
2. Далее необходимо отсортировать Bean Definitions, таким образом, чтобы осуществлялась зависимость между ними, потому что один Bean Definition не может быть инициализирован, если у него есть зависимость, от другого, который еще моет быть не инициализирован

Создаются экземпляры бинов через BeanFactory на основе ранее созданных BeanDefinition.
3. Далее с помощью цикла for each последовательно начинается инициализация бина с помощью Bean Definitions:
Сначала BeanFactory из коллекции Map с объектами BeanDefinition достаёт те из них, из которых создаёт все BeanPostProcessor-ы, необходимые для настройки обычных бинов.

После того, как все Bean Definitions были инициализированы, начинается процесс создания самих бинов:
- Вызывается конструктор у Bean Definitions
- Вызываются сеттеры

Создание экземпляра бина (как мы уже обсуждали выше).
Если бин реализует интерфейс BeanNameAware, то вызывается метод setBeanName(String name), передавая ему имя бина.
Если бин реализует интерфейс BeanClassLoaderAware, то вызывается метод setBeanClassLoader(ClassLoader classLoader), передавая ему загрузчик классов бина.
Если бин реализует интерфейс BeanFactoryAware, то вызывается метод setBeanFactory(BeanFactory beanFactory), передавая ему фабрику бинов.
Если у бина есть какие-либо BeanPostProcessors, то вызывается их метод postProcessBeforeInitialization(Object bean, String beanName). Этот метод может модифицировать бин перед его инициализацией.
Если у бина есть метод, аннотированный аннотацией @PostConstruct, то этот метод вызывается.
Если у бина есть метод, реализующий интерфейс InitializingBean, то вызывается метод afterPropertiesSet().
Если у бина есть пользовательский метод инициализации, то он вызывается (если он определен в XML-конфигурации, то это будет метод, указанный в атрибуте init-method).
Если у бина есть какие-либо BeanPostProcessors, то вызывается их метод postProcessAfterInitialization(Object bean, String beanName). Этот метод может модифицировать бин после его инициализации.
После завершения этапа инициализации бина, он становится полностью инициализированным и готов к использованию в контексте приложения.
если синглтон, то остается в контейнере, если прототайп то выдается по месту запроса

Когда контекст закрывается (метод close() из ApplicationContext), бин уничтожается.

Если в бине есть метод, аннотированный @PreDestroy, то перед уничтожением вызовется этот метод.

Если бин имплементирует DisposibleBean, то Spring вызовет метод destroy() - не рекомендуется к использованию как устаревший.

Если в аннотации @Bean определен метод destroyMethod, то будет вызван и он.

31
Q

Жизненный цикл бинов

A

1) Парсирование конфигурации и создание BeanDefinition

Парсирование конфигурации и создание BeanDefinition. Когда приложение запускается, Spring считывает конфигурацию и парсит ее для определения бинов, которые необходимо создать. Для каждого бина создается объект BeanDefinition, который содержит информацию о классе, зависимостях и других настройках.

Объект BeanDefinition - это набор метаданных будущего бина, макет, по которому нужно будет создавать бин.

BeanDefinition содержит следующие метаданные:

1) Имя класса с указанием пакета

2) Элементы поведенческой конфигурации бина (scope, обратные вызовы ЖЦ и тд.)

3) Зависимости

4) Другие параметры конфигурации для установки в новом бине (размер пула, кол-во соединений и тд.)

  1. Xml конфигурация — ClassPathXmlApplicationContext(“context.xml”)
  2. Конфигурация через аннотации с указанием пакета для сканирования — AnnotationConfigApplicationContext(“package.name”)
  3. Конфигурация через аннотации с указанием класса (или массива классов) помеченного аннотацией @Configuration -AnnotationConfigApplicationContext(JavaConfig.class). Этот способ конфигурации называется — JavaConfig.
  4. Groovy конфигурация — GenericGroovyApplicationContext(“context.groovy”)
    Если заглянуть внутрь AnnotationConfigApplicationContext, то можно увидеть два поля. private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner;

ClassPathBeanDefinitionScanner сканирует указанный пакет на наличие классов помеченных аннотацией @Component (или её алиаса). Найденные классы парсируются и для них создаются BeanDefinition. Чтобы было запущено сканирование, в конфигурации должен быть указан пакет для сканирования @ComponentScan({“package.name”}). AnnotatedBeanDefinitionReader работает в несколько этапов.
1. Первый этап — это регистрация всех @Configuration для дальнейшего парсирования. Если в конфигурации используются Conditional, то будут зарегистрированы только те конфигурации, для которых Condition вернет true.
2. Второй этап — это регистрация BeanDefinitionRegistryPostProcessor, который при помощи класса ConfigurationClassPostProcessor парсирует JavaConfig и создает BeanDefinition.

Цель первого этапа — это создание всех BeanDefinition. BeanDefinition — это специальный интерфейс, через который можно получить доступ к метаданным будущего бина. В зависимости от того, какая у вас конфигурация, будет использоваться тот или иной механизм парсирования конфигурации. BeanDefinition – это объект, который хранит в себе информацию о бине. Сюда входит: из какого класса бин надо создать, scope, установлена ли ленивая инициализация, нужно ли перед данным бином инициализировать другой, init и destroy методы, зависимости.
Все полученные
BeanDefinition’ы складываются в ConcurrentHashMap, в которой ключом является имя бина, а объект - сам BeanDefinition. При старте приложения, в IoC контейнер попадут бины, которые имеют scope Singleton (устанавливается по-умолчанию), остальные же создаются, тогда когда они нужны.

2) Настройка созданных BeanDefinition

Есть возможность повлиять на бины до их создания, иначе говоря мы имеем доступ к метаданным класса. Для этого существует специальный интерфейс BeanFactoryPostProcessor, реализовав который, мы получаем доступ к созданным BeanDefinition и можем их изменять. В нем один метод.
Метод postProcessBeanFactory принимает параметром ConfigurableListableBeanFactory. Данная фабрика содержит много полезных методов, в том числе getBeanDefinitionNames, через который мы можем получить все BeanDefinitionNames, а уже потом по конкретному имени получить BeanDefinition для дальнейшей обработки метаданных.
Разберем одну из родных реализаций интерфейса BeanFactoryPostProcessor. Обычно, настройки подключения к базе данных выносятся в отдельный property файл, потом при помощи PropertySourcesPlaceholderConfigurer они загружаются и делается inject этих значений в нужное поле. Так как inject делается по ключу, то до создания экземпляра бина нужно заменить этот ключ на само значение из property файла. Эта замена происходит в классе, который реализует интерфейс BeanFactoryPostProcessor. Название этого класса — PropertySourcesPlaceholderConfigurer. Он должен быть объявлен как static
@Bean
public static PropertySourcesPlaceholderConfigurer configurer() {
return new PropertySourcesPlaceholderConfigurer();
}
3) Создание кастомных FactoryBean
FactoryBean — это generic интерфейс, которому можно делегировать процесс создания бинов типа . В те времена, когда конфигурация была исключительно в xml, разработчикам был необходим механизм с помощью которого они бы могли управлять процессом создания бинов. Именно для этого и был сделан этот интерфейс.
Создадим фабрику которая будет отвечать за создание всех бинов типа — Color.
public class ColorFactory implements FactoryBean<Color> {
@Override
public Color getObject() throws Exception {
Random random = new Random();
Color color = new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
return color;
}</Color>

@Override 
public Class<?> getObjectType() {
    return Color.class;
}

@Override
public boolean isSingleton() {
    return false;
} } Теперь создание бина типа Color.class будет делегироваться ColorFactory, у которого при каждом создании нового бина будет вызываться метод getObject. Для тех кто пользуется JavaConfig, этот интерфейс будет абсолютно бесполезен.

4) Создание экземпляров бинов

Сначала BeanFactory из коллекции Map с объектами BeanDefinition достаёт те из них, из которых создает все BeanPostProcessor-ы (Инфраструктурные бины), необходимые для настройки обычных бинов.
Создаются экземпляры бинов через BeanFactory на основе ранее созданных BeanDefinition.
Созданием экземпляров бинов занимается BeanFactory на основе ранее созданных BeanDefinition. Из Map<BeanName, BeanDefinition> получаем Map<BeanName, Bean>
Создание бинов может делегироваться кастомным FactoryBean. О их создании читай выше.

5) Настройка созданных бинов

На данном этапе бины уже созданы, мы можем лишь их донастроить.
Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки наших бинов до того, как они попадут в контейнер.
BeanPostProcessor (BPP) в Spring Framework позволяет донастраивать бины (bean) до и после их инициализации, что может быть полезно для решения различных задач, например:

  • Манипуляции с зависимостями. BPP позволяет изменять зависимости бинов до их инициализации, что может быть полезно, например, для решения проблемы циклических зависимостей между бинами.
  • Проксирование бинов. BPP может создавать прокси-объекты для бинов, что позволяет внедрять дополнительную логику, например, для реализации трассировки вызовов методов или кеширования результатов.
  • Внедрение значений. BPP может внедрять значения в поля бинов, которые не могут быть установлены при создании бина, например, значения из внешнего конфигурационного файла или из базы данных.
  • Прочие задачи. BPP может выполнять и другие задачи, например, обеспечивать поддержку многопоточности или выполнение дополнительных проверок валидации.

ApplicationContext автоматически обнаруживает любые бины с реализацией BeanPostProcessor и помечает их как “post-processors” для того, чтобы создать их определенным способом. Например, в Spring есть реализации BeanPostProcessor-ов, которые обрабатывают аннотации @Autowired, @Inject, @Value и @Resource.
Интерфейс несет в себе два метода: postProcessBeforeInitialization(Object bean, String beanName) и postProcessAfterInitialization(Object bean, String beanName). У обоих методов параметры абсолютно одинаковые. Разница только в порядке их вызова. Первый вызывается до init-метода, второй - после.
Как правило, BeanPostProcessor-ы, которые заполняют бины через маркерные интерфейсы или тому подобное, реализовывают метод postProcessBeforeInitialization (Object bean, String beanName), тогда как BeanPostProcessor-ы, которые оборачивают бины в прокси, обычно реализуют postProcessAfterInitialization (Object bean, String beanName).
Прокси — это класс-декорация над бином. Например, мы хотим добавить логику нашему бину, но джава-код уже скомпилирован, поэтому нам нужно на лету сгенерировать новый класс. Этим классом мы должны заменить оригинальный класс так, чтобы никто не заметил подмены.
Есть два варианта создания этого класса:
1. либо он должен наследоваться от оригинального класса (CGLIB) и переопределять его методы, добавляя нужную логику;
2. либо он должен имплементировать те же самые интерфейсы, что и первый класс (Dynamic Proxy).
По конвенции спринга, если какой-то из BeanPostProcessor-ов меняет что-то в классе, то он должен это делать на этапе postProcessAfterInitialization(). Таким образом мы уверены, что initMethod у данного бина, работает на оригинальный метод, до того, как на него накрутился прокси.

Хронология событий:

  1. Сначала сработает метод postProcessBeforeInitialization() всех имеющихся BeanPostProcessor-ов.
  2. Затем, при наличии, будет вызван метод, аннотированный @PostConstruct.
  3. Если бин имплементирует InitializingBean, то Spring вызовет метод afterPropertiesSet() - не рекомендуется к использованию как устаревший.
  4. При наличии, будет вызван метод, указанный в параметре initMethod аннотации @Bean.
  5. В конце бины пройдут через postProcessAfterInitialization (Object bean, String beanName). Именно на данном этапе создаются прокси стандартными BeanPostProcessor-ами. Затем отработают наши кастомные BeanPostProcessor-ы и применят нашу логику к прокси-объектам. После чего все бины окажутся в контейнере, который будет обязательно обновлен методом refresh().
  6. Но даже после этого мы можем донастроить наши бины ApplicationListener-ами.
  7. Теперь всё.

Таким образом, метод postProcessBeforeInitialization() предоставляет вам возможность настроить бин перед его инициализацией, а метод, аннотированный @PostConstruct, позволяет вам выполнить любые необходимые действия после его инициализации.

6. Бины созданы

Их можно получить с помощью метода ApplicationContext.getBean().

7. Закрытие контекста

Когда контекст закрывается (метод close() из ApplicationContext), бин уничтожается. Если в бине есть метод, аннотированный @PreDestroy, то перед уничтожением вызовется этот метод.
Если в аннотации @Bean определен метод destroyMethod, то будет вызван и он.

32
Q

Аннотация @PostConstruct

A

Spring вызывает методы, аннотированные @PostConstruct, только один раз, сразу после инициализации свойств компонента. За данную аннотацию отвечает один из BeanPostProcessor - ов.

Метод, с этой аннотацией:

1) Может иметь любой тип доступа

2) Может иметь любой тип возвращаемого значения (хотя это игнорируется спрингом)

3) Не должен принимать аргументы
4) Может быть static, но преимуществ от этого мало

Одним из примеров использования @PostConstruct является заполнение базы данных.

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

33
Q

Аннотация @PreDestroy

A

Spring вызывает методы, аннотированные этой аннотацией только один раз, перед тем, как Spring удаляет наш компонент из контекста.

Такие методы не могут быть static!

Целью этого метода, является освобождение ресурсов или выполнение любых задач очистки до уничтожения бина. Например закрытие соединения с БД.

Аннотация @PreDestroy используется в Spring для определения метода, который должен быть вызван перед уничтожением бина. Этот метод может содержать логику по очистке ресурсов, освобождению соединений с базой данных и т.д.

Когда Spring контекст закрывается, все бины, которые реализуют интерфейс DisposableBean, также получают сообщение об удалении и вызывают метод destroy(). Также вызывается метод, помеченный аннотацией @PreDestroy.

34
Q

Расскажите про скоупы бинов? Какой скоуп используется по умолчанию? Что
изменилось в пятом спринге?

A

Scope в Spring - это конфигурационный параметр, который определяет:
* жизненный цикл бина,
* возможное количество создаваемых бинов

Spring поддерживает следующие Scope для бинов:

  1. Singleton - по умолчанию. Каждый бин, созданный в этом Scope, представляет собой единственный экземпляр в контексте приложения(создается в момент поднятия контекста). Такой бин создается только один раз, когда Spring контекст создается, и все последующие запросы на этот бин возвращают тот же экземпляр.
  2. Prototype - контейнер Spring IoC создаёт новый экземпляр бина на каждый полученный запрос. IoC не хранит их в контексте и каждый новый Бин будет проходить через все BeanPostProcessor-ы, что может значительно снизить производительность. У бинов данного типа не возможно вызвать destroy() метод
  3. Request - для каждого запроса создается новый экземпляр бина. Бин будет существовать только в пределах текущего запроса, а затем будет уничтожен.
  4. Session - каждый HTTP-сеанс получает свой экземпляр бина. Бин будет существовать в течение всего сеанса и будет уничтожен, когда сеанс закончится.
  5. Application - бин будет существовать в течение всего жизненного цикла приложения.
  6. WebSocket - аналогично сеансу, но используется только в контексте веб-сокетов.

Scope по умолчанию - Singleton. То есть если вы не задаете Scope явно, то ваш бин будет создан с Scope Singleton.

В Spring 5 был добавлен новый Scope - Websocket, который позволяет определять бины, существующие только в контексте веб-сокетов. Также был добавлен новый Scope - Application, который позволяет определять бины, существующие только в пределах всего жизненного цикла приложения. удалили GlobalSession - аналогично Session, но используется только в контексте портлетов.

35
Q

Что такое АОП? Как реализовано в спринге?

A

Аспектно-ориентированное программирование (АОП) — это парадигма программирования, целью которой является повышение модульности за счет разделения междисциплинарных задач. Это достигается путем добавления дополнительного поведения к существующему коду без изменения самого кода.
АОП предоставляет возможность реализации сквозной логики в одном месте - т.е. логики, которая применяется к множеству частей приложения - и обеспечения автоматического применения этой логики по всему приложению.
Аспект в АОП - это модуль или класс, реализующий сквозную функциональность. Аспект изменяет поведение остального кода, применяя совет в точках соединения, определённых некоторым срезом.
Совет (advice) – дополнительная логика — код, который должен быть вызван из точки соединения.
Пример использования АОП в spring - это управление транзакциями, фильтры или перехватчики
Точка соединения (join point) — место в выполняемой программе (вызов метода, создание объекта, обращение к переменной), где следует применить совет;
Срез (pointcut) — набор точек соединения.
Подход Spring к АОП заключается в создании “динамических прокси” для целевых объектов и “привязывании” объектов к конфигурированному совету для выполнения сквозной логики.
Есть два варианта создания прокси-класса:
1. либо он должен наследоваться от оригинального класса (CGLIB) и переопределять его методы, добавляя нужную логику;
2. либо он должен имплементировать те же самые интерфейсы, что и первый класс (Dynamic Proxy).

Вот первый варинат называется cglib, который является хуже, чем второй вариант dynamic proxy, поскольку дело не только в хуже производительности, а в ограничениях при этом варианте, мы не от любого класса можем наследоваться, если final классы, есть final методы, поэтому предпочтительнее идти, да и сам спринг предпочитает идти через dynamic proxy, идти через интерфейсы. Spring АОП, @Transactional работают через dynamic proxy. Если Spring’у нужно сделать proxy на какой-то объект, то он смотрит есть ли у оригинального объекта имплементация интерфейсов, если да, то он идет через dynamic proxy, если нет, то через cglib

36
Q

AOP-прокси в Spring Framework

A

AOP-прокси в Spring Framework представляют собой объекты, которые оборачивают реальные объекты и позволяют добавлять дополнительную логику в методы этих объектов.

Для создания AOP-прокси Spring Framework использует два подхода: JDK-динамический прокси и CGLIB-прокси.

JDK-динамический прокси создается путем генерации класса-прокси на основе интерфейса, реализуемого оригинальным объектом. JDK-динамический прокси позволяет перехватывать вызовы методов только на интерфейсном уровне. Если оригинальный объект не реализует интерфейс, то для создания прокси используется CGLIB-прокси.

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

Выбор между JDK-динамическим прокси и CGLIB-прокси зависит от типа оригинального объекта, а также от настроек Spring Framework. Обычно Spring Framework использует JDK-динамический прокси по умолчанию, но если оригинальный объект не реализует интерфейс или настройки Spring Framework указывают на использование CGLIB-прокси, то будет использован CGLIB-прокси.

37
Q

Отличие CGLIB-прокси и JDK-динамические прокси

A

CGLIB-прокси и JDK-динамические прокси являются двумя основными типами прокси, используемыми в Spring Framework.

JDK-динамические прокси являются прокси, создаваемыми на основе интерфейса целевого объекта. Они используются в том случае, если целевой объект реализует хотя бы один интерфейс. Для создания динамического прокси используется стандартная библиотека Java, и это означает, что вы не должны добавлять никаких дополнительных зависимостей к вашему проекту.

CGLIB-прокси, с другой стороны, используются, когда целевой объект не реализует интерфейс, и создаются с помощью библиотеки CGLIB, которая генерирует новый класс, наследующийся от целевого класса. Этот новый класс затем используется для создания прокси.

Разница между двумя типами прокси заключается в том, что JDK-динамические прокси работают на уровне интерфейса, в то время как CGLIB-прокси работают на уровне класса. Это означает, что если целевой объект реализует интерфейс, Spring Framework будет использовать JDK-динамический прокси, а если он не реализует интерфейс, Spring Framework будет использовать CGLIB-прокси.

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

38
Q

Как Spring Framework использует AOP-прокси для управления транзакциями?

A

AOP-прокси -

Когда вы помечаете методы в классе сервиса аннотацией @Transactional, Spring создает прокси-объект для этого класса, который реализует те же интерфейсы, что и оригинальный объект. Данный прокси-объект встраивается между вызывающим кодом и объектом-сервиса. Когда вызывающий код вызывает метод сервиса, он на самом деле вызывает метод на прокси-объекте. Этот прокси-объект, в свою очередь, обрабатывает транзакции перед вызовом метода сервиса и после него.

Прокси-объект создается с помощью специального фабричного метода, который определен в конфигурации транзакций. Когда создается прокси-объект, Spring создает цепочку советов (advices), которые будут применяться к вызову метода сервиса. В случае транзакций, эта цепочка советов будет состоять из двух советов: “до” и “после” выполнения метода.

Совет “до” выполняется перед вызовом метода сервиса и открывает транзакцию. Совет “после” выполняется после выполнения метода и закрывает транзакцию. Если метод завершается успешно, то транзакция коммитится, иначе транзакция откатывается.

Важно отметить, что прокси-объекты Spring создает только для тех классов, которые реализуют интерфейсы. Если класс сервиса не реализует интерфейс, то Spring использует CGLIB для создания прокси-объекта. Также стоит учитывать, что прокси-объект создается только для тех методов, которые помечены аннотацией @Transactional. Если метод не помечен этой аннотацией, то он будет вызван напрямую на оригинальном объекте без управления транзакцией.

39
Q

Spring Framework Transaction Manager?

A

В Spring работа с транзакциями осуществляется через интерфейс

Transaction Manager. Этот интерфейс предоставляет методы для начала и завершения транзакций, отката транзакций, установки параметров транзакций и т.д. Он определяет единый API для управления транзакциями, независимо от используемого конкретного провайдера транзакций (например, JTA или JDBC).Он также обеспечивает возможность настройки различных параметров транзакций, таких как уровень изоляции, управление временем жизни транзакций и т.д.

При настройке Spring-приложения необходимо настроить конкретный Transaction Manager, который будет использоваться при работе с транзакциями.

Transaction Manager может выполнять декларативное управление транзакциями с помощью аннотации @Transactional или программное управление транзакциями с помощью программного API.(TransactionTemplate(Основная идея заключается в том, что разработчик должен предоставлять код, который должен быть выполнен внутри транзакции, а TransactionTemplate позаботится об управлении транзакциями. Если транзакция завершается успешно, TransactionCallback возвращает результат, если же произошла ошибка и транзакция откатывается, исключение будет выброшено.))

Для настройки конкретного Transaction Manager в Spring необходимо выполнить следующие шаги:

  • Для включения возможности управления транзакциями нужно разместить аннотацию @EnableTransactionManagement(proxyTargetClass true = CGLIB-прокси(class based), false = JDK-динамические прокси(interface based)) у класса-конфигурации @Configuration.
  • Определить Transaction Manager в конфигурационном файле Spring, указав его тип (например, DataSourceTransactionManager) и настроив его свойства, такие как источник данных, имя транзакции и т.д.
  • Настроить уровень изоляции транзакций, уровень управления транзакциями и другие параметры в зависимости от требований приложения.
  • Применить Transaction Manager к нужным слоям приложения, помечая их аннотацией @Transactional или вызывая методы TransactionTemplate.
  • Обрабатывать исключения в транзакциях, используя соответствующие методы TransactionManager, такие как setRollbackOnly().

Spring Framework предоставляет механизм управления транзакциями, который позволяет управлять транзакциями в приложении, используя декларативный подход. Этот механизм основывается на использовании аннотации @Transactional.

Аннотация @Transactional указывает, что метод должен выполняться в контексте транзакции. Это означает, что если метод выполняется успешно, изменения, внесенные в базу данных в рамках транзакции, будут сохранены, а если метод завершается неудачно, транзакция будет отменена, и все изменения будут отменены.

Вот некоторые важные моменты, связанные с использованием аннотации @Transactional:

Аннотация @Transactional может быть применена как к методам, так и к классам.

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

По умолчанию, @Transactional использует уровень изоляции READ_COMMITTED, который означает, что изменения, внесенные в рамках транзакции, не будут видны другим транзакциям до ее фиксации.

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

Аннотация @Transactional может принимать ряд параметров, таких как propagation, isolation, timeout и т.д., которые позволяют настроить поведение транзакций.

Параметр propagation определяет, как будет взаимодействовать метод с текущей транзакцией. Например, если установить значение REQUIRES_NEW, метод будет выполняться в новой транзакции.

Параметр isolation определяет уровень изоляции транзакции.

Если метод, помеченный @Transactional, выбрасывает исключение, транзакция будет отменена, и все изменения, внесенные в рамках транзакции, будут отменены.

Если метод, помеченный @Transactional, не выбрасывает исключение, транзакция будет зафиксирована, и все изменения, внесенные в рамках транзакции, будут сохранены.

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

40
Q

параметры @Transactional.

A

У @Transactional есть ряд параметров:

@Transactional (isolation=Isolation.READ_COMMITTED) - уровень изоляции.
TransactionDefinition.ISOLATION_DEFAULT
Стандартный уровень изоляции лежащего в основе хранилища данных
TransactionDefinition.ISOLATION_READ UNCOММITTED
Самый низкий уровень изоляции; это с трудом можно назвать транзакцией, поскольку при таком уровне разрешено видеть данные, модифицированные другими незафиксированными транзакциями
TransactionDefinition.ISOLATION_READ СОММIТТЕD
Стандартный уровень изоляции в большинствебаз данных; он гарантирует, что другие транзакции не имеют возможности читать данные, которые не были зафиксированы текущей транзакцией . Однако данные, прочитанные одной транзакцией, могут быть обновлены другими транзакциями
TransactionDefinition.ISOLATION_REPEATAВLE READ

Более строгий уровень , чем ISOLAТION READ_СОММIТЕD он гарантирует, что после выборки - данных можно произвести выборку, по крайней мере, того же набора данных снова. Если другие транзакции вставили новые данные, эти новые данные могут быть извлечены
TransactionDefinition.ISOLATION_SERIALIZAВLE
Наиболее дорогостоящий и надежный уровень изоляции; все транзакции трактуются как выполняемые последовательно, друг за другом

@Transactional(timeout=60) - По умолчанию используется таймаут, установленный по умолчанию для базовой транзакционной системы. Сообщает TransactionManager-у о продолжительности времени, чтобы дождаться простоя транзакции, прежде чем принять решение об откате не отвечающих транзакций.

Этот атрибут отвечает на вопрос: должна ли быть запущена новая или приостановлена выполняющаяся транзакция или должен ли метод выполняться в контексте транзакции
@Transactional(propagation=Propagation.REQUIRED) - (Если не указано, распространяющееся поведение по умолчанию — REQUIRED.) Указывает, что целевой метод не может работать без другой транзакции. Если до вызова этого метода уже была запущена транзакция, то метод будет работать в той же транзакции, если транзакции не было, то будет создана новая.
1) REQUIRED - по умолчанию, Поддерживает транзакцию, если она уже существует.Если транзакций нет, начинается новая транзакция

2) REQUIRES_NEW - всегда создается новая транзакция при входе в метод. Ранее созданные транзакции приостанавливаются до окончания метода.

3) NESTED - корректно работает только с БД, которые умеют savepoints. При входе в метод в уже существующую транзакцию добавляет savepoint, который будет либо сохранен либо откачен. Все изменения подтвердятся только с подтверждением всей транзакции. Если транзакции нет - будет создана новая.

4) MANDATORY - всегда используется существующая транзакция, exception если транзакции нет.
5) SUPPORTS - будет использовать текущую транзакцию, если она есть, без транзакции если ее нет
6) NOT_SUPPORTED - при входе в метод текущая транзакция, если она есть, будет приостановлена и будет выполняться без транзакции.
7) NEVER - запрещает использовать в контексте транзакции. При наличии транзакции exception.

Остальные атрибуты:
* rollbackFor = Exception.class - если какой-либо метод выбрасывает указанное исключение, контейнер всегда откатывает текущую транзакцию. По умолчанию отлавливает RuntimeException
* noRollbackFor = Exception.class - указание того, что любое исключение, кроме заданных, должно приводить к откату транзакции.
* rollbackForClassName и noRollbackForClassName - для задания имен исключений в строковом виде.
* readOnly - разрешает только операции чтения.
* В свойстве transactionManager хранится ссылка на менеджер транзакций, определенный в конфигурации Spring.
* timeOut - По умолчанию используется таймаут, установленный по умолчанию для базовой транзакционной системы. Сообщает менеджеру tx о продолжительности времени, чтобы дождаться простоя tx, прежде чем принять решение об откате не отвечающих транзакций.
* isolation - уровень изолированности транзакций

41
Q

Что происходит при вызове метода save() при включённых транзакциях?

A

“Слой логики(Service) - лучшее место для @Transactional.
Помечая @Transactional класс @Service, то все его методы станут транзакционными. Так, при вызове, например, метода save() произойдет примерно следующее:
1.** Вначале мы имеем**:
❖ класс TransactionInterceptor, у которого вызывается метод invoke(…), внутри которого вызывается метод класса-родителя TransactionAspectSupport: invokeWithinTransaction(…), в рамках которого происходит магия транзакций.
❖ TransactionManager: решает, создавать ли новый EntityManager и/или транзакцию.
❖ EntityManager proxy: EntityManager - это интерфейс, и то, что внедряется в бин в слое DAO на самом деле не является реализацией EntityManager. В это поле внедряется EntityManager proxy, который будет перехватывать обращение к полю EntityManager и делегировать выполнение конкретному EntityManager в рантайме. Обычно EntityManager proxy представлен классом SharedEntityManagerInvocationHandler.”
“2. Transaction Interceptor
В TransactionInterceptor отработает код до работы метода save(), в котором будет определено, выполнить ли метод save() в пределах уже существующей транзакции БД или должна стартовать новая отдельная транзакция. TransactionInterceptor сам не содержит логики по принятию решения, решение начать новую транзакцию, если это нужно, делегируется TransactionManager. Грубо говоря, на данном этапе наш метод будет обёрнут в try-catch и будет добавлена логика до его вызова и после:
try {
transaction.begin(); // логика до
service.save();
transaction.commit(); // логика после
} catch(Exception ex) {
transaction.rollback();
throw ex;
}”
“3. TransactionManager
Менеджер транзакций должен предоставить ответ на два вопроса:
❖ Должен ли создаться новый EntityManager?
❖ Должна ли стартовать новая транзакция БД?
Решение принимается, основываясь на следующих фактах:
❖ выполняется ли хоть одна транзакция в текущий момент или нет;
❖ атрибута «propagation» в @Transactional.
Если TransactionManager решил создать новую транзакцию, тогда:
❖ Создается новый EntityManager;
❖ EntityManager «привязывается» к текущему потоку (Thread);
❖ «Получается» соединение из пула соединений БД;
❖ Соединение «привязывается» к текущему потоку.
И EntityManager и это соединение привязываются к текущему потоку, используя переменные ThreadLocal.”
“4. EntityManager proxy
Когда метод save() слоя Service делает вызов метода save() слоя DAO, внутри которого вызывается, например, entityManager.persist(), то не происходит вызов метода persist() напрямую у EntityManager, записанного в поле класса DAO. Вместо этого метод вызывает EntityManager proxy, который достает текущий EntityManager для нашего потока, и у него вызывается метод persist().

  1. Отрабатывает DAO-метод save().
    “6. TransactionInterceptor
    Отработает код после работы метода save(), а именно будет принято решение по коммиту/откату транзакции.

Кроме того, если мы в рамках одного метода сервиса обращаемся не только к методу save(), а к разным методам Service и DAO, то все они буду работать в рамках одной транзакции, которая оборачивает этот метод сервиса.”
Вся работа происходит через прокси-объекты разных классов. Представим, что у нас в классе сервиса только один метод с аннотацией @Transactional, а остальные нет. Если мы вызовем метод с @Transactional, из которого вызовем метод без @Transactional, то оба будут отработаны в рамках прокси и будут обернуты в нашу транзакционную логику. Однако, если мы вызовем метод без @Transactional, из которого вызовем метод с @Transactional, то они уже не будут работать в рамках прокси и не будут обернуты в нашу транзакционную логику.

42
Q

Вопросы про @Transactional

A

“Коротко: Spring создает прокси для всех классов, помеченных @Transactional (либо если любой из методов класса помечен этой аннотацией), что позволяет вводить транзакционную логику до и после вызываемого метода. При вызове такого метода происходит следующее:
- proxy, который создал Spring, создаёт persistence context (или соединение с базой),
- открывает в нём транзакцию и сохраняет всё это в контексте нити исполнения (натурально, в ThreadLocal).
- По мере надобности всё сохранённое достаётся и внедряется в бины.

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

Что произойдёт, если один метод с @Transactional вызовет другой метод с @Transactional?
Если это происходит в рамках одного сервиса, то второй транзакционный метод будет считаться частью первого, так как вызван у него изнутри, а так как спринг не знает о внутреннем вызове, то не создаст прокси для второго метода.

Что произойдёт, если один метод БЕЗ @Transactional вызовет другой метод с @Transactional?
Так как spring не знает о внутреннем вызове, то не создаст прокси для второго метода.
В таком случае транзакция для второго метода будет управляться напрямую от базы данных, а не через Spring. Это может привести к нежелательным эффектам, таким как несогласованность данных и блокировки в базе данных.

Будет ли транзакция откачена, если будет брошено исключение, которое указано в контракте метода?
Если в контракте описано это исключение, то она не откатится. Unchecked исключения в транзакционном методе можно ловить, а можно и не ловить.”

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

Однако, если выброшено исключение, которое не соответствует указанным условиям отката транзакции (т.е. не указано в атрибуте rollbackFor), то транзакция будет закоммичена. В этом случае изменения в базе данных, выполненные в рамках этой транзакции, сохранятся.

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

43
Q

Расскажите про аннотации @Controller и @RestController. Чем они отличаются?
Как вернуть ответ со своим статусом (например 213)?

A

Аннотации @Controller и @RestController в Spring Framework используются для создания контроллеров веб-приложений.

@Controller помечает класс как контроллер HTTP-запросов. @Controller обычно используется в сочетании с аннотацией @RequestMapping, используемой в методах обработки запросов. Это просто дочерняя аннотация аннотации @Component и позволяет автоматически определять классы при сканировании пакетов.
Методы в классе-контроллере обычно аннотируются другими аннотациями, такими как @RequestMapping или @GetMapping, чтобы указать, какой URL-адрес запроса должен быть обработан каждым методом.
~~~
@Controller
public class HomeController {

@RequestMapping(“/home”)
public String home() {
return “home”;
}
}
~~~

Аннотация @RestController была введена в Spring 4.0 для упрощения создания RESTful веб-сервисов. Это удобная аннотация, которая объединяет @Controller и @ResponseBody, что устраняет необходимость аннотировать каждый метод обработки запросов аннотацией @ResponseBody.
Аннотация @RestController, в свою очередь, является специализацией @Controller и используется для создания классов, которые будут обрабатывать REST-запросы и возвращать данные в формате JSON или XML. Методы в классе-контроллере с аннотацией @RestController не должны возвращать представления, как это делается в случае с @Controller.

@RestController
public class UserController {
 
   @GetMapping("/users")
   public List<User> getUsers() {
      return userRepository.findAll();
   }
}

Этот код определяет класс UserController как REST-контроллер и метод getUsers() как обработчик GET-запросов для URL “/users”. Метод возвращает список пользователей в формате JSON, который будет отправлен клиенту.

Рест-контроллеры напрямую возвращают объекты Java, которые Spring MVC будет удобно сериализовать в JSON / XML или любой другой формат, который пользователь запросил с помощью HttpMessageConverters.

Однако вы должны убедиться в двух вещах:
1)Имеются соответствующие сторонние библиотеки на пути к классам.
2) Отправлены правильные заголовки Accept или Content-Type с каждым запросом.

Таким образом, главное отличие между аннотациями @Controller и @RestController заключается в том, что @Controller используется для создания контроллеров, которые возвращают представления, а @RestController используется для создания контроллеров, которые возвращают данные в формате JSON или XML.

@ResponseBody сообщает контроллеру, что возвращаемый объект автоматически сериализуется в json или xml и передается обратно в объект HttpResponse. Контроллер использует Jackson message converter для конвертации входящих/исходящих данных. Как правило целевые данные представлены в json или xml.

Если метод контроллера не помечен аннотацией @ResponseBody, то по умолчанию Spring Framework ожидает, что метод контроллера будет возвращать имя представления, которое будет использоваться для создания HTML-страницы. Однако, если метод контроллера помечен аннотацией @ResponseBody, то Spring Framework автоматически преобразует возвращаемое значение метода в тело ответа HTTP, используя соответствующий конвертер сообщений.
~~~
@Controller
@RequestMapping(“/api”)
public class ApiController {

@GetMapping("/person")
@ResponseBody
public Person getPerson() {
    Person person = new Person("John", "Doe", 30);
    return person;
} } ~~~

ResponseEntity

Данный класс используется для формирования ответа HTTP с пользовательскими параметрами (заголовки, код статуса и тело ответа). ResponseEntity необходим, только если мы хотим кастомизировать ответ. Во всех остальных случаях достаточно использовать @ResponseBody.
Он обычно используется в контроллерах Spring для возвращения ответа клиенту. Класс ResponseEntity предоставляет большую гибкость в формировании ответа, чем просто возвращение тела ответа.

Например, мы можем использовать ResponseEntity для установки HTTP-статуса ответа, как показано ниже:

@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
    User user = userService.getUserById(id);
    if (user != null) {
        return new ResponseEntity<>(user, HttpStatus.OK);
    } else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

В этом примере, если пользователь с заданным идентификатором существует, мы возвращаем объект ResponseEntity со статусом OK и телом ответа, содержащим объект пользователя. Если пользователь не найден, мы возвращаем объект ResponseEntity со статусом NOT_FOUND и без тела ответа.

Если мы хотим использовать ResponseEntity, то просто должны вернуть его из метода, Spring позаботится обо всем остальном.

return ResponseEntity.status(213);

44
Q

HTTP-ответ, статус-код, заголовки и тело ответа.

A

HTTP-ответ - это ответ сервера на HTTP-запрос, который содержит статус-код, заголовки и тело ответа.

Статус-код HTTP-ответа указывает на результат обработки запроса сервером и имеет числовое значение, состоящее из трех цифр. Он указывает на тип ответа, например, успешный ответ (200), перенаправление (301), ошибка сервера (500) и т.д.

Заголовки HTTP-ответа содержат дополнительную информацию о запросе или ответе. Некоторые из наиболее распространенных заголовков HTTP-ответа включают “Content-Type”, который определяет тип содержимого ответа, “Cache-Control”, который указывает, как кэшировать ответ, и “Location”, который используется для перенаправления на другой URL.

Тело ответа HTTP содержит данные, которые отправляются клиенту в ответ на запрос. Тело ответа может быть в различных форматах, таких как HTML, JSON, XML и т.д.

Примеры HTTP-ответов:

Успешный HTTP-ответ с телом в формате JSON:
~~~
HTTP/1.1 200 OK
Content-Type: application/json

{
“name”: “John”,
“age”: 30,
“city”: “New York”
}
~~~

45
Q

Что такое ViewResolver?

A

ViewResolver - это интерфейс в Spring Framework, который позволяет настраивать разрешение и поиск представлений (views) для веб-приложений. Когда веб-контроллер возвращает строку в качестве имени представления, ViewResolver ищет соответствующий файл представления и возвращает объект представления, который может быть использован для отображения данных пользователю.

Например, если ваш веб-контроллер возвращает строку “hello” в качестве имени представления, ViewResolver может найти файл “hello.jsp” в папке представлений и вернуть объект InternalResourceView, который можно использовать для отображения данных пользователю.

наиболее распространенная из них - это InternalResourceViewResolver

ViewResolver в Spring Framework позволяет настроить, какие файлы представлений будут использоваться для отображения данных, возвращаемых из контроллера.

Когда контроллер возвращает имя представления, Spring ищет соответствующий файл представления на основе настроек, заданных в ViewResolver. Затем, найденный файл представления используется для создания HTML-страницы, которая будет отправлена клиенту в качестве ответа на запрос.

Таким образом, ViewResolver позволяет разделять логику контроллера и логику представления, что облегчает поддержку и разработку приложения.ViewResolver - это механизм в Spring Framework, который помогает найти и загрузить файлы представлений, когда контроллер возвращает имя представления.

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

ViewResolver позволяет контроллеру возвращать простое имя представления, а затем находит соответствующий файл представления и возвращает его в качестве ответа на запрос.

Например, если у вас есть контроллер, который возвращает имя представления “home”, ViewResolver попытается найти файл представления с именем “home” (например, home.jsp) и вернуть его в качестве ответа.

ViewResolver можно настроить для различных типов представлений (например, JSP, Thymeleaf, FreeMarker) и для различных путей поиска файлов представлений (например, внутри папки “WEB-INF/views/”).

ViewResolver в Spring Framework - это компонент, который отвечает за поиск и выбор подходящего представления (view) для возвращаемого контроллером модели (model).

ViewResolver - распознаватель представлений - это способ работы с представлениями(html-файлы), который поддерживает их распознавание на основе имени, возвращаемого контроллером.

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

Класс, который пытается найти View, должен реализовать ViewResolver.

ViewResolver сопоставляет имена представлений, возвращаемых методами контроллеров, с фактическими представлениями (html-файлами). Spring Framework поставляется с довольно большим количеством ViewResolver, например InternalResourceViewResolver (default), XmlViewResolver, ResourceBundleViewResolver и несколькими другими.

По умолчанию реализаций интерфейса ViewResolver является класс InternalResourceViewResolver

Существует также несколько реализаций для интеграции с различными технологиями представлений, например Thymeleaf (ThymeleafViewResolver)

Spring Boot автоматически настраивает ViewResolver каждый раз, когда вы добавляете в свой проект зависимость, такую как spring-boot-starter-thymeleaf.
Он также настраивает ViewResolver так, чтобы он по умолчанию просматривал ваш каталог src/main/resources/template.

46
Q

Чем отличаются Model, ModelMap и ModelAndView?

A

Model, ModelMap и ModelAndView - это классы, которые используются в Spring для передачи данных из контроллера в представление. Вот их основные различия:

Model - это интерфейс, который предоставляет методы для добавления атрибутов в модель. Он используется для передачи данных между контроллером и представлением. Контроллер может добавлять атрибуты в модель, и эти атрибуты будут доступны в представлении.
Пример:
~~~
@GetMapping(“/hello”)
public String hello(Model model) {
model.addAttribute(“message”, “Hello World!”);
return “hello”;
}
~~~
Model - интерфейс, представляет коллекцию пар ключ-значение Map<String, Object>. Содержимое модели используется для отображения данных во View.
Например, если View выводит информацию об объекте Customer, то она может ссылаться к ключам модели, например customerName, customerPhone, и получать значения для этих ключей.
Объекты-значения из модели также могут содержать бизнес-логику.

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

@GetMapping("/hello")
public String hello(ModelMap model) {
    model.addAttribute("message", "Hello, World!");
    model.addAttribute("name", "John Doe");
    return "hello";
}

ModelMap - класс, реализует Model и наследуется от LinkedHashMap, используется для передачи значений для визуализации представления.
Преимущество ModelMap заключается в том, что он дает нам возможность передавать коллекцию значений и обрабатывать эти значения, как если бы они были внутри Map.

Следует отметить, что в Model и ModelMap мы можем хранить только данные. Мы помещаем данные и возвращаем имя представления.

ModelAndView - это класс, который представляет из себя комбинацию модели данных и имени представления. Контроллер может создать объект ModelAndView, установить модель данных и имя представления, и вернуть объект ModelAndView в качестве результата работы метода контроллера. Пример:
~~~
@GetMapping(“/hello”)
public ModelAndView hello() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject(“message”, “Hello, World!”);
modelAndView.setViewName(“hello”);
return modelAndView;
}
~~~
ModelAndView - это просто контейнер для ModelMap, объекта View и HttpStatus. Это позволяет контроллеру возвращать все значения как одно.
View используется для отображения данных приложения пользователю.
Spring MVC поддерживает несколько поставщиков View(они называются шаблонизаторы) — JSP, JSF, Thymeleaf, и т.п.
Интерфейс View преобразует объекты в обычные сервлеты.

Таким образом, основное отличие между Model и ModelMap заключается в том, что Model - это интерфейс, который предоставляет только базовый набор методов для добавления атрибутов в модель данных, в то время как ModelMap - это конкретная реализация интерфейса Map, которая предоставляет более широкий набор методов для управления данными.

атрибуты - это данные, которые передаются из контроллера в представление для того, чтобы они были отображены на странице. Атрибуты могут быть как простыми значениями (такими как строки или числа), так и объектами, представляющими более сложные данные (например, коллекции или объекты модели).

Атрибуты хранятся в модели данных (Model, ModelMap или ModelAndView), которая представляет собой объект Map с парами “ключ-значение”. В представлении атрибуты могут быть использованы для отображения данных на странице, например, с помощью выражений JSP EL или Thymeleaf.

47
Q

Расскажите про паттерн MVC, как он реализован в Spring?

A

MVC (Model-View-Controller)
Это шаблон проектирования программного обеспечения, который делит программную логику на три отдельных, но взаимосвязанных компонента: модель, представление и контроллер — таким образом, что модификация каждого компонента может осуществляться независимо.

Модель (Model) предоставляет данные и реагирует на команды контроллера, изменяя своё состояние. Она содержит всю бизнес-логику приложения.

Представление (View) отвечает за отображение пользователю данных из модели в нужном формате.

Контроллер (Controller) содержит код, который отвечает за обработку действий пользователя и обменивается данными с моделью (любое действие пользователя в системе обрабатывается в контроллере).

Основная цель следования принципам MVC — отделить реализацию бизнес-логики приложения (модели) от ее визуализации (вида). Такое разделение повысит возможность повторного использования кода.

При написании веб-приложений на Java с использованием Spring или без него мы пишем приложения, которые возвращают два разных формата данных:
1 HTML → приложение создает HTML-страницы, которые можно просматривать в браузере.
2. JSON/XML → приложение предоставляет сервисы RESTful, которые генерируют JSON или XML.

Spring MVC - веб-фреймворк, основанный на Servlet API, предназначенный для создания веб-приложений на языке Java, с использованием двух самых популярных шаблонов проектирования - Front controller и MVC.

Front controller (Единая точка входа) - паттерн, где центральный сервлет, DispatcherServlet, принимает все запросы и распределяет их между контроллерами, обрабатывающими разные URL.

**
последовательность событий, соответствующая входящему HTTP- запросу:
**
1) После получения HTTP-запроса DispatcherServlet обращается к интерфейсу HandlerMapping, который по аннотации @RequestMapping определяет, какой Контроллер (Controller) должен быть вызван, после чего HandlerAdapter, отправляет запрос в нужный метод Контроллера.
2) Контроллер принимает запрос и вызывает соответствующий служебный метод, основанный на GET, POST и т.д. Вызванный метод формирует данные Модели (например, набор данных из БД) и возвращает их в DispatcherServlet вместе с именем Представления (View) (как правило имя html-файла).
3) При помощи интерфейса ViewResolver DispatcherServlet определяет, какое Представление нужно использовать на основании полученного имени и получает в ответе имя представления View.
- Если это REST-запрос на сырые данные (JSON/XML), то DispatcherServlet сам его отправляет;
- Если обычный запрос, то DispatcherServlet отправляет данные Модели в виде атрибутов в Представление (View) - шаблонизаторы Thymeleaf, FreeMarker и т.д., которые сами отправляют ответ.

https://habr.com/ru/post/500572/

48
Q

Расскажите про паттерн MVC, как он реализован в Spring?v.2

A

MVC — это шаблон проектирования, делящий программу на 3 вида компонентов:
Model — модель отвечает за хранение данных.
View — отвечает за вывод данных на фронтенде.
Controller — оперирует моделями и отвечает за обмен данными model с view.
Основная цель следования принципам MVC — отделить реализацию бизнес-логики приложения (модели) от ее визуализации (вида). Такое разделение повысит возможность повторного использования кода.
Spring MVC - это веб-фреймворк, основанный на Servlet API, с использованием двух шаблонов проектирования - Front controller и MVC.
Spring MVC реализует четкое разделение задач, что позволяет нам легко разрабатывать и тестировать наши приложения. Данные задачи разбиты между разными компонентами: Dispatcher Servlet, Controllers, View Resolvers, Views, Models, ModelAndView, Model and Session Attributes, которые полностью независимы друг от друга, и отвечают только за одно направление. Поэтому MVC дает нам довольно большую гибкость. Он основан на интерфейсах (с предоставленными классами реализации), и мы можем настраивать каждую часть фреймворка с помощью пользовательских интерфейсов.

Основные интерфейсы MVC
1. DispatcherServlet - главный контроллер, получает запросы и распределяет их между другими контроллерами. @RequestMapping указывает, какие именно запросы будут обрабатываться в конкретном контроллере.

  1. HandlerMapping - выбор класса или метода на основе внутреннего или внешнего состояния, для обработки входящего запроса.
  2. Controller - оперирует моделями и отвечает за обмен данными model с View.
  3. ViewResolver - выбор, какое именно View должно быть показано на основе имени, полученного с контроллера.
  4. View - фронт
  5. HandlerAdapter - помогает DispatcherSerlvlet вызывать и выполнять метод для обработки входящего запроса
  6. ContextLoaderListener - связывание жизненного цикла ApplicationContext и ServletContext

Последовательность событий MVC
* После получения HTTP-запроса DispatcherServlet обращается к интерфейсу HandlerMapping, который определяет, какой Контроллер (Controller) должен быть вызван, после чего HandlerAdapter, отправляет запрос в нужный метод Контроллера.
* Контроллер принимает запрос и вызывает соответствующий служебный метод, основанный на GET, POST и т.д. Вызванный метод формирует данные Модели (например, набор данных из БД) и возвращает их в DispatcherServlet вместе с именем Представления (View) (как правило имя html-файла).
* При помощи интерфейса ViewResolver DispatcherServlet определяет, какое Представление нужно использовать на основании полученного имени и получает в ответе имя представления View.
a). если это REST-запрос на сырые данные (JSON/XML), то DispatcherServlet сам его отправляет, минуя ViewResolver;
b). если обычный запрос, то DispatcherServlet отправляет данные Model в виде атрибутов во View - шаблонизаторы Thymeleaf, FreeMarker и т.д., которые сами отправляют ответ.

Как видим, все действия происходят через один DispatcherServlet.

49
Q

Расскажите про паттерн MVC, как он реализован в Spring?v.3

A

Архитектура **Spring MVC **основана на шаблоне проектирования MVC (Model-View-Controller), который разделяет приложение на три основных компонента:

Модель (Model): представляет данные и бизнес-логику приложения.

Представление (View): отображает данные пользователю.

Контроллер (Controller): обрабатывает запросы от клиента, взаимодействует с моделью и выбирает представление для отображения данных.

Фронт-контроллер DispatcherServlet - это центральный компонент архитектуры Spring MVC, который обрабатывает все запросы от клиента и координирует работу остальных компонентов.

После того, как DispatcherServlet получает запрос от клиента, он передает его обработчику запросов (Handler), который может быть любым классом, реализующим интерфейс HandlerMapping. Обработчик запросов определяет, какой контроллер должен обработать запрос и возвращает его DispatcherServlet.

Контроллер обрабатывает запрос, взаимодействуя с моделью приложения, и возвращает информацию о представлении, которое должно быть отображено пользователю. Эта информация передается DispatcherServlet, который выбирает подходящий ViewResolver (разрешитель представлений) и возвращает соответствующее представление.

Представление отображает данные пользователю, используя различные технологии, такие как JSP, Thymeleaf, FreeMarker и т.д.

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

Spring MVC может быть настроен с помощью Java-конфигурации или XML-конфигурации. Вот некоторые основные шаги, которые нужно выполнить для настройки Spring MVC:

Добавить зависимости: Для использования Spring MVC в проекте необходимо добавить зависимости в файл pom.xml или build.gradle.

Настройка контекста Spring: Создать класс конфигурации, который будет настраивать контекст Spring и определять компоненты, такие как контроллеры, сервисы и DAO.

Настройка DispatcherServlet: Добавить DispatcherServlet в файл web.xml (для XML-конфигурации) или в класс настройки конфигурации (для Java-конфигурации). DispatcherServlet является центральным компонентом в Spring MVC и отвечает за обработку запросов и возврат соответствующих представлений.

Настройка ViewResolver: Добавить ViewResolver в конфигурационный класс. ViewResolver - это компонент, который преобразует логические имена представлений в фактические имена файлов представлений, которые будут использоваться для отображения данных. Spring поддерживает различные виды ViewResolver, такие как InternalResourceViewResolver, ThymeleafViewResolver, и т.д.

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

Настройка интерцепторов: Интерцепторы позволяют выполнять дополнительные действия до или после обработки запросов. Настройка интерцепторов производится в конфигурационном классе с помощью метода addInterceptors().

Настройка ресурсов: Настроить доступ к статическим ресурсам, таким как изображения, CSS и JavaScript файлы, в конфигурационном классе с помощью метода addResourceHandlers().

В зависимости от того, используется ли Java-конфигурация или XML-конфигурация, эти шаги могут немного отличаться. Но в целом, они описывают основные шаги, необходимые для настройки Spring MVC.

Контроллеры являются ключевым элементом в архитектуре Spring MVC и отвечают за обработку запросов от клиентов. Вот некоторые основные шаги для создания контроллеров Spring MVC:

Создание класса контроллера: Создайте класс контроллера, который будет обрабатывать запросы. Класс контроллера должен быть отмечен аннотацией @Controller.

Создание методов-обработчиков запросов: Добавьте методы в класс контроллера, которые будут обрабатывать запросы. Методы должны быть отмечены аннотацией @RequestMapping и должны возвращать объект типа ModelAndView или String. Аннотация @RequestMapping указывает на URL-адрес, по которому будет обрабатываться запрос, а тип возвращаемого значения определяет, какие данные будут переданы в представление.

Передача данных в представление: Контроллер может передавать данные в представление с помощью объекта типа Model. Для этого метод-обработчик запроса должен принимать объект типа Model как аргумент и добавлять необходимые данные в этот объект. Например, model.addAttribute(“message”, “Hello World!”); добавляет атрибут “message” со значением “Hello World!” в объект модели.

Использование аннотаций для обработки запросов: Контроллер может использовать различные аннотации, такие как @PathVariable, @RequestParam, @ModelAttribute и другие, для обработки параметров запроса. Например, @RequestParam(“id”) int id указывает, что параметр запроса с именем “id” должен быть преобразован в целочисленное значение.

Возврат представления: Метод-обработчик запроса может возвращать имя представления, которое будет использоваться для отображения данных. Например, return “hello”; означает, что контроллер должен использовать файл представления “hello.jsp” для отображения данных.

Контроллеры Spring MVC предоставляют множество возможностей для обработки запросов от клиентов и передачи данных в представление. С помощью аннотаций и методов-обработчиков запросов можно создавать мощные и гибкие приложения.

В Spring MVC представление отвечает за отображение данных на странице. Существует несколько технологий, которые можно использовать для создания представлений в Spring MVC:

JSP (JavaServer Pages) - технология, которая позволяет создавать представления, объединяя HTML и Java-код. В Spring MVC можно использовать JSP в сочетании с JSTL (JavaServer Pages Standard Tag Library), которая облегчает работу с данными на странице.

Thymeleaf - шаблонизатор, который позволяет создавать представления, объединяя HTML и выражения Thymeleaf. Это дает большую гибкость при работе с данными на странице.

FreeMarker - шаблонизатор, который позволяет создавать представления, используя шаблоны FreeMarker. Он предоставляет более чистый и простой синтаксис, чем JSP.

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

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

В Spring MVC есть встроенная поддержка валидации, которая позволяет проверять данные, вводимые пользователем, перед тем, как они будут обработаны контроллером. Для этого необходимо выполнить следующие шаги:

Добавить аннотацию @Valid перед аргументом метода контроллера, который принимает данные пользователя.

Добавить объект BindingResult в качестве следующего аргумента после аргумента, помеченного аннотацией @Valid. BindingResult содержит результаты валидации и ошибки, которые могут возникнуть при проверке данных.

Создать класс-валидатор, который реализует интерфейс Validator и определяет, какие поля входных данных должны быть проверены и какие ошибки должны быть возвращены при неправильных данных.

Обработка исключений в Spring MVC происходит с помощью механизма обработки исключений, который позволяет определить, как обрабатывать исключения, возникающие во время выполнения приложения.

Существует несколько способов обработки исключений в Spring MVC:

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

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

Использование аспектов. С помощью аспектов можно определить, как обрабатывать исключения в разных частях приложения.

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

Пример использования аннотации @ExceptionHandler:
~~~
@ControllerAdvice
public class ExceptionController {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
    ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
    return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
} } ~~~ В этом примере определен контроллер для обработки исключения ResourceNotFoundException. Метод handleResourceNotFoundException будет вызываться при возникновении этого исключения, и он вернет ResponseEntity с кодом ответа 404 и сообщением об ошибке.

Также можно определить глобальный обработчик исключений в файле web.xml или с помощью Java-конфигурации. Пример Java-конфигурации:
~~~
@EnableWebMvc
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
    Properties mappings = new Properties();
    mappings.setProperty("org.springframework.web.servlet.PageNotFound", "error404");
    resolver.setExceptionMappings(mappings);
    exceptionResolvers.add(resolver);
} } ~~~ В этом примере определяется SimpleMappingExceptionResolver, который будет использоваться для обработки исключения PageNotFound. Если при выполнении запроса возникнет исключение PageNotFound, то будет вызвано представление error404.jsp.

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

Для обеспечения безопасности приложения с использованием Spring Security нужно выполнить следующие шаги:

Добавить зависимость Spring Security в проект.
Настроить Spring Security в приложении, создав конфигурационный класс.
Настроить аутентификацию и авторизацию, определив пользователей и их роли.
Защитить ресурсы приложения, указав права доступа для каждого ресурса.
Пример настройки Spring Security в Java-конфигурации:
~~~
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(“user”).password(“{noop}password”).roles(“USER”)
.and()
.withUser(“admin”).password(“{noop}password”).roles(“ADMIN”);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(“/admin/**”).hasRole(“ADMIN”)
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout();
}
}
~~~

В этом примере мы создаем конфигурационный класс SecurityConfig, наследуемый от WebSecurityConfigurerAdapter. Мы также добавляем аннотацию @EnableWebSecurity, чтобы включить поддержку Spring Security.

Метод configureGlobal() используется для настройки аутентификации. Мы определяем двух пользователей - user с ролью USER и admin с ролью ADMIN.

Метод configure() используется для настройки авторизации и защиты ресурсов. Мы указываем, что ресурсы, начинающиеся с /admin, могут быть доступны только для пользователей с ролью ADMIN. Для всех остальных ресурсов требуется аутентификация. Мы также настраиваем форму входа и выхода из системы.

Это простой пример настройки Spring Security. В зависимости от требований приложения, могут быть использованы и другие механизмы защиты, такие как JWT-токены, двухфакторная аутентификация и т.д.

50
Q

Расскажите про паттерн Front Controller, как он реализован в Spring?

A

Паттерн Front Controller обеспечивает единую точку входа для всех входящих запросов.

В Spring Framework Front Controller реализуется с помощью механизма DispatcherServlet. DispatcherServlet является центральным элементом веб-приложения, который обрабатывает все входящие HTTP-запросы и решает, какой конкретный контроллер должен быть вызван для обработки запроса.

DispatcherServlet выполняет следующие действия:

  1. Получает HTTP-запрос от клиента.
  2. Анализирует запрос и определяет, какой контроллер должен быть вызван для его обработки.
  3. Передает запрос выбранному контроллеру.
  4. Контроллер обрабатывает запрос и возвращает модель и представление.
  5. DispatcherServlet выбирает подходящий ViewResolver для конкретного представления и формирует ответ.
  6. Ответ отправляется обратно клиенту.

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

Почти все веб-фреймворки Java основаны на сервлетах, поэтому Spring MVC также нужен сервлет, который обрабатывает каждый входящий HTTP-запрос: поэтому фронт-контроллер реализован в виде DispatcherServlet extends HttpServlet.

Иногда необходимо определить два и более DispatcherServlet-а.
Например, чтобы один обрабатывал REST-запросы с маппингом “/api”, а другой обычные запросы с маппингом “/default”:
последовательность событий, соответствующая входящему HTTP-запросу:

  • После получения HTTP-запроса DispatcherServlet обращается к интерфейсу HandlerMapping, который определяет, какой Контроллер должен быть вызван, после чего, отправляет запрос в нужный Контроллер.
  • Контроллер принимает запрос и вызывает соответствующий служебный метод, основанный на GET или POST. Вызванный метод определяет данные Модели, основанные на определённой бизнес-логике и возвращает в DispatcherServlet имя Вида (View).
  • При помощи интерфейса ViewResolver DispatcherServlet определяет, какой Вид нужно использовать на основании полученного имени.
  • После того, как Вид (View) создан, DispatcherServlet отправляет данные Модели в виде атрибутов в Вид, который в конечном итоге отображается в браузере.

Все вышеупомянутые компоненты, а именно, HandlerMapping, Controller и ViewResolver, являются частями интерфейса WebApplicationContext extends ApplicationContext, с некоторыми дополнительными особенностями, необходимыми для создания web-приложений.

Spring может иметь несколько контекстов одновременно.
* Одним из них будет корневой контекст, а все остальные контексты будут дочерними.
* Все дочерние контексты могут получить доступ к бинам, определенным в корневом контексте, но не наоборот.
* Каждый дочерний контекст внутри себя может переопределить бины из корневого контекста.
ContextLoaderListener создает корневой контекст приложения (Root WebApplicationContext extends ApplicationContext) и он будет использоваться всеми дочерними контекстами, созданными всеми DispatcherServlet.
Корневой контекст приложения будет общим и может быть только один.
Он содержит компоненты, которые видны всем дочерним контекстам, такие как сервисы, репозитории, компоненты инфраструктуры и т.д.

Каждый DispatcherServlet создаёт себе один дочерний WebApplicationContext.

51
Q

Расскажите про паттерн Front Controller, как он реализован в Spring?

A

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

Spring MVC (Model-View-Controller) является одним из наиболее популярных фреймворков Java для создания веб-приложений и предоставляет реализацию паттерна Front Controller. В Spring MVC контроллер представляет собой Java-класс, который обрабатывает запросы HTTP и возвращает ответы.

Реализация Front Controller в Spring MVC происходит следующим образом:

Контроллер определяется как класс, который обрабатывает запросы от клиентов. Обычно это аннотированный класс с аннотацией @Controller.

Контроллер содержит методы, которые обрабатывают различные типы запросов. Каждый метод аннотирован аннотацией @RequestMapping, которая указывает URL-адрес, на который должен реагировать метод.

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

После того, как запрос обработан, контроллер возвращает представление, которое должно быть показано пользователю. Представление в Spring MVC представляет собой HTML-страницу или JSON-данные, которые могут быть отображены на странице.

Контроллер может использовать аннотацию @ModelAttribute для добавления модели в представление. Модель - это Java-объект, который содержит данные, которые будут использоваться для отображения на странице.

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

Таким образом, паттерн Front Controller реализуется в Spring MVC с помощью аннотированных классов-контроллеров и аннотаций, которые определяют, как обрабатывать запросы и возвращать ответы.

52
Q

В чем разница между Filters, Listeners and Interceptors?

A

Filter
Это интерфейс из пакета javax.servlet, имплементации которого выполняют задачи фильтрации либо по пути запроса к ресурсу (сервлету, либо по статическому контенту), либо по пути ответа от ресурса, либо в обоих направлениях.

Чтобы создать фильтр в Spring, необходимо:

  • Cоздать класс, который реализует интерфейс Filter.
  • Реализовать метод doFilter(), (позволяет выполнять действия до и после вызова следующего элемента в цепочке фильтров.)

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

находится снаружи и не является частью Spring MVC:
может перехватывать HTTP request/responce на входе / выходе в / из Dispatcher Servlet. Подходит для
- аутентификации
- логирования in/out запросов
- компресии / декомпрессии
- любого функционала, который мы хотим отделить от Spring MVC
~~~
public class MyFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// выполняем необходимые действия до обработки запроса цепочкой фильтров
// например, проверяем наличие и валидность авторизационных данных
chain.doFilter(request, response);
// выполняем необходимые действия после обработки запроса цепочкой фильтров
// например, логируем результаты обработки запроса
}
// реализация остальных методов интерфейса Filter
}
~~~

Interceptor

часть Spring MVC и находятся между Dispatcher Servlet и контроллерами. В частности являются инструментом внедрения AOP.
Следует знать, что HandlerInterceptor связан с бином DefaultAnnotationHandlerMapping, который отвечает за применение перехватчиков к любому классу, помеченному аннотацией @Controller.

Это интерфейс из пакета org.aopalliance.intercept, предназначенный для аспектно- ориентированного программирования.

В Spring, когда запрос отправляется в Controller, перед тем как он в него попадёт, он может пройти через перехватчики Interceptor (0 или более). Это одна из реализаций АОП в Spring.
* Вы можете использовать Interceptor для выполнения таких задач, как:
* Аутентификация и авторизация: Interceptor’ы могут использоваться для проверки аутентификации и авторизации пользователя перед выполнением запроса к серверу.
* Логирование: Interceptor’ы могут использоваться для логирования запросов и ответов на сервер.
* Кэширование: Interceptor’ы могут использоваться для кэширования ответов на запросы, чтобы уменьшить нагрузку на сервер.
* Обработка ошибок: Interceptor’ы могут использоваться для обработки ошибок, возникающих при выполнении запроса на сервер.
* Дополнительная обработка запросов и ответов: Interceptor’ы могут использоваться для дополнительной обработки запросов и ответов на сервер, например, для добавления заголовков в запросы или ответы.
* Измерение производительности: Interceptor’ы могут использоваться для измерения производительности запросов и ответов на сервер.

Чтобы использовать Interceptor в Spring, необходимо выполнить следующие шаги:

  • Создать класс, реализующий интерфейс HandlerInterceptor или наследующий класс HandlerInterceptorAdapter.
  • Переопределить необходимые методы интерфейса (например, preHandle(), postHandle(), afterCompletion()).
  • Зарегистрировать созданный класс как компонент Spring (например, с помощью аннотации @Component или в конфигурационном классе).
  • Добавить созданный компонент в список Interceptor’ов конфигурации WebMvcConfigurer (если вы используете Spring MVC).

preHandle — метод используется для обработки запросов, которые еще не были переданы в метод контроллера. Должен вернуть true для передачи следующему перехватчику или в handler method. False укажет на обработку запроса самим обработчиком и отсутствию необходимости передавать его дальше. Метод имеет возможность выкидывать исключения и пересылать ошибки к представлению.

postHandle — вызывается после handler method, но до обработки DispatcherServlet для передачи представлению. Может использоваться для добавления параметров в объект ModelAndView.

afterCompletion — вызывается после отрисовки представления.

ServletContextListener

Java Listener
Listener (Слушатель) - это класс, который реализует интерфейс javax.servlet.ServletContextListener. Он инициализируется только один раз при запуске веб-приложения и уничтожается при остановке веб-приложения. Слушатель сидит и ждет, когда произойдет указанное событие, затем «перехватывает» событие и запускает собственное событие. Например, мы хотим инициализировать пул соединений с базой данных до запуска веб-приложения. ServletContextListener - это то, что нам нужно, он будет запускать наш код до запуска веб-приложения.
Все ServletContextListeners уведомляются об инициализации контекста до инициализации любых фильтров или сервлетов в веб-приложении.
Все ServletContextListeners уведомляются об уничтожении контекста после того, как все сервлеты и фильтры уничтожены.

Слушатели позволяют приложению реагировать на различные события в жизненном цикле, что может быть полезным для выполнения различных операций, например:
* Инициализация и очистка бинов приложения
* Работа с кэшем и базой данных
* Отправка уведомлений об ошибках или событиях приложения
* Работа с сессиями пользователей
* Работа с аутентификацией и авторизацией

Для использования Listeners в Spring необходимо выполнить следующие шаги:

  • Создать класс слушателя, который реализует интерфейс нужного слушателя (например, ServletContextListener, HttpSessionListener, ApplicationListener и т.д.) и определить нужный метод обработки события.
  • Зарегистрировать слушателя в контексте приложения.
  • 2.2. Если вы используете Java-конфигурацию, то необходимо добавить аннотацию @WebListener к классу слушателя и зарегистрировать его с помощью метода addListener():
  • Обработать события в методах, определенных в классе слушателя.

Кроме того, в Spring есть возможность использовать аннотации для регистрации слушателей. Например, если вы хотите зарегистрировать слушателя при запуске приложения, то можно использовать аннотацию @EventListener над методом в любом бине:

53
Q

В чем разница между Filters, Listeners and Interceptors?

A

В Spring Framework Filters, Listeners и Interceptors также являются различными способами обработки запросов, но они имеют свои особенности и назначение:

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

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

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

В Spring Framework есть несколько различных типов фильтров, слушателей и перехватчиков, которые могут использоваться для различных целей. Вот некоторые из них:

Filter Используется для изменения HTTP-запросов и ответов, а также для проверки безопасности и выполнения других операций на низком уровне
HandlerInterceptor Используется для изменения запросов и ответов на более высоком уровне, непосредственно перед тем, как они будут обработаны контроллером
ApplicationListener Используется для прослушивания событий в жизненном цикле приложения, таких как запуск и остановка
WebMvcConfigurer Используется для настройки фильтров и перехватчиков в рамках Spring MVC

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

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

Интерсепторы предоставляют более гранулированный способ обработки запросов и ответов в Spring MVC приложениях. Они позволяют выполнять логику до и после обработки запроса контроллером. Интерсепторы также могут использоваться для изменения модели и управления исключениями. Интерсепторы настраиваются в конфигурационных классах Spring.

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

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

54
Q

Что такое АОП? Как реализовано в спринге?

A

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

Ключевые понятия:
- сквозная функциональность: затрагивает несколько модулей, но она не имеет прямого отношения к бизнес коду, и ее хорошо бы вынести в отдельное место
- Aspect: модуль в котором собраны описания Pointcut и Advice.

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

ПойнтКат - выражение, описывающее где должен быть применен Эдвайс

Join point: точка присоединения к коду, где планируется внедрение функциональности. В Spring AOP это всегда выполнение метода.
Pointcut: это предикат, вычисляет Join points. (можно: ?? / || / !)
Advice: набор инструкций выполняемых на точках среза (Pointcut). Инструкции можно выполнять по событию разных типов:
Before — перед вызовом метода
After — после вызова метода
After returning — после возврата значения из функции
After throwing — в случае exception
After finally — в случае выполнения блока finally
Around — можно сделать пред., пост., обработку перед вызовом метода, а также вообще обойти вызов метода.
на один Pointcut можно «повесить» несколько Advice разного типа.

AspectJ де-факто является стандартом реализации АОП.
Spring AOP поддерживает аннотации AspectJ, таким образом мы можем работать в спринг проекте похожим образом с AspectJ проектом.
Реализация АОП от Spring имеет некоторые отличия:

в Spring AOP нет необходимости следить за процессом связывания.
Spring + AOP поддерживает только proxy-based АОП и может использовать только один тип точек соединения - Method Invocation. AspectJ поддерживает все виды точек соединения.
Spring AOP работает только со своими бинами, которые существуют в Spring Context.

55
Q

Что такое АОП? Как реализовано в спринге?

A

АОП (аспектно-ориентированное программирование) - это парадигма программирования, которая позволяет выделить некоторые повторяющиеся аспекты в приложении и вынести их в отдельные модули, называемые аспектами. Аспекты могут содержать код, который будет выполняться перед, после или вместо выполнения методов в основном приложении.

Spring Framework поддерживает АОП и предоставляет механизмы для реализации аспектно-ориентированного программирования. В Spring Framework АОП может быть реализовано с помощью двух основных механизмов: Прокси и Аспекты.

Прокси - это механизм, который создает объект-посредник, который может перехватывать вызовы методов на оригинальном объекте и выполнять дополнительную обработку до или после выполнения метода. Прокси могут быть созданы как на основе интерфейсов, так и на основе классов.

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

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

Вот некоторые примеры использования АОП в Spring Framework:

Транзакционность Использование аннотации @Transactional или XML-конфигурации для определения транзакционных методов
Кэширование Использование аннотации @Cacheable или XML-конфигурации для определения методов, которые могут быть закешированы
Логирование Использование аспекта org.springframework.aop.aspectj.AspectJAfterThrowingAdvice для выполнения логирования при возникновении исключений
Проверка безопасности Использование аннотации @Secured или XML-конфигурации для определения методов, которые могут быть

56
Q

Что такое АОП? Как реализовано в спринге ?

A

АОП (Аспектно-ориентированное программирование) - это подход к программированию, который позволяет выделить повторяющиеся задачи в отдельные компоненты и применять их к различным частям кода без необходимости изменения самого кода. В Spring фреймворке АОП реализуется с помощью применения аспектов и советов.

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

Основные термины:

Срез (Pointcut) - определяет, к каким методам или классам будет применяться совет.
Advice выполняет набор инструкций по запросу Pointcut`a
Аспект (Aspect) - это модуль, содержащий срезы и советы.
Weaver - это инструмент, который внедряет аспекты в байт-код компилируемых классов.
Примеры использования AOP в Spring:

Следующие таблицы демонстрируют примеры срезов и советов в Spring.
@Before(“execution(* com.example.service.UserService.(..))”) Срабатывает перед вызовом любого метода в UserService
@AfterReturning(“execution(
com.example.service.UserService.*(..))”) Срабатывает после возвращения любого значения из любого метода UserService
@Around(“@annotation(com.example.annotation.Loggable)”) Срабатывает вместо метода, помеченного аннотацией @Loggable

@Before(“myPointcut()”) Выполняется перед выполнением среза myPointcut()
@After(“myPointcut()”) Выполняется после выполнения среза myPointcut()
@AfterReturning(“myPointcut()”, returning=”returnValue”) Выполняется после успешного выполнения среза myPointcut() и получения значения возвращаемого методом

57
Q

Пример кода

A

Pointcut:

@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}

Aspect:

@Aspect
@Component
public class LoggingAspect {
 
    @Before("userServiceMethods()")
    public void logMethodCall(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Calling method: " + methodName);
    }
}

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

Таким образом, при вызове любого метода в UserService, мы увидим сообщение в консоли “Calling method: {имя вызываемого метода}”.

вот пример кода с использованием Spring AOP, который логирует исключения:

Pointcut:

@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}
Aspect:
@Aspect
@Component
public class LoggingAspect {
 
    @Before("userServiceMethods()")
    public void logMethodCall(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Calling method: " + methodName);
    }
    
    @AfterThrowing(pointcut = "userServiceMethods()", throwing = "exception")
    public void logException(JoinPoint joinPoint, Throwable exception) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Exception in method: " + methodName + ", exception message: " + exception.getMessage());
    }
}

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

В методе logMethodCall мы выводим название вызываемого метода в консоль, а в методе logException мы выводим название метода и сообщение исключения.

Таким образом, при вызове любого метода в UserService, мы увидим сообщение в консоли “Calling method: {имя вызываемого метода}”, а при бросании исключения в методе, мы увидим сообщение в консоли “Exception in method: {имя вызываемого метода}, exception message: {сообщение исключения}”.

58
Q

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

A

Да, можно принять все значения, используя массив в методе контроллера: http://localhost:8080/login?name=Ranga&name=Ravi&name=Sathish

public String method(@RequestParam(value=\"name\") String[] names){...}

В вашем первом примере, используя аннотацию @RequestParam с параметром “value” для указания имени параметра в запросе, и указывая тип параметра как массив String [], Spring Framework автоматически соберет все значения параметра, переданные в запросе, в массив String[].

59
Q

Как работает Spring Security?

A

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

При работе с Spring Security есть несколько ключевых концепций:

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

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

FilterChain - это цепочка фильтров, которые обрабатывают запросы и ответы, прежде чем они будут переданы обработчику. FilterChainProxy - это основной компонент фреймворка Spring Security, который управляет цепочкой фильтров.

UserDetails - это интерфейс, который предоставляет информацию о конкретном пользователе в приложении, такую как имя пользователя, пароль, роли и т. д. UserDetailsService отвечает за загрузку этой информации.

AuthenticationProvider - это интерфейс, который обрабатывает процесс аутентификации пользователя. Он принимает Authentication и возвращает Authentication после прохождения проверки.

GrantedAuthority - это интерфейс, который представляет собой роль пользователя в системе. Он используется для определения прав доступа пользователя.

SecurityContextHolder - это класс, который хранит информацию об аутентификации и авторизации пользователя в текущем потоке.

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

Конфигурация **Spring Security **- это процесс определения параметров и настроек для защиты веб-приложений. В основе конфигурации Spring Security лежит специальный класс конфигурации, который может быть настроен с помощью аннотаций или методов. Класс конфигурации может настраивать авторизацию и аутентификацию, права доступа пользователей, SSL, CSRF-защиту и другие аспекты безопасности.

Основным интерфейсом Spring Security является WebSecurityConfigurerAdapter. Этот интерфейс используется для определения правил безопасности веб-приложения. При настройке конфигурации Spring Security вы должны создать класс, который расширяет WebSecurityConfigurerAdapter и переопределить методы для настройки аутентификации, авторизации и других аспектов безопасности.

Наиболее важные методы, которые необходимо переопределить в классе конфигурации, включают:

configure(HttpSecurity http): этот метод используется для настройки правил безопасности для конкретных HTTP-запросов. Вы можете определить, какие URL-адреса должны быть доступны для каждой роли пользователя, а также какие параметры безопасности должны использоваться, например, CSRF-защита или SSL.

configure(AuthenticationManagerBuilder auth): этот метод используется для настройки AuthenticationManager, который управляет аутентификацией пользователей. Вы можете определить, какой AuthenticationProvider использовать для проверки учетных данных пользователя.

userDetailsService(): этот метод определяет, какой сервис пользователей должен использоваться для получения информации о пользователях, в том числе их учетных данных и ролей.

configure(WebSecurity web): этот метод используется для настройки параметров безопасности, не относящихся к конкретным HTTP-запросам, например, для настройки доступа к статическим ресурсам.

Для включения Spring Security в приложение необходимо использовать аннотацию @EnableWebSecurity на основном классе приложения. Она сообщает Spring о том, что класс содержит настройки безопасности.

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

60
Q

Возможные атаки

A

CSRF (Cross-Site Request Forgery) - атака, при которой злоумышленник отправляет запросы от имени авторизованного пользователя без его согласия. Для защиты от этой атаки в Spring Security можно использовать токены CSRF.

XSS (Cross-Site Scripting) - атака, при которой злоумышленник вводит вредоносный код на страницу, который выполняется в браузере пользователя. Для защиты от этой атаки Spring Security предоставляет функциональность фильтрации входных данных и экранирования символов.

SQL-инъекции - атака, при которой злоумышленник вводит вредоносный SQL-код для выполнения нежелательных операций с базой данных. Для защиты от этой атаки Spring Security предоставляет возможность использования параметризованных запросов и механизмов предотвращения SQL-инъекций.

61
Q

Как сконфигурировать Spring Security?

A

Spring Security можно настроить с помощью Java-конфигурации или XML-конфигурации. Рассмотрим пример настройки Spring Security с помощью Java-конфигурации:

Создайте класс конфигурации для Spring Security, который расширяет класс WebSecurityConfigurerAdapter:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/resources/**", "/signup", "/about").permitAll()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
        .logout()
            .permitAll();
}
 
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .inMemoryAuthentication()
            .withUser("user").password("{noop}password").roles("USER")
            .and()
            .withUser("admin").password("{noop}password").roles("USER", "ADMIN");
} } В методе configure() вы можете настроить различные параметры безопасности, такие как ограничение доступа к ресурсам, настройку формы входа в систему и выхода из нее и т.д.

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

Вы можете использовать различные провайдеры аутентификации, такие как JDBC, LDAP, OAuth и т.д.

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

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

62
Q

Процесс аутентификации

A
  1. UsernamePasswordAuthenticationFilter получают имя пользователя и пароль и создает экземпляр класса UsernamePasswordAuthenticationToken (экземпляр интерфейса Authentication).
  2. Токен передается экземпляру AuthenticationManager для проверки.
  3. AuthenticationManager возвращает полностью заполненный экземпляр Authentication в случае успешной аутентификации.
  4. Устанавливается контекст безопасности путем вызова SecurityContextHolder.getContext().setAuthentication(…), куда передается вернувшийся экземпляр Authentication.
  5. При успешной аутентификации можно использовать successHandler
63
Q

CSRF (Cross-Site Request Forgery) токен в Spring Framework

A

CSRF (Cross-Site Request Forgery) токен в Spring Framework представляет собой механизм защиты от атак, когда злоумышленник пытается отправить запрос от имени авторизованного пользователя без его согласия.

В Spring Framework CSRF токен генерируется автоматически и добавляется в каждый HTML формуляр, который отправляется на сервер. Когда пользователь отправляет форму, CSRF токен включается в запрос, который затем проверяется на стороне сервера. Если токен не соответствует ожидаемому значению, сервер отклонит запрос.

Чтобы включить CSRF защиту в Spring, необходимо добавить следующий код в конфигурационный класс:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf()
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
  }
}

Здесь используется CookieCsrfTokenRepository, который сохраняет CSRF токен в cookie браузера, что обеспечивает более безопасную передачу токена между клиентом и сервером. Также используется параметр httpOnly, который предотвращает доступ к токену из JavaScript.

CSRF защита является важной частью обеспечения безопасности веб-приложений и ее использование в Spring Framework является рекомендованным.

64
Q

Основные Интерфейсы:

A

Основные Интерфейсы:
1) SecurityContext -контейнер для объекта типа Authentication. (Аналог - ApplicationContext, в котором лежат бины).
Имеет только два метода: getAuthentication() и setAuthentication(Authentication authentication).
По умолчанию на каждый поток создается один SecurityContext.

2) SecurityContextHolder - Класс, хранящий в ThreadLocal SecurityContext-ы для каждого потока, и содержащий статические методы для работы с SecurityContext-ами, а через них с текущим объектом Authentication, привязанным к нашему веб-запросу.

3) Authentication - объект, отражающий информацию о текущем пользователе и его привилегиях. Хранит объекты Principal, Credentials, Collection<GrantedAuthority>.</GrantedAuthority>

4) Principal - интерфейс из пакета java.security, отражающий учетную запись пользователя.
В терминах логин-пароль это логин. В интерфейсе Authentication есть метод getPrincipal(), возвращающий Object. При аутентификации с использованием имени пользователя/пароля Principal реализуется объектом типа UserDetails.

5) Credentials - любой Object; то, что подтверждает учетную запись пользователя, как правило пароль (отпечатки пальцев, пин - всё это Credentials, а владелец отпечатков и пина - Principal).

6) GrantedAuthority - полномочия, предоставленные пользователю, например, роли или уровни доступа.

7) UserDetails - интерфейс, представляющий учетную запись пользователя. Как правило модель нашего пользователя должна имплементировать его. Она просто хранит пользовательскую информацию в виде логина, пароля и флагов isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled, а также коллекции прав (ролей) пользователя. Данная информация позже инкапсулируется в объекты Authentication.

8) UserDetailsService - интерфейс объекта, реализующего загрузку пользовательских данных из хранилища. Созданный нами объект с этим интерфейсом должен обращаться к БД и получать оттуда юзеров.

9) AuthenticationManager - основной стратегический интерфейс для аутентификации.

10) AuthenticationProvider - интерфейс объекта, выполняющего аутентификацию.
AuthenticationProvider немного похож на AuthenticationManager, но у него есть дополнительный метод, позволяющий вызывающей стороне спрашивать, поддерживает ли он переданный ему объект Authentication, возможно этот AuthenticationProvider может поддерживать только аутентификацию по логину и паролю, но не поддерживать Google-аутентификацию: boolean supports(java.lang.Class<?> authentication)
Основное отличие между AuthenticationManager и AuthenticationProvider заключается в том, что AuthenticationManager принимает запрос на аутентификацию и делегирует этот запрос всем зарегистрированным провайдерам аутентификации для выполнения аутентификации пользователя. В то же время AuthenticationProvider используется провайдерами, которые выполняют фактическую проверку подлинности пользователя.

11) PasswordEncoder - интерфейс для шифрования/расшифровывания паролей. Одна из популярных реализаций - BCryptPasswordEncoder.

65
Q

Что такое SpringBoot? Как конфигурируется?

A

Spring Boot - это модуль Spring-а, который предоставляет функцию RAD для среды Spring (Rapid Application Development - Быстрая разработка приложений).

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

Ключевые особенности и преимущества Spring Boot:

1) Простота управления зависимостями (spring-boot-starter-* в pom.xml).

Чтобы ускорить процесс управления зависимостями Spring Boot неявно упаковывает необходимые сторонние зависимости для каждого типа приложения на основе Spring и предоставляет их разработчику в виде так называемых starter-пакетов.
Starter-пакеты представляют собой набор удобных дескрипторов зависимостей, которые можно включить в свое приложение. Это позволяет получить универсальное решение для всех технологий, связанных со Spring, избавляя программиста от лишнего поиска необходимых зависимостей, библиотек и решения вопросов, связанных с конфликтом версий различных библиотек.

Например, если вы хотите начать использовать Spring Data JPA для доступа к базе данных, просто включите в свой проект зависимость spring-boot-starter-data-jpa (вам не придется искать совместимые драйверы баз данных и библиотеки Hibernate). Если вы хотите создать Spring web-приложение, просто добавьте зависимость spring-boot-starter-web, которая подтянет в проект все библиотеки, необходимые для разработки Spring MVC-приложений, таких как spring-webmvc, jackson-json, validation-api и Tomcat.

Другими словами, Spring Boot собирает все общие зависимости и определяет их в одном месте, что позволяет разработчикам просто их использовать. Также при использовании Spring Boot, файл pom.xml содержит намного меньше строк, чем в Spring-приложениях.

2) Автоматическая конфигурация.
Автоматическая конфигурация включается аннотацией @EnableAutoConfiguration. (входит в состав аннотации @SpringBootApplication) После выбора необходимых для приложения starter-пакетов Spring Boot попытается автоматически настроить Spring-приложение на основе выбранных jar-зависимостей, доступных в classpath классов, свойств в application.properties и т.п.

Например, если добавим spring-boot-starter-web, то Spring boot автоматически сконфигурирует такие бины как DispatcherServlet, ResourceHandlers, MessageSource итд

Автоматическая конфигурация работает в последнюю очередь, после регистрации пользовательских бинов и всегда отдает им приоритет. Если ваш код уже зарегистрировал бин DataSource — автоконфигурация не будет его переопределять.

3) Встроенная поддержка сервера приложений/контейнера сервлетов (Tomcat, Jetty, итд).
Каждое Spring Boot web-приложение включает встроенный web-сервер. Не нужно беспокоиться о настройке контейнера сервлетов и развертывания приложения в нем.

Теперь приложение может запускаться само как исполняемый .jar-файл с использованием встроенного сервера.

Готовые к работе функции, такие как метрики, проверки работоспособности, security и внешняя конфигурация.
Инструмент CLI (command-line interface) для разработки и тестирования приложения Spring Boot.
Минимизация boilerplate кода (код, который должен быть включен во многих местах практически без изменений), конфигурации XML и аннотаций.

Как происходит автоконфигурация в Spring Boot:
1. Отмечаем main класс аннотацией @SpringBootApplication (аннотация инкапсулирует в себе: @SpringBootConfiguration, @ComponentScan, @EnableAutoConfiguration), таким образом наличие @SpringBootApplication включает сканирование компонентов, автоконфигурацию и показывает разным компонентам Spring (например, интеграционным тестам), что это Spring Boot приложение.

@EnableAutoConfiguration импортирует класс EnableAutoConfigurationImportSelector. Этот класс не объявляет бины сам, а использует фабрики.
Класс EnableAutoConfigurationImportSelector импортирует ВСЕ (более 150) перечисленные в META-INF/spring.factories конфигурации, чтобы предоставить нужные бины в контекст приложения.
Каждая из этих конфигураций пытается сконфигурировать различные аспекты приложения (web, JPA, AMQP и т.д.), регистрируя нужные бины. Логика при регистрации бинов управляется набором @ConditionalOn* аннотаций. Можно указать, чтобы бин создавался при наличии класса в classpath (@ConditionalOnClass), наличии существующего бина (@ConditionalOnBean), отсуствии бина (@ConditionalOnMissingBean) и т.п. Таким образом наличие конфигурации не значит, что бин будет создан и зачастую конфигурация ничего делать и создавать не будет.
Созданный в итоге AnnotationConfigEmbeddedWebApplicationContext ищет в том же DI контейнере фабрику для запуска embedded servlet container.
Servlet container запускается, приложение готово к работе

66
Q

Основные преимущества Spring Boot:

A

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

Основные преимущества Spring Boot:

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

Упрощенная конфигурация - Spring Boot использует конфигурацию на основе условий, что позволяет легко настраивать приложения для разных окружений.

Встроенный сервер - Spring Boot включает в себя встроенный сервер Tomcat, Jetty или Undertow, что упрощает запуск и развертывание приложений.

Удобство развертывания - Spring Boot автоматически упаковывает приложение в один выполняемый JAR-файл, что упрощает его развертывание на сервере.

Автоматическая конфигурация - Spring Boot автоматически конфигурирует множество стандартных библиотек, таких как Hibernate, Spring MVC и другие.

Удобство тестирования - Spring Boot предоставляет множество утилит и инструментов для удобного тестирования приложений.

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

Spring Boot конфигурируется с помощью файла application.properties или application.yml, который находится в папке src/main/resources. В этом файле можно определить различные свойства и параметры приложения, такие как настройки базы данных, порты сервера, директории для сохранения файлов и т.д.

Также Spring Boot поддерживает JavaConfig, которая позволяет конфигурировать приложение в Java-коде. В JavaConfig можно определить бины, настроить компоненты Spring и многое другое.

67
Q

Расскажите про нововведения Spring 5.

A

“● Используется JDK 8+ (Optional, CompletableFuture, Time API, java.util.function, default methods)
● Поддержка JUnit 5 + Testing Improvements (conditional and concurrent)
● Spring-Data-JPA 2.x, Spring-Security 5.x
● Поддержка Java 9 (Automatic-Module-Name in 5.0, module-info in 6.0+, ASM 6)
● Поддержка HTTP/2 (TLS, Push), NIO/NIO.2
● Поддержка Kotlin
● Реактивность (веб-инфраструктура с реактивным стеком, «Spring WebFlux»)
● Null-safety аннотации(@Nullable), новая документация
● Совместимость с Java EE 8 (Servlet 4.0, Bean Validation 2.0, JPA 2.2, JSON Binding API 1.0)
● Удалена поддержка: Portlet, Velocity, JasperReports, XMLBeans, JDO, Guava”

68
Q

JWT (JSON Web Token)

A

JWT (JSON Web Token) - это открытый стандарт для передачи данных в формате JSON между клиентом и сервером. Он представляет собой токен, который содержит информацию об аутентификации пользователя и может использоваться для безопасной передачи этой информации между системами.

В Spring Framework можно использовать JWT для аутентификации и авторизации пользователей. Для этого можно использовать библиотеку Spring Security, которая имеет поддержку JWT.

В Spring Security JWT токен может быть создан при успешной аутентификации пользователя и включать информацию о его ролях и правах доступа. Этот токен может быть передан клиенту и использоваться для дальнейшей аутентификации при каждом запросе к серверу.

Spring Security имеет поддержку различных методов аутентификации, включая аутентификацию на основе токенов. Для использования JWT токена в Spring Security можно настроить специальный фильтр, который будет извлекать информацию из токена и устанавливать ее в контекст безопасности.

69
Q

Spring REST

A

Spring REST (Representational State Transfer) - это фреймворк для создания веб-сервисов, использующих REST-архитектуру. Он предоставляет набор инструментов для обработки входящих запросов и формирования ответов в формате JSON или XML.

Работа Spring REST начинается с создания контроллеров, которые обрабатывают входящие запросы. Контроллеры могут быть написаны с использованием аннотаций, которые определяют, какой HTTP-метод должен быть обработан и какие параметры должны быть переданы в запросе.

Для обработки параметров запроса Spring REST использует механизм привязки параметров, который позволяет автоматически конвертировать параметры запроса в Java-объекты.

Spring REST также предоставляет инструменты для валидации данных, получаемых от клиентов, а также для генерации ошибок в формате JSON или XML в случае возникновения ошибок.

Для сериализации Java-объектов в формат JSON или XML Spring REST использует специальные библиотеки, такие как Jackson или JAXB.

Важным аспектом работы Spring REST является управление состоянием приложения на стороне клиента. По умолчанию REST-сервисы не сохраняют состояние, но для управления состоянием на стороне клиента можно использовать токены доступа, такие как JWT.

Кроме того, Spring REST предоставляет инструменты для защиты веб-сервисов от атак, таких как CSRF и XSS. Для этого можно использовать механизмы аутентификации и авторизации, предоставляемые Spring Security.

В целом, Spring REST облегчает создание веб-сервисов с помощью REST-архитектуры, обеспечивает управление состоянием на стороне клиента и защиту от атак.

основные компоненты и функции Spring REST:

Контроллеры: контроллеры Spring REST используют аннотации для определения URI-шаблонов и методов HTTP, которые обрабатывают запросы. Контроллеры обрабатывают запросы и возвращают данные клиенту в виде JSON или XML.

Объекты передачи данных (DTO): объекты DTO используются для передачи данных между клиентом и сервером. Они могут быть созданы с помощью классов Java или с использованием библиотеки Jackson для маршалинга и демаршалинга JSON.

Spring Data: Spring REST интегрируется с Spring Data, что позволяет легко создавать репозитории для работы с базами данных.

Конфигурация: Spring REST позволяет настраивать контекст приложения с помощью Java-конфигурации или XML-файлов конфигурации.

Сериализация и десериализация: Spring REST использует библиотеку Jackson для сериализации и десериализации объектов JSON.

Обработка ошибок: Spring REST обеспечивает обработку ошибок и возвращение соответствующих кодов ошибок клиенту.

Аутентификация и авторизация: Spring REST обеспечивает механизмы аутентификации и авторизации, такие как использование токенов JWT или Basic Auth.

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

В целом, Spring REST позволяет разработчикам создавать быстро и эффективно RESTful веб-сервисы, обеспечивая широкий спектр функций для работы с HTTP-запросами и JSON-данными.

70
Q

RESTful веб-сервисы

A

RESTful веб-сервисы - это сервисы, которые используют принципы архитектуры REST (Representational State Transfer). RESTful сервисы позволяют клиентам взаимодействовать с сервером через стандартные HTTP-методы, такие как GET, POST, PUT и DELETE, используя URI (Uniform Resource Identifier) для определения ресурсов.

Основными принципами архитектуры REST являются:

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

Spring предоставляет набор инструментов для разработки RESTful сервисов, включая Spring MVC, Spring Boot, Spring Data и т.д. В Spring MVC контроллеры обрабатывают запросы и возвращают ответы в формате JSON или XML. Spring Boot облегчает создание и развертывание RESTful приложений, автоматически настраивая многие параметры приложения и предоставляя встроенный сервер Tomcat. Spring Data позволяет легко взаимодействовать с базами данных, используя Spring-совместимые репозитории.