The objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. These beans are created with the configuration metadata that you supply to the container. For example, in the form of XML definitions which you have already seen in the previous chapters.
Bean definition contains the information called configuration metadata, which is needed for the container to know the following −
All the above configuration metadata translates into a set of the following properties that make up each bean definition.
Sr.No.
Properties & Description
1
This attribute is mandatory and specifies the bean class to be used to create the bean.
This attribute specifies the bean identifier uniquely. In XMLbased configuration metadata, you use the id and/or name attributes to specify the bean identifier(s).
This attribute specifies the scope of the objects created from a particular bean definition and it will be discussed in bean scopes chapter.
This is used to inject the dependencies and will be discussed in subsequent chapters.
This is used to inject the dependencies and will be discussed in subsequent chapters.
This is used to inject the dependencies and will be discussed in subsequent chapters.
A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at the startup.
A callback to be called just after all necessary properties on the bean have been set by the container. It will be discussed in bean life cycle chapter.
A callback to be used when the container containing the bean is destroyed. It will be discussed in bean life cycle chapter.
Spring Configuration Metadata
Spring IoC container is totally decoupled from the format in which this configuration metadata is actually written. Following are the three important methods to provide configuration metadata to the Spring Container −
You already have seen how XML-based configuration metadata is provided to the container, but let us see another sample of XML-based configuration file with different bean definitions including lazy initialization, initialization method, and destruction method −
You can check Spring Hello World Example to understand how to define, configure and create Spring Beans.
We will discuss about Annotation Based Configuration in a separate chapter. It is intentionally discussed in a separate chapter as we want you to grasp a few other important Spring concepts, before you start programming with Spring Dependency Injection with Annotations.
Какие возможности предоставляет Spring для кастомизации своего поведения
Всем привет. На связи Владислав Родин. В настоящее время я являюсь руководителем курса «Архитектор высоких нагрузок» в OTUS, а также преподаю на курсах, посвященных архитектуре ПО.
Помимо преподавания, я занимаюсь написанием авторского материала для блога OTUS на хабре и сегодняшнюю статью хочу приурочить к запуску курса «Разработчик на Spring Framework», на который прямо сейчас открыт набор.
Введение
С точки зрения читателя код приложения, использующего Spring, выглядит достаточно просто: объявляются некоторые bean’ы, классы размечаются аннотациями, а дальше bean’ы inject’ятся куда нужно, все прекрасно работает. Но у пытливого читателя возникает вопрос: «А как это работает? Что происходит?». В этой статье мы попытаемся ответить на данный вопрос, но только не ради удовлетворения праздного любопытства.
Spring framework известен тем, что он является достаточно гибким и предоставляет возможности для настройки поведения framework’а. Также Spring «изобилует» рядом достаточно интересных правил на применение некоторых аннотаций (например, Transactional). Для того, чтобы понять смысл этих правил, уметь их выводить, а также понимать что и как можно настраивать в Spring’е, необходимо понять несколько принципов работы того, что находится у Spring’а под капотом. Как известно, знание нескольких принципов освобождает от знания множества фактов. Я предлагаю ознакомиться с этими принципами ниже, если вы их, конечно, еще не знаете.
Чтение конфигураций
Примеры классов, принимающих участие в этом процессе: GroovyBeanDefinitionReader, XmlBeanDefinitionReader, AnnotatedBeanDefinitionReader, реализующие интерфейс BeanDefinitionReader.
Настройка BeanDefinition’ов
Итак, у нас есть описания bean’ов, но при этом самих bean’ов нет, они еще не созданы. Перед созданием bean’ов Spring предоставляет возможность настроить полученные BeanDefinition’ы. Для этих целей служит интерфейс BeanFactoryPostProcessor. Он выглядит следующим образом:
Параметр метода данного интерфейса позволяет с помощью своего уже метода getBeanDefinitionNames получить имена, по которым можно вытаскивать BeanDefinition’ы из мапы и править их.
Зачем это может понадобиться? Предположим, что некоторые bean’ы требуют реквизиты для подключения к какой-либо внешней системе, например, к базе данных. Мы хотим, чтобы bean’ы создавались уже с реквизитами, но сами реквизиты хранятся в property-файле. Мы можем применить один из стандартных BeanFactoryPostProcessor’ов — PropertySourcesPlaceholderConfigurer, который заменит в BeanDefinition’е название property на актуальное значение, хранящееся в property-файле. То есть фактически заменит Value(«user») на Value(«root») в BeanDefinion’е. Для того, чтобы это сработало, PropertySourcesPlaceholderConfigurer, конечно же, необходимо подключить. Но этим дело не ограничивается, вы можете зарегистрировать свой BeanFactoryPostProcessor, в котором реализовать любую нужную вам логику по обработке BeanDefinition’ов.
Создание bean’ов
На данном этапе у нас есть мапа, у которой по ключам располагаются имена bean’ов, а по значениям располагаются настроенные BeanDefinition’ы. Теперь необходимо создать эти bean’ы. Этим занимается BeanFactory. Но и здесь можно добавить кастомизацию, написав и зарегистрировав свой FactoryBean. FactoryBean представляет из себя интерфейс вида:
Таким образом, BeanFactory создает bean сам, если отсутствует соответствующий классу bean’а FactoryBean, либо просит FactoryBean этот самый bean создать. Здесь есть маленький нюанс: если scope bean’а является singletone, то bean создается на данном этапе, если prototype, то каждый раз, когда этот bean будет нужен, он будет запрошен у BeanFactory.
В итоге, у нас снова получается мапа, но уже несколько другая: по ключам находятся имена bean’ов, а по значениям сами bean’ы. Но это верно только для singletone’ов.
Настройка bean’ов
Теперь наступает самый интересный этап. У нас есть мапа, содержащая созданные bean’ы, но эти bean’ы пока еще не настроены. То есть мы не обработали аннотации, задающие состояние bean’а: Autowired, Value. Мы также не обработали аннотации, изменяющие поведение bean’а: Transactional, Async. Решить данную задачу позволяют BeanPostProcessor‘ы, которые опять-таки являются реализациями соответствующего интерфейса:
Мы видим 2 метода со страшными, но исчерпывающими названиями. Оба метода принимают на вход bean, у которого можно спросить какого он класса, а затем воспользоваться Reflection API для обработки аннотаций. Возвращают методы bean, возможно замененный на proxy.
Для каждого bean’а перед помещением его в контекст происходит следующее: срабатывают методы postProcessBeforeInitialization у всех BeanPostProcessor’ов, затем срабатывает init-метод, а затем срабатывают методы postProcessAfterInitialization также у всех BeanPostProcessor’ов.
У этих двух методов разная семантика, postProcessBeforeInitialization обрабатывает аннотации на состояние, postProcessAfterInitialization — поведение, потому как для обработки поведения применяется проксирование, что может привести к потере аннотаций. Именно поэтому поведение изменяется в самую последнюю очередь.
Где же тут кастомизация? Мы можем написать свою аннотацию, BeanPostProcessor для нее, и Spring ее обработает. Правда, чтобы BeanPostProcessor сработал, его также необходимо зарегистрировать в виде bean’а.
Например, для внедрения в поле рандомного числа мы создаем аннотацию InjectRandomInt (вешается на поля), создаем и регистрируем InjectRandomIntBeanPostProcessor, в первом методе которого обрабатываем созданную аннотацию, а во втором методе просто возвращаем пришедший bean.
Для профилирования bean’ов создаем аннотацию Profile, которая вещается на методы, создаем и регистрируем ProfileBeanPostProcess’ор, в первом методе которого возвращаем пришедший bean, а во втором методе возвращаем прокси, оборачивающий вызов оригинального метода засечением и логгированием времени выполнения.
Spring изнутри. Этапы инициализации контекста
Доброго времени суток уважаемые хабравчане. Уже 3 года я работаю на проекте в котором мы используем Spring. Мне всегда было интересно разобраться с тем, как он устроен внутри. Я поискал статьи про внутреннее устройство Spring, но, к сожалению, ничего не нашел.
Всех, кого интересует внутреннее устройство Spring, прошу под кат.
На схеме изображены основные этапы поднятия ApplicationContext. В этом посте мы остановимся на каждом из этих этапов. Какой-то этап будет рассмотрен подробно, а какой-то будет описан в общих чертах.
1. Парсирование конфигурации и создание BeanDefinition
Цель первого этапа — это создание всех BeanDefinition. BeanDefinition — это специальный интерфейс, через который можно получить доступ к метаданным будущего бина. В зависимости от того, какая у вас конфигурация, будет использоваться тот или иной механизм парсирования конфигурации.
Xml конфигурация
Для Xml конфигурации используется класс — XmlBeanDefinitionReader, который реализует интерфейс BeanDefinitionReader. Тут все достаточно прозрачно. XmlBeanDefinitionReader получает InputStream и загружает Document через DefaultDocumentLoader. Далее обрабатывается каждый элемент документа и если он является бином, то создается BeanDefinition на основе заполненных данных (id, name, class, alias, init-method, destroy-method и др.). Каждый BeanDefinition помещается в Map. Map хранится в классе DefaultListableBeanFactory. В коде Map выглядит вот так.
Конфигурация через аннотации с указанием пакета для сканирования или JavaConfig
Конфигурация через аннотации с указанием пакета для сканирования или JavaConfig в корне отличается от конфигурации через xml. В обоих случаях используется класс AnnotationConfigApplicationContext.
Если заглянуть во внутрь AnnotationConfigApplicationContext, то можно увидеть два поля.
ClassPathBeanDefinitionScanner сканирует указанный пакет на наличие классов помеченных аннотацией @Component (или любой другой аннотацией которая включает в себя @Component). Найденные классы парсируются и для них создаются BeanDefinition. Чтобы сканирование было запущено, в конфигурации должен быть указан пакет для сканирования.
Groovy конфигурация
Данная конфигурация очень похожа на конфигурацию через Xml, за исключением того, что в файле не XML, а Groovy. Чтением и парсированием groovy конфигурации занимается класс GroovyBeanDefinitionReader.
2. Настройка созданных BeanDefinition
После первого этапа у нас имеется Map, в котором хранятся BeanDefinition. Архитектура спринга построена таким образом, что у нас есть возможность повлиять на то, какими будут наши бины еще до их фактического создания, иначе говоря мы имеем доступ к метаданным класса. Для этого существует специальный интерфейс BeanFactoryPostProcessor, реализовав который, мы получаем доступ к созданным BeanDefinition и можем их изменять. В этом интерфейсе всего один метод.
Метод postProcessBeanFactory принимает параметром ConfigurableListableBeanFactory. Данная фабрика содержит много полезных методов, в том числе getBeanDefinitionNames, через который мы можем получить все BeanDefinitionNames, а уже потом по конкретному имени получить BeanDefinition для дальнейшей обработки метаданных.
Давайте разберем одну из родных реализаций интерфейса BeanFactoryPostProcessor. Обычно, настройки подключения к базе данных выносятся в отдельный property файл, потом при помощи PropertySourcesPlaceholderConfigurer они загружаются и делается inject этих значений в нужное поле. Так как inject делается по ключу, то до создания экземпляра бина нужно заменить этот ключ на само значение из property файла. Эта замена происходит в классе, который реализует интерфейс BeanFactoryPostProcessor. Название этого класса — PropertySourcesPlaceholderConfigurer. Весь этот процесс можно увидеть на рисунке ниже.
Давайте еще раз разберем что же у нас тут происходит. У нас имеется BeanDefinition для класса ClassName. Код класса приведен ниже.
Если PropertySourcesPlaceholderConfigurer не обработает этот BeanDefinition, то после создания экземпляра ClassName, в поле host проинжектится значение — «$» (в остальные поля проинжектятся соответсвующие значения). Если PropertySourcesPlaceholderConfigurer все таки обработает этот BeanDefinition, то после обработки, метаданные этого класса будут выглядеть следующим образом.
Соответственно в эти поля проинжектятся правильные значения.
Для того что бы PropertySourcesPlaceholderConfigurer был добавлен в цикл настройки созданных BeanDefinition, нужно сделать одно из следующих действий.
Для XML конфигурации.
PropertySourcesPlaceholderConfigurer обязательно должен быть объявлен как static. Без static у вас все будет работать до тех пор, пока вы не попробуете использовать @ Value внутри класса @Configuration.
3. Создание кастомных FactoryBean
На первый взгляд, тут все нормально и нет никаких проблем. А что делать если нужен другой цвет? Создать еще один бин? Не вопрос.
А что делать если я хочу каждый раз случайный цвет? Вот тут то и приходит на помощь интерфейс FactoryBean.
Создадим фабрику которая будет отвечать за создание всех бинов типа — Color.
Добавим ее в xml и удалим объявленные до этого бины типа — Color.
Теперь создание бина типа Color.class будет делегироваться ColorFactory, у которого при каждом создании нового бина будет вызываться метод getObject.
Для тех кто пользуется JavaConfig, этот интерфейс будет абсолютно бесполезен.
4. Создание экземпляров бинов
Созданием экземпляров бинов занимается BeanFactory при этом, если нужно, делегирует это кастомным FactoryBean. Экземпляры бинов создаются на основе ранее созданных BeanDefinition.
5. Настройка созданных бинов
Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки ваших бинов до того, как они попадут в контейнер. Интерфейс несет в себе несколько методов.
Процесс донастройки показан на рисунке ниже. Порядок в котором будут вызваны BeanPostProcessor не известен, но мы точно знаем что выполнены они будут последовательно.
Для того, что бы лучше понять для чего это нужно, давайте разберемся на каком-нибудь примере.
При разработке больших проектов, как правило, команда делится на несколько групп. Например первая группа разработчиков занимается написанием инфраструктуры проекта, а вторая группа, используя наработки первой группы, занимается написанием бизнес логики. Допустим второй группе понадобился функционал, который позволит в их бины инжектить некоторые значения, например случайные числа.
На первом этапе будет создана аннотация, которой будут помечаться поля класса, в которые нужно проинжектить значение.
По умолчанию, диапазон случайных числе будет от 0 до 10.
Затем, нужно создать обработчик этой аннотации, а именно реализацию BeanPostProcessor для обработки аннотации InjectRandomInt.
Код данного BeanPostProcessor достаточно прозрачен, поэтому мы не будем на нем останавливаться, но тут есть один важный момент.
BeanPostProcessor обязательно должен быть бином, поэтому мы его либо помечаем аннотацией @Component, либо регестрируем его в xml конфигурации как обычный бин.
Первая группа разработчиков свою задачу выполнила. Теперь вторая группа может использовать эти наработки.
В итоге, все бины типа MyBean, получаемые из контекста, будут создаваться с уже проинициализированными полями value1 и value2. Также тут стоить отметить, этап на котором будет происходить инжект значений в эти поля будет зависеть от того какой @ Scope у вашего бина. SCOPE_SINGLETON — инициализация произойдет один раз на этапе поднятия контекста. SCOPE_PROTOTYPE — инициализация будет выполняться каждый раз по запросу. Причем во втором случае ваш бин будет проходить через все BeanPostProcessor-ы что может значительно ударить по производительности.
Полный код программы вы можете найти тут.
Хочу сказать отдельное спасибо EvgenyBorisov. Благодаря его курсу, я решился на написание этого поста.
Также советую посмотреть его доклад с JPoint 2014.
Spring: вопросы к собеседованию
Этот небольшой список вопросов даст вам понимание самых важных концепций Spring, а так же поможет подготовится к собеседованию
Если вы понимаете как работает Component Scan, то вы понимаете Spring
Однако, Spring ничего не знает об этих бинах, если он не знает где искать их. То, что скажет Spring где искать эти бины и называется Component Scan. В @ComponentScan вы указываете пакеты, которые должны сканироваться.
Spring будет искать бины не только в пакетах для сканирования, но и в их подпакетах.
@SpringBootApplication определяет автоматическое сканирование пакета, где находится класс Application
Всё будет в порядке, ваш код целиком находится в указанном пакете или его подпакетах.
Однако, если необходимый вам компонент находится в другом пакете, вы должны использовать дополнительно аннотацию @ComponentScan, где перечислите все дополнительные пакеты для сканирования
@Component и @ComponentScan предназначены для разных целей
@Component помечает класс в качестве кандидата для создания Spring бина. @ComponentScan указывает где Spring искать классы, помеченные аннотацией @Component или его производной
В классах конфигурации Spring, @Bean используется для определения компонентов с кастомной логикой.
@Bean используется в конфигурационных классах Spring. Он используется для непосредственного создания бина.
Все они определяют бины Spring. Однако между ними всё же есть разница.
@Component — универсальный компонент @Repository — компонент, который предназначен для хранения, извлечения и поиска. Как правило, используется для работы с базами данных. @Service — фасад для некоторой бизнес логики
Если @Component является универсальным стереотипом для любого Spring компонента, то @Service в настоящее время является его псевдонимом. Однако, в официальной документации Spring рекомендуется использовать именно @Service для бизнес логики. Вполне возможно, что в будущих версиях фреймворка, для данного стереотипа добавится дополнительная семантика, и его бины станут обладать дополнительной логикой.
web.xml — Метаданные и конфигурация любого веб-приложения, совместимого с Java EE. Java EE стандарт для веб-приложений. servlet.xml — файл конфигурации, специфичный для Spring Framework.
Предпочитаю аннотации, если кодовая база хорошо описывается такими элементами, как @Service, @Component, @Autowired
Однако когда дело доходит до конфигурации, у меня нет каких-либо предпочтений. Я бы оставил этот вопрос команде.
@Autowired может использоваться вместе с конструкторами, сеттерами или любым другими методами. Когда Spring находит @Autowired на методе, Spring автоматически вызовет этот метод, после создания экземпляра бина. В качестве аргументов, будут подобраны подходящие объекты из контекста Spring.
Сквозная Функциональность — функциональность, которая может потребоваться вам на нескольких различных уровнях — логирование, управление производительностью, безопасность и т.д. АОП — один из подходов к реализации данной проблемы
IOC — инверсия управления. Вместо ручного внедрения зависимостей, фреймворк забирает ответственность за это. ApplicationContext — реализация IOC спрингом.
Bean Factory — это базовая версия IOC контейнера
Application Context также включает дополнительные функции, которые обычно нужны для разработки корпоративных приложений
classPathXmlApplicationContext — если вы хотите инициализировать контекст Spring при помощи xml annotationConfigApplicationContext — если вы хотите инициализировать контекст Spring при помощи конфигурационного класса java
Если все бины имеют одинаковый приоритет, мы всегда будем использовать @Qualifier
На мой взгляд это Functional Web Framework, Kotlin и и поддержка реактивного программирования.
Web Container и EJB Containers являются частью приложения/веб-сервера, таких как Tomcat, Websphere, Weblogic. Они добавляют свою дополнительную функциональность к ним. Java EE определяет контракт для веб-приложений, эти контейнеры являются реализацией этих контрактов.
Spring контейнер может являться частью любого приложения, которое вы делаете на java. Spring может работать внутри веб-контейнера, ejb контейнера или даже без них.
Тогда в application.properties добавим свойство application.greeting: real
Воспользуемся данным решением:
Spring 5.0 и Spring Boot 2.0 поддерживают Java 8 и более поздней версии.
@RestController = @Controller + @ResponseBody
@RestController превращает помеченный класс в Spring-бин. Этот бин для конвертации входящих/исходящих данных использует Jackson message converter. Как правило целевые данные представлены в json или xml.
ResponseEntity необходим, только если мы хотим кастомизировать ответ, добавив к нему статус ответа. Во всех остальных случаях будем использовать @ResponseBody.
Стандартные HTTP коды статусов ответов, которые можно использовать. 200 — SUCCESS 201 — CREATED 404 — RESOURCE NOT FOUND 400 — BAD REQUEST 401 — UNAUTHORIZED 500 — SERVER ERROR
Для @ResponseBody единственные состояния статуса это SUCCESS(200), если всё ок и SERVER ERROR(500), если произошла какая-либо ошибка.
Допустим мы что-то создали и хотим отправить статус CREATED(201). В этом случае мы используем ResponseEntity.
Концептуально всё просто, фильтры сервлетов могут перехватывать только HTTPServlets. Listeners могут перехватывать специфические события. Как перехватить события которые относятся ни к тем не другим?
Фильтры и перехватчики делают по сути одно и тоже: они перехватывают какое-то событие, и делают что-то до или после.
Java EE использует термин Filter, Spring называет их Interceptors.
Именно здесь AOP используется в полную силу, благодаря чему возможно перехватывание вызовов любых объектов
Model — интерфейс, ModelMap его реализация..
ModelAndView является контейнером для пары, как ModelMap и View.
Обычно я люблю использовать ModelAndView. Однако есть так же способ когда мы задаем необходимые атрибуты в ModelMap, и возвращаем название View обычной строкой из метода контроллера.
Метод addAttribute отделяет нас от работы с базовой структурой hashmap. По сути addAttribute это обертка над put, где делается дополнительная проверка на null. Метод addAttribute в отличии от put возвращает modelmap. model.addAttribute(“attribute1”,”value1”).addAttribute(“attribute2”,”value2”);
Нам это может понадобиться, если мы, например, захотим взять некоторое значение с HTML страницы и сохранить его в БД. Для этого нам надо это значение переместить в контроллер Спринга.
Если мы будем использовать Spring MVC form tags, Spring автоматически свяжет переменные на HTML странице с Бином Спринга.
Если мне придется с этим работать, я обязательно буду смотреть официальную документацию Spring MVC Form Tags.
Hibernate Validator никак не связан с БД. Это просто библиотека для валидации.
Hibernate Validator версии 5.x является эталонной реализацией Bean Validation 1.1
Так же если взглянуть по адресу http://beanvalidation.org/2.0, то Hibernate Validator является единственным, который сертифицирован.
Расположение статических ресурсов можно настроить. В документации Spring Boot рекомендуется использовать /static, или /public, или /resources, или /META-INF/resources
В случае GET запроса передаваемые параметры являются частью url, и все маршрутизаторы, через которые пройдет наш GET запрос, смогут их прочитать.
В случае POST запроса передаваемые параметры являются частью тела запроса. При использовании HTTPs, тело запроса шифруется. Следовательно, использование POST запросов является более безопасным
Пример: http://localhost:8080/login?name=Ranga&name=Ravi&name=Sathish Да, можно принять все значения, используя массив в методе контроллера
Хочу поблагодарить пользователя хабра jd2050, за помощь с переводом.