Создание компонентов
Основными элементами в контексте приложения, использующего Spring Framework, являются компоненты или бины (beans), используемые для внедрения зависимостей (Dependency Injection, DI). Есть два основных способа создания таких компонентов.
Создание компонента при помощи аннотаций
Самый простой и наиболее распространённый способ создания компонента — при помощи аннотации @Component или аннотаций, наследующих её, таких как @Service, @Repository или @Controller из пакета org.springframework.stereotype.
Пример простого компонента:
import org . springframework . stereotype . Component ;
public class SimpleBean <
public String hello ( ) <
return "Hello world!" ;
В данном случае будет создан компонент типа SimpleBean с именем и идентификатором simpleBean. Контекст Spring автоматически найдёт этот класс и создаст объект этого типа, который будет использоваться в дальнейшем для внедрения зависимостей.
Создание компонента при помощи @Bean-метода
Ещё один популярный способ создания компонентов — при помощи метода, помеченного аннотацией @Bean. В этом случае сам тип компонента не нужно помечать аннотацией @Component.
import org . acruxsource . sandbox . filters . SimpleBean ;
import org . springframework . context . annotation . Bean ;
import org . springframework . context . annotation . Configuration ;
@ Configuration
public class ContextConfig <
public SimpleBean simpleBean ( ) <
return new SimpleBean ( ) ;
public SimpleBean anotherSimpleBean ( ) <
return new SimpleBean ( ) ;
В данном случае будут созданы 2 компонента типа SimpleBean и с именами и идентификаторами simpleBean и anotherSimpleBean. Данный способ удобен тем, что можно создавать компоненты сторонних классов. Так же отмечу тот момент, что создание компонентов при помощи @Bean-методов возможно только в классах, помеченных аннотацией @Confuguration, как это показано в примере выше. Так же этот код соответствует следующей XML-конфигурации:
При создании компонентов таким способом стоит помнить, что если в классе, объект которого вы создаёте, есть зависимости внедряемые при помощи аннотации @Autowired, они будут автоматически внедрены.
Использование компонентов
Теперь, когда мы разобрались с созданием компонентов, можно приступать к рассмотрению примеров их использования. Компоненты можно внедрять в другие компоненты, и самый распространённый пример — внедрение репозитория в контроллер. Внедрять зависимости можно тоже несколькими способами.
Внедрение через свойство класса
Самый распространённый, но не самый удобный для тестирования, способ внедрения, о чём я как-нибудь напишу в заметке о тестировании.
@Controller
public class SimpleController <
private SimpleBean simpleBean ;
В данном случае контекст Spring находит класс, помеченный аннотацией @Controller, создаёт объект этого класса, находит свойство помеченное аннотацией @Autowired и присваивает указанному свойству компонент нужного класса, находящийся в контексте Spring.
Тут может произойти две ошибки: в контексте Spring не окажется компонента класса SimpleBean, либо в контексте окажется более одного компонента класса SimpleBean. Об этом я расскажу чуть ниже.
Внедрение через конструктор
Внедрение через конструктор значительно удобнее, так как при тестировании даёт возможность вручную создать объект класса и передать ему мок вместо реального компонента.
@Controller
class SimpleController <
private final SimpleBean simpleBean ;
private final AnotherSimpleBean simpleBean ;
public SimpleController ( SimpleBean simpleBean , AnotherSimpleBean anotherSimpleBean ) <
this . simpleBean = simpleBean ;
this . anotherSimpleBean = anotherSimpleBean ;
Данный код соответствует XML-конфигурации:
Аннотации @Autowired , @Inject , @Resource и @Value обрабатываются Spring реализацией BeanPostProcessor , поэтому вы не можете их применять в своих собственных BeanPostProcessor и BeanFactoryPostProcessor , а только лишь явной инициализацией через XML или @Bean метод. Ниже представлена диаграмма, отражающая, как работает Spring. Ваши классы приложения совмещаются с метаданными конфигурации, в результате чего будет создан и инициализирован ApplicationContext , а на выходе вы получите полностью настроенное и готовое к выполнению приложение. ApplicationContext представляет собой Spring IoC контейнер и необходим для инициализации, настройки и сборки бинов для построения приложения. В метаданных конфигурации разработчик описывает как инициализировать, настроить IoC контейнер и собрать объекты в вашем приложении. В данном и других уроках этого цикла везде, где возможно, будет использоваться подход на основе аннотаций и Java-конфигурации. Если вы сторонник XML-конфигурации, либо хотите посмотреть как делать тоже самое через XML, обратитесь к оригинальной документации по Spring Framework или соответствующего модуля/проекта. Основными признаками и частями Java-конфигурации IoC контейнера являются классы с аннотацией @Configuration и методы с аннотацией @Bean . Аннотация @Bean используется для указания того, что метод создает, настраивает и инициализирует новый объект, управляемый Spring IoC контейнером. Такие методы можно использовать как в классах с аннотацией @Configuration , так и в классах с аннотацией @Component (или её наследниках). Класс с аннотацией @Configuration говорит о том, что он является источником определения бинов. Самая простейшая из возможных конфигураций выглядит следующим образом: Полный @Configuration vs легкий @Bean режимы Когда методы с аннотацией @Bean определены в классах, не имеющих аннотацию @Configuration , то относятся к обработке в легком режиме, то же относится и к классам с аннотацией @Component . Иначе, такие методы относятся к полному режиму обработки. В отличие от полного, в легком режиме @Bean методы не могут просто так объявлять внутренние зависимости. Поэтому, в основном предпочтительно работать в полном режиме, во избежание трудноуловимых ошибок. Для того, чтобы приступить к настройке и изучению Spring IoC контейнера, вы должны инициализировать ApplicationContext , который поможет также с разрешением зависимостей. Для обычной Java-конфигурации применяется AnnotationConfigApplicationContext , в качестве аргумента к которому передается класс, либо список классов с аннотацией @Configuration , либо с любой другой аннотацией JSR-330, в том числе и @Component : Как вариант, можно инициализировать контекст(ы) таким образом: Как упоминалось выше, для того, чтобы объявить Bean-объект(далее просто бин), достаточно указать аннотацию @Bean тому методу, который возвращает тип бина как в классах с аннотацией @Configuration , так и в классах с аннотацией @Component (или её наследниках). Например, определим интерфейс какого-нибудь сервиса и его реализацию: Теперь, для того, чтобы объект с типом GreetingService был доступен для использования, необходимо описать его в конфигурации следующим образом: А для того, чтобы использовать его, достаточно выполнить следующее: Метод getBean() может принимать в качестве аргумента как класс(как показано выше), так и названия бина(подробнее будет рассмотрено ниже), либо другие варианты, с которыми вы можете ознакомится в документации. Однако такой подход не рекомендуется использовать в production-конфигурациях, т.к. для подобных целей существует механизм Dependency Injection (DI), собственно говоря, для чего и предназначен Spring IoC контейнер. Использование DI будет рассмотрено ниже в отдельной главе. Именовать бины принято в соответствии со стандартным соглашением по именованию полей Java-классов. Т.е. имена бинов должны начинаться со строчной буквы и быть в «Верблюжьей» нотации. По умолчанию, так, как будет назван метод определения бина, по такому имени и нужно получать бин через метод getBean() или автоматическое связывание. Однако вы можете переопределить это имя или указать несколько псевдонимов, через параметр name аннотации @Bean . Выглядеть это будет примерно так: Иногда полезно предоставить более подробное описание бина, например, в целях мониторинга. Для этого существует аннотация @Description : Для управления контейнером жизненным циклом бина, вы можете реализовать метод afterPropertiesSet() интерфейса InitializingBean и метод destroy() интерфейса DisposableBean . Метод afterPropertiesSet() позволяет выполнять какие-либо действий после инициализации всех свойств бина контейнером, метод destroy() выполняется при уничтожении бина контейнером. Однако их не рекомендуется использовать, поскольку они дублируют код Spring. Как вариант, предпочтительно использовать методы с JSR-250 аннотациями @PostConstruct и @PreDestroy . Также существует вариант определить аналогичные методы как параметры аннотации @Bean , например так: @Bean(initMethod = «initMethod», destroyMethod = «destroyMethod») .В качестве примера применения данных методов, интерфейсов и аннотаций вы можете ознакомиться в классе GreetingServiceImpl . При совместном использовании методов, интерфейсов и аннотаций, описанных выше, учитывайте их порядок вызовов. Для методов инициализации порядок будет следующий: Для методов разрушения порядок будет следующий: Если вам необходимо реализовать свою собственную модель жизненного цикла бина, то в таком случае бин должен реализовывать один из интерфейсов, приведенных ниже: SmartLifecycle интересен тем, что наследует интерфейс Phased , в котором есть метод int getPhase(); . Суть в том, что порядок создания бинов, реализующих этот интерфейс, зависит от возвращаемого методом значения и чем оно меньше, тем раньше всех будет создан бин и тем позже он будет разрушен. Если вы на данном этапе запустите Starter.java , то в логах увидите, что методы разрушения не вызываются, однако программа завершает свою работу корректно. Дело в том, что для обычных приложений для этих целей стоит инициализировать контекст с типом AbstractApplicationContext , который также реализует ApplicationContext и имеет метод registerShutdownHook() . В итоге, у вас должно быть премерно следующее: После этого у вас появятся результаты работы методов при разрушении бина. Однако стоит заметить ещё раз, что это относится к обычным приложения, не относящимся к web-приложения(поскольку для них применяется отдельный тип контекста и подобный метод в них уже есть). В некоторых случаях необходимо производить манипуляции с ApplicationContext ‘ом, например, в самом бине. Для этого существуют интерфейсы *Aware , полный список которых приведен в таблице 5.4 документации. Поэтому когда ApplicationContext создает экземпляр бина, он учитывает соответствующий интерфейс и передает ссылку на соответствующий ресурс. Как было описано выше, Spring IoC контейнеру требуются метаданные для конфигурации. Одну из таких аннотаций мы уже рассмотрели, это @Bean , рассмотрим теперь и другие. Другой основной аннотацией является @Component , а также её наследники @Repository , @Service и @Controller . Все они являются общими шаблонами для любых компонентов, управляемыми контейнеером. @Repository , @Service и @Controller рекомендуется использовать в тех случаях, когда вы можете отнести аннотируемый класс к определенному слою, например DAO, либо когда вам необходима поддержка функциональности, которую предоставляет аннотация. Также эти аннотации могут иметь дополнительный смысл в будущих версиях Spring Framework. В остальных же случаях достаточно использовать аннотацию @Component . Для того, чтобы ваша конфигурация могла знать о таких компонентах и вы могли бы их использовать, существует специальная аннотация для класса вашей конфигурации @ComponentScan . По умолчанию, такая конфигурация сканирует на наличие классов с аннотацией @Component и его потомков в том пакете, в котором сама находится, а также в подпакетах. Однако, если вы хотите, чтобы сканирование было по определенным каталогам, то это можно настроить, просто добавив в аннотацию @ComponentScan параметр basePackages с указанием одного или нескольких пакетов. Выглядеть это будет примерно таким образом: @ComponentScan(basePackages = «lessons.services») , а классу GreetingServiceImpl при этом необходимо добавить аннотацию @Component . Стоит упомянуть ещё одну мета-аннотацию @Required . Данная аннотация применяется к setter-методу бина и указывает на то, чтобы соответствующее свойство метода было установлено на момент конфигурирования значением из определения бина или автоматического связывания. Если же значение не будет установлено, будет выброшено исключение. Использование аннотации позволит избежать NullPointerException в процессе использования свойства бина. Пример использования: Когда вы создаете определение бинов, вы вы создаете рецепт для создания экземпляров класса, который определяет бин. Важно понять, что определение бинов является рецептом, потому что он означает, какого класса вы можете создать множество экземпляров по этому рецепту. Вы можете контролировать не только какие зависимости и значения конфигурации вы можете подключить в объекте, который создан из определения бина, но также область видимости из того же определения бина. Это мощный и гибкий подход, при котором вы можете выбрать область видимости создаваемых объектов. Изначально, Spring Framework поддерживает несколько вариантов, некоторые доступны, только если вы используете web-aware ApplicationContext . Также вы можете создать свою собственную облать видимости. Ниже приведен список областей видимостей, описанных в документации на момент написания урока: С более подробной информацией о настройке приложения для применения областей видимости request , session , global session и application вы можете ознакомиться в документации. Пример реализации собственной области видимости будет рассмотрено в отдельном уроке. Для того, чтобы указать область видимости бина, отличный от singleton , необходимо добавить аннотацию @Scope(«область_видимости») методу объявления бина или классу с аннотацией @Component : Как упоминалось выше, классы с аннотацией @Configuration указывают на то, что они являются источниками определения бинов, public-методов с аннотацией @Bean . Кода бин имеет зависимость от другого бина, то зависимость выражается просто как вызов метода: Однако работает такое взаимодействие только в @Configuration -классах, в @Component -классах такое не работает. Представим теперь ситуацию, когда у вас есть бин с областью видимости singleton , который имеет зависимость от бина с областью видимости prototype . Большая часть приложений строится по модульной архитектуре, разделенная по слоям, например DAO, сервисы, контроллеры и др. Создавая конфигурацию, можно также её разбивать на составные части, что также улучшит читабельность и панимание архитектуры вашего приложения. Для этого в конфигурацию необходимо добавить аннотацию @Import , в параметрах которой указываются другие классы с аннотацией @Configuration , например: Таким образом, при инициализации контекста вам не нужно дополнительно указывать загрузку из конфигурации AnotherConfiguration , все останется так, как и было: В большинстве случаев, имеются такие случаи, когда бин в одной конфигурации имеет зависимость от бина в другой конфигурации. Поскольку конфигурация является источником определения бинов, то разрешить такую зависимость не является проблемой, достаточно объявить поле класса конфигурации с аннотацией @Autowired (более подробно оисано в отдельной главе): При этом LessonsConfiguration остается без изменений: Классы с аннотацией @Configuration не стремятся на 100% заменить конфигурации на XML, при этом, если вам удобно или имеется какая-то необходимость в использовании XML конфигурации, то к вашей Java-конфигурации необходимо добавить аннотацию @ImportResource , в параметрах которой необходимо указать нужное вам количество XML-конфигураций. Выглядит это следующим способом: IoC контейнер выполняет разрешение зависимостей бинов в следующем порядке: Spring каждый раз при создании контейнера проверяет конфигурацию каждого бина. И только бины с областью видимости(scope) singleton создаются сразу вместе со своими зависимостями, в отличие от остальных, которые создаются по запросу и в соответствии со своей областью видимости. В случае цикличной зависимости(когда класс А требует экземпляр В, а классу В требуется экземпляр А) Spring IoC контейнер обнаруживает её и выбрасывает исключение BeanCurrentlyInCreationException . Spring контейнер может разрешать зависимости между бинами через autowiring(далее, автоматическое связывание). Данный механизм основан на просмотре содержимого в ApplicationContext и имеет следующие преимущества: Для того, чтобы воспользоваться механизмом автоматического связывания, Spring Framework предоставляет аннотацию @Autowired . Примеры применения приведены ниже: Т.к. кандидатов для автоматического связывания может быть несколько, то для установки конкретного экземпляра необходимо использовать аннотацию @Qualifier , как показано ниже. Данная аннотация может быть применена как к отдельному полю класса, так и к отдельному аргументу метода или конструктора: Соответственно, у одной из реализации GreetingService должна быть установлена соответствующая аннотация @Qualifier : Spring также поддерживает использование JSR-250 @Resource аннотации автоматического связывания для полей класса или параметров setter-методов: Spring Framework поддерживает JSR-330 аннотации. Эти аннотации работают таким же способом, как и Spring аннотации. Для того, чтобы работать с ними, необходимо добавить в pom.xml следующую зависимость: Ниже приведена таблица сравнения JSR-330 и Spring аннотаций для DI: Мы пишем логику через аннотации или настраиваем бины в XML либо пользуемся java-config и все чудесным образом работает. Рассмотрим для пример на основе XML. Остальные будут рассмотрены ниже. Важное значение имеет XmlBeanDefinitionReader (реализаует интерфейс BeanDefinitionReader). С его помощью происходит настройка контекста. Мы в XML описываем бины, из этого XML поднимаем контекст и с помощью XmlBeanDefinitionReader это все работает, т.е. он является внутренним компонентом Spring: он сканирует XML и все что мы там пишем переводит в BeanDefinitions — это объекты, которые хранят информацию про бины. Давайте более подробно остановимся на этом этапе. В XML мы прописали бины из каких-то классов. Когда поднимается контекст на первом этапе XmlBeanDefinitionReader считывает из XML все декларации бинов. А именно, XmlBeanDefinitionReader загружает документ при помощи DefaultDocumentLoader. Каждый элемент документа обрабатывается и если он является бином, то создается BeanDefinition на основе каких-то заполненных данных. Каждый BeanDefinition помещается в Map и хранится в классе DefaultListableBeanFactory. Создались BeanDefinitions. Что такое BeanDefinitions ? В первую очередь это интерфейс. У интерфейса BeanDefinitions есть много имплементаций. Пока бины не созданы, мы имеем доступ к метаданным класса. Далее отрабатывает BeanFactoryPostProcessor. Это интерфейс, который позволяет настраивать BeanDefinitions до того, как создаются бины. Он работает на этапе, когда кроме BeanDefinitions еще ничего нету (когда никаких бинов еще нет, ничего нет) и может что-то “подкрутить” в BeanDefinitions. Он создается через BeanDefinitionRegistryPostProcessor, который при помощи класса ConfigurationClassParser парсирует JavaConfig(например если используем конфигурацию через аннотации) и создает BeanDefinition. Он содержит единственный метод postProcessorBeanFactory()(который в качестве параметра принимает ConfigurableListableBeanFactory — о ней написано ниже) у которого есть доступ к BeanFactory, т.е. мы можем как-то повлиять на BeanFactory до того, как он начнет работать и повлиять на BeanDefinitions до того, как BeanFactory из них начнет создавать бины. BeanFactory — основной ключевой игрок на поле Spring. О нем поговорим чуть позже. Далее BeanDefinitionReader кладет бины в map BeanDefinition. BeanFactory из этих BeanDefinition вначале достает определения этих BeanPostProcessor-ов, создает их и кладет в сторону, т.е. в первую очередь он создаст те бины, которые имплементируют интерфейс BeanPostProcessor и с их помощью будет потом настраивать все остальные бины, которые мы с вами прописали в XML. BeanFactory — важный элемент структуры, т.к. он отвечает за создание и хранение всех объектов (занимается созданием экземпляров бинов, которые создаются на основе ранее созданных BeanDefinition). Это generic интерфейс. Он изначально был сделан для того, чтобы у разработчика была возможность управлять процессом создания бинов. BeanPostProcessor — это интерфейс. Он позволяет настраивать бины прежде чем они попадут в контейнер. Здесь задействован паттерн “Change Of Responsibility” (“цепочка обязанностей””). У этого интерфейса обязательно нужно переопределить 2 метода, если вы собираетесь его имплементировать: После того, как объект создался и настроился (т.е. BeanFactory его вначале настроит согласно конфигурации) и до init() метода, BeanFactory сначала передает его BeanPostProcessor-у чтобы он сделал с ним что-нибудь (метод postProcessBeforeInitialization()). Он что-то сделал или ничего не сделал и вернул объект обратно в BeanFactory. Объект прошел первую стадию настройки при помощи BeanPostProcessor. Затем вызывается init() метод у данного бина. Затем после init() метода идет еще один проход: BeanFactory передает объект BeanPostProcessor-у чтобы он сделал с ним что-нибудь (метод postProcessAfterInitialization()). Он что-то сделал или ничего не сделал и вернул объект обратно в BeanFactory. Объект прошел вторую стадию настройки при помощи BeanPostProcessor. В итоге мы получаем полностью настроенные объекты. В init() методе мы пишем логику, которая инициализирует бин. Почему для этого нельзя воспользоваться конструктором ? На этапе работы конструктора еще ничего не настроено, ничего нету, бин не настроен. Потому что вначале просканировался XML, затем создались BeanDefinitions, Spring создает бин (при условии что он “singleton”): при помощи Reflection запускается конструктор, конструктор отработал, объект создался. И только когда объект создан Spring может его настроить, т.е. если в конструкторе мы пытаемся обратиться к каким-то вещам, которые должен настроить Spring (ведь их нет еще), то в лучшем случае мы получим NPE. @PostConstruct — это иными словами init() метод, а на этапе init() метода бин еще не до конца сконфигурирован и поэтому если у бина есть какие-то proxy, аспекты, которые для него что-то делают, этого на этапе @PostConstruct еще не существует. Поэтому нежелательно использовать @PostConstruct для вызова бизнес логики. По умолчанию XML ничего не знает про аннотации. Про аннотации знают BeanPostProcessor. Аннотацию @PostConstruct (обозначает init() метод) должен обрабатывать какой-то BeanPostProcessor, а именно CommonAnnotationBeanPostProcessor. Для этого в XML файле надо добавить его. Но если детальней провалиться внутрь, то мы увидим класс CommonAnnotationBeanPostProcessor, который имплементирует интерфейс BeanPostProcessor и отвечает за аннотацию @PostConstruct. Вы можете написать свой собственный BeanPostProcessor, вам ничто не мешает это сделать. Только не забудьте пометить его как бин (с помощью XML или аннотаций @Component), иначе он у вас не отработает. Давайте рассмотрим такой пример, в котором вы написали свою аннотацию и хотите чтобы над всеми методами, которые помеченные этой аннотацией, выполнялась какая-то ваша логика. Вы напишите свой BeanPоstProcessor, который будет относиться к вашей аннотации. Есть 2 важных правила написания: Что он будет делать ? Он будет получать бин от BeanFactory, и будет проверять не стоит ли над классом аннотация @Вася (ваша аннотация) и если стоит, то он должен будет в каждый метод этого бина дописывать какую-то логику. В java к сожалению (а может и к счастью) нельзя просто взять и подставить новую логику в уже существующий метод. Надо будет сгенерировать новый класс proxy. Есть важное правило: никто не должен заметить подмены! 2 правила создания: Немного об аннотациях: Создали свою аннотацию: @Target(value=ElementType.FIELD) где newImplMyTestAnnotation — ваша новая имплементация. Аннотация @Retention позволяет указать жизненный цикл аннотации. SOURCE — видна только в исходном коде. Когда мы компилируем, в байткоде ничего не будет, в рантайме тоже ничего о ней не знаем. Аннотация @Target указывает, что именно мы можем пометить этой аннотацией: это может быть поле, метод, тип и т.д. Иными словами область ее применения. @Inherited — наследуется потомками (аннотация будет распространяться на всех потомков) Когда мы написали свою аннотацию, чтобы она отработала нам надо в XML для нее указать свой BeanPostProcessor, иначе не отработает, т.к. про нее никто не знает. либо добавить @Component над MyTestAnnotationBeanPostProcessor, указав его как бин. Для примера мы можем задать смысловую логику аннотации @MyTestAnnotation как Deprecated и в случае ее использования рекомендовать использовать новую имплементацию с какой-то новой логикой. В таком случае указываем новую имплементацию в самой аннотации и пишем свой BeanFactoryPostProcessor, который будет содержать следующий алгоритм: У BeanFactoryPostProcessor есть PropertySourcesPlaceholderConfigurer, который “проинжектит” значения из property файла в нужные поля класса и в результате создания экземпляра класса вместо @Value(“$”) над полями класса мы увидим готовые значения из файла property. Также PropertySourcesPlaceholderConfigurer может быть добавлен в цикл настройки созданных BeanDefinition. Поэтому вытаскивать сами бины неправильно. Надо вытаскивать их BeanDefinitions и уже в них искать ту информацию, которая интересует. 2. пробегаемся по всем BeanDefinitions 3. получаем конкретный BeanDefinition (здесь еще содержится просто метадата, мы можем получить имя класса как String, но не можем получить сам класс, т.к. он еще не загружен) 4. вытаскиваем название (имя) класса из BeanDefinition (из какого класса будет создаваться бин). Но эта информация будет пуста, если мы определяем бин через Java-config ! 5. вытаскиваем класс для BeanDefinitions по имени 6. с помощью beanClass.getAnnotation(MyTestAnnotation.class) достаем аннотацию @MyTestAnnotation 7. проверяем, если аннотация есть, значит бин устарел и в его BeanDefinitions надо установить новое имя для класса (новая имплементация), которое можно взять из этой аннотации (newImplMyTestAnnotation) public class MyTestAnnotationBeanFactoryPostProcessor implements BeanFactoryPostProcessor @Override String[] names = beanFactory.getBeanDefinitionNames(); > Данные махинации опасны для production среды, т.к. мы прописали один бин и на него можем сделать proxy, а в итоге нам придет совершенно другой класс, т.к. наш BeanFactoryPostProcessor изменил BeanDefinitions и заменил их. Пару слов о proxy: Если Spring аспекту нужно сделать proxy на какой-то объект, то он вначале смотрит на его интерфейсы, и если они есть, то использует Dynamic proxy, а если их нету, то CGLib. (С 3.2 версии CGLib идет вместе с Spring) Т.е., согласно документации, для создания прокси объектов может использоваться как Dynamic Proxy (JDK) так и CGLib, но предпочтение должно отдаваться Dynamic Proxy. И, если класс имеет хотя бы один интерфейс, то именно Dynamic Proxy и будет использоваться (хотя это можно изменить, явно задав флаг proxy-target-class). При создании прокси объекта с помощью Dynamic Proxy на вход передаются все интерфейсы класса и метод для имплементации нового поведения. В результате получаем объект, который абсолютно точно реализует паттерн Proxy. Все это происходит на этапе создания бинов, поэтому, когда начинается внедрение зависимостей, то в реальности внедрен будет этот самый прокси-объект. И все обращения будут производиться именно к нему. Но выполнив свою часть функционала, он обратиться к объекту исходного класса и передаст ему управление. Если же этот объект сам обратиться к одному из своих методов, то это будет уже прямой вызов без всяких прокси. Еще на этапе создания бинов есть то самое “магическое” место, где собственно и производится выбор какую из библиотек использовать. Происходит это в классе DefaultAopProxyFactory, а именно в методе Другими словами, Spring AOP не просто использует CGLib для создания наследников от классов бинов, а реализует на этом этапе полноценный прокси объект (т.е. объект соответствующий паттерну Proxy). Абсолютно без разницы, какую библиотеку под капотом использует Spring. В любом случае поведение его будет одинаковым. В любом случае для организации сквозного программирования будет создан прокси объект, который собственно и обеспечит вызовы методов объекта реального класса. С другой стороны, если какие-либо методы вызываются из методов этого же класса, то перехватить (перекрыть) средствами Spring AOP их уже не получиться. Здесь в момент вызова метода A() на самом деле вызывается метод прокси объекта. Сам прокси объект не выполняет вызовов методов целевого объекта, он содержит цепочку интерсепторов, которые делают это. Создается новая транзакция и далее происходит вызов метода B() класса Test. А когда из A() вызовем B(), обращения к прокси нет, вызывается уже сразу метод нашего класса и, соответственно, никаких новых транзакций создаваться не будет. Вернемся к нашим BPP: Те BeanPostProcessor-ы, которые в классе что-то меняют, они должны это делать на этапе postProcessAfterInitialization(), а не на этапе postProcessBeforeInitialization(). Нельзя ставить @PostConstruct над методом, который что-то принимает). Аннотация @PostCоnstruct всегда работает на оригинальный метод до того, как все proxy на него “навесились”. Если мы заменяем оригинальный объект на proxy до того, как отработал init() метод, то тот механизм в Spring, который ищет аннотацию @PostConstruct и запускает init() метод просто не сможет ее найти, т.к. он уже имеет дело не с оригинальным классом, а с proxy, в котором нет никаких аннотаций. Вывод: Dynamic proxy могут перекрывать только public методы. CGLib proxy — кроме public, также и protected методы и package. Соответственно, если мы явно не указали для среза (pointcut) ограничение “только для public методов”, то потенциально можем получить неожиданное поведение. Ну а если есть какие-либо сомнения, можно принудительно включить использование CGLib для генерации прокси-объектов (в последних версиях Spring идет по умолчанию). Пару слов про CTW и LTW У нас есть возможность использовать компилятор AspectJ. Т.е. фреймворк написан так, что если мы для описания аспектов используем аннотации @AspectJ, то нам не придется вносить никаких изменений, что бы перейти от runtime weaving к CTW (compile-time weaving). Для этого нам надо только подключить плагин, который и выполнит компиляцию. При выполнении этого кода (при условии что мы создали свою аннотацию @MyAnnotation и написали для нее некую логику при использовании Spring AOP мы получим: а при использовании CTW: т.к. связывание кода произошло не только в месте исполнения методов, но и в месте их вызовов. Дело в том, что Spring AOP имеет ограничение, которое позволяет связывать код только по месту исполнения. Возможности AspectJ в этом плане значительно шире. Также для компилятора AspectJ не нужна аннотация @Component. Но если мы ее уберем, то Spring AOP не найдет такого бина и аспект не будет задействован. 2. LTW (Load-time weaving) LTW в Spring доступно из коробки. Все что надо сделать — в классе конфигурации добавить еще одну аннотацию: @EnableLoadTimeWeaving Для этого случая выше написанный код выполнится так: Для выполнения должен использоваться класс InstrumentationLoadTimeWeaver. В нем метод, который должен вызываться на этапе создания бинов И в этом случае Spring AOP создает все те же прокси объекты, о которых мы говорили чуть выше. С тем лишь отличием, что связывание теперь будет производиться не на этапе исполнения, а уже на этапе загрузки классов. Вывод: Spring AOP является proxy-based фреймворком. Это значит, что всегда будут создаваться прокси-объекты на любой наш бин подпадающий под действие аспекта. Если нам все-таки надо добиться, что бы в рассматриваемом примере выполнялся вызов одного метода класса другим (с аспектами), мы можем “заинжектить” сервис сам в себя при условии, что бин —singleton (используя последнюю версию Spring): Не могу обойти стороной транзакции. Пару слов о том, если объявлена аннотация @Transactional над классом. Более подробно про транзакции в Spring я хочу рассказать в отдельной статье. В какой момент BeanPostProcessor, отвечающий за транзакцию, добавит в класс логику, связанную с этой транзакцией ? Он это сделает после того, как PostConstruct (метод init()) отработал, т.к. транзакции на этапе PostConstruct еще не существуют (она еще просто не настроена). Напоминаю, у нас есть 3 этапа: т.е. другими словами PostConstruct работает до того, как настроились все proxy! (включая те proxy, которые отвечают за транзакции). Например если метод будет одновременно транзакционными и помечен как @PostConstruct, то он отработает без транзакции! Потому что Spring вначале создает объект, потом настраивает его, отрабатывает его init() метод и только потом на него накручиваются все proxy ! Т.е. init() метод работает “до” того, как есть proxy, а транзакция как раз имплементируются про помощи proxy. Если бин “singleton”, то по умолчанию он создается сразу, как только поднимается контекст и складывается в контейнер. Они находятся в контексте Spring, он их кэширует. А все “prototype” создаются в тот момент, когда они нужны. Spring их нигде не хранит. Если у “singleton” бина есть destroy() метод, то после завершения контекста Spring проходит по всем бинам, которые хранятся в контейнере, находит у них destroy() методы (если они прописаны) и вызывает их и следовательно удалит “singleton” бин. Важно заметить, что у “prototype” не вызовет, потому что Spring нигде его не хранит. Как только кому-то потребовался “prototype” бин, Spring его сделал, отдал вам и забыл про него. Это важно знать! Немаловажный игрок на нашей арене — это ApplicationListener. Он умеет слушать контекст Spring, все “events”, которые с ним происходят. Работает на этапе, когда все уже создано. Также он имеет дженерики <>, в которых мы можем указать что конкретно мы хотим слушать. Обозначается аннотацией @EventListener. — contextStartedEvent — контекст начал свое построение (не построился, а только начал) Теперь мы получше знаем как происходит поэтапное создание объектов, используя XML. А что на счет других вариантов, таких как аннотации и java-config ? Какие есть способы настройки Spring: Пару слов про конфигурацию через аннотации: Чтобы кто-то просканировал пакеты, в которых лежат классы, аннотированные @Component, есть 2 пути: Как работает AnnotationConfigApplicationContext ? У него есть 2 поля: private final AnnotatedBeanDefinitionReader reader; Его ClassPathBeanDefinitionScanner является аналогом XmlBeanDefinitionReader для XML, описанный выше. ClassPathBeanDefinitionScanner не является по своей структуре BeanPostProcessor-ом. Он подгружает дополнительные ресурсы, т.е. сканирует пакет и ищет все бины, аннотированные @Component или любой другой аннотацией, которая относится к бину (@Component, @Service, @Repository, @Controller). Он создает дополнительные BeanDefenitions из всех классов, аннотированные @Component и т.д. Найденные классы парсируются и для них создаются BeanDefinition. Затем AnnotatedBeanDefinitionReader начинает работать: Java-config: Java-config сам по себе является бином. Сканирует пакеты AnnotationConfigApplicationContext, как и в случае с аннотациями. Java-config парсирует класс AnnotatedBeanDefinitionReader, аналог XMLBeanDefinitionReader для XML. Когда мы создаем AnnotationConfigApplicationContext и передаем в него 1 или несколько Java-сonfig, то у AnnotationConfigApplicationContext есть внутри AnnotatedBeanDefinitionReader, который делает всю логику: он только регистрирует все Java-сonfig-и а также является частью ApplicationContext. Java-сonfig — это Java файл и на него при помощи CGLib накручивается proxy и каждый раз, когда нужен бин, происходит делегация в тот метод, который мы пропишем в Java-сonfig. Пару слов о BeanDefinitions, сделанных через Java-Config. BeanDefinitions для тех бинов, которые декларируются через Java-config отличаются. В них нету информации о том, из какого класса они сделаны. т.к. пока не запущен метод никаким образом нельзя узнать что этот метод вернет! Когда мы пишем бины в Java-config, мы в основном концентрируемся на том, что говорим Spring при помощи какой логики надо создать бин. В Groovy-config, XML, аннотациях — все работает через Reflection: Spring сам знает название класса и через Reflection его создает. В @Bean мы можем указать как будет называться init() метод, destroy() метод, также можем указать id для бина (по умолчанию Spring возьмет имя метода в качестве id бина), но указать из какого класса будет создан бин мы никак не можем! В какой момент мы можем узнать из какого класса создался бин ? Когда создался объект и сделав у него getClass(), но это уже происходит на этапе ”после”. Groovy-config: Начиная с версии Spring 4 появилась возможность прописывать бины через Groovy-config. Создается при помощи new GenericGroovyApplicationContext(“context.groovy”); Из существенных минусов можно выделить то, что в нем пока нету таких штуковин, как ComponentScan. В чем отличие Java-config от Groovy-config ? С помощью Java-config мы пишем код, который будет создавать наши объекты. Он считается немного лучше XML тем, что в нем можно писать код, но этот код не относится к созданию объекта. Пример: Название бина и его имплементация. Просто и лаконично. beans coolDao(CoolDaoImpl) coolService(CoolService) Какие типы контекста мы знаем ? 1. ClassPathXmlApplicationContext Они отличаются друг от друга тем, каким способом задаются мета-данные и где хранится эта конфигурация. Например: Надеюсь данная статья будет полезна. На написание этой статьи на меня повлияли видео талантливого человека Евгений Борисова. Огромное ему спасибо! Если вы нашли неточности в описании данной статьи, вы можете написать мне на email и я с радостью вам отвечу. Kirill Sereda This article explores “What is Bean in Java Spring”- Inversion of Control and the most commonly used spring bean, how to use it, bean scopes in spring, and the bean lifecycle. We will also explain the semantics represented by each of the Spring stereotype annotations. The table of contents below allows you to quickly navigate to different sections of the Java bean string tutorial in detail. Get Trained And Certified Get Trained And Certified Java Spring Community Java Bean is a modular programming framework. Beans are the objects that make up the framework of your Spring application and are controlled by the Spring IoC container so that they can be accessed in different contexts. Not only that, but it’s also very low maintenance. Learn how Java spring bean annotations lead to a clean, layered design and how the different parts of an application can be kept separate. Also, they make configuration smaller because we no longer have to define beans manually. The configuration metadata you give the container is used to make these beans. So get into brief! The Spring Bean is the fundamental component of any Spring application. To put it simply, a bean is an object that is created, constructed, and managed by the Spring IoC container. We must understand it before we begin working with the Spring Framework. The Spring Framework relies heavily on the idea of a Spring Bean. Understanding this concept is critical for grasping the framework and using it effectively. The Spring IoC manages the life cycle of the Spring Bean. JavaBeans are classes that contain many objects into one object (the bean). The word «Bean» encompasses the following standards, which strive to produce reusable Java software components. To put it briefly, Inversion of Control (IoC) is a technique whereby an object declares its dependencies without really creating them. When passed to an IoC container, this object instructs it to construct such dependencies. Before we get into the IoC, let’s start by declaring a few domain classes. Let’s say there’s a class declaration: This class requires a partner of type Address: Typically, we use the constructors for a class to create objects: Consider a programme with dozens or perhaps hundreds of classes. Sometimes we wish to share a single instance of a class throughout the entire application, other times we require a unique object for each use case, etc. The management of so many objects is nothing short of a nightmare. Here is when control inversion comes to the rescue. Instead of manually creating its dependencies, an object can retrieve them through an IoC container. All that is required is to give the container with configuration details. First of all, let’s add the @Component annotation to the Company class: A bean of type Address is made by the configuration class. It also has an annotation called @ComponentScan, which tells the container to look for beans in the same package as the Company class. When a Spring IoC container makes objects of these types, they are all called Spring beans because the IoC container manages them. To construct a container from the beans we defined in the configuration class, we require an instance of the AnnotationConfigApplicationContext class: A quick test proves that our beans exist and what they are worth: The result shows that the beans were made and set up correctly by the IoC container Basically, the method responsible for retrieving a bean instance from the Spring container is the BeanFactory.getBean() method. Usually, in the place of BeanFactory, we use one of the known implementing classes as by documentation here. Whatever implementation you use, there are (by BeanFactory) different signatures by which we can retrieve a bean from the Spring container: If you give it a name, it will return an object if there is a bean in the application context with that name. If not, it will throw a NoSuchBeanDefinitionException. In the presence of a name and a type, this method returns a bean of that type if it exists in the application environment; this method can be helpful because it eliminates the need to cast the object returned. In the case that no such bean definition exists, this method will throw NoSuchBeanDefinitionException; otherwise, it will throw BeanNotOfRequiredTypeException. it’s very much like the previous one, except that there may be multiple instantiated beans of the same type, leading to a NoUniqueBeanDefinitionException. It is possible to use the following syntax to specify the bean name and function Object() parameters: In this case, we get a 5-year-old animal named Fuffy. But if the bean is singleton (not a prototype), we can get BeanDefinitionStoreException. It is similar to the last, but we have to pass the type instead of the name, and the bean can’t be a singleton: To retrieve beans from the container, we typically use the ApplicationContext to invoke these primary methods provided by the BeanFactory interface. Moreover, we rely on auto wiring and dependency injection to get the beans inside another Spring bean so that we don’t have to combine logic with a framework-related technique for getting beans. Some of the most used spring bean annotations include: @Component is an annotation that goes on a class. Spring Framework automatically finds classes marked with @Component during the component scan: As a default, all bean instances of this class will share the same name as the class name, but with a lowercase beginning. The value argument of this annotation is optional and can be used to define a different name. Beans annotated with @Configuration, @Service, @Repository, or @Controller all follow the same conventions for naming due to the fact that they are all meta-annotations of @Component. Spring also detects them mechanically during the scanning procedure for individual components. Classes annotated with @Repository are typically DAOs or repositories and constitute the application’s database access layer. The use of this annotation allows for the automatic translation of persistence exceptions, which is a definite plus. By default, Spring will convert any native exceptions thrown inside a class annotated with @Repository using a persistence framework like Hibernate into derived classes of Spring’s DataAccessException. The @Service annotation will be used to designate a class as part of the service layer, which typically houses the application’s business logic: In Spring MVC, a class’s use as a controller is indicated to the Spring Framework using the @Controller class annotation. Related Article>>> Spring Boot vs Spring MVC Methods annotated with @Bean can be found in configuration classes that define beans. Related Article>>> Spring Cloud Tutorial The bean’s scope determines how long it will live and who can see it in various scenarios. Singleton and prototype are examples of basic scopes, while request, session, application, and WebSocket are examples of web-aware scopes that can be used with beans. You can declare a bean’s scope when you define it. For the spring bean example, to make a new bean instance every time one is needed, you should set the bean’s scope attribute to prototype. There are a total of five scopes available within the Spring Framework. However, three of them require a web-aware ApplicationContext in order to be used. In the spring, there are many ways to use your beans. The most important parts of the framework are In addition, Spring provides its own set of web-specific bean scopes in spring: Each and every metadata configuration holds the attributes necessary to define a Spring bean. Almost all of Spring’s properties have default settings that can be used to quickly and easily create working bean declarations. It is useful to understand how to alter the default settings. When you define a bean, you link it to a specific class in your programme. The primary characteristic of a bean is this class itself. When Spring searches for a bean’s dependents, the class property is used as the default identifier. Does that rule out the possibility of having different bean definitions for the same class? No. It’s not impossible. However, in such a scenario, you need to use a different bean identifier: a name, to avoid confusion. This property must always be present. This file outlines the Spring Framework bean class that must be present in order to construct the bean. Spring bean name is a string that is unique to each bean that Spring uses to identify it. In contrast to the bean class, names must be unique throughout the entire application. You can’t talk about two different kinds of beans with the same name. You don’t have to set a name for every bean you make, which is good news. Spring makes up names on the fly that it uses internally. You can use the default setup if you don’t want to use bean names to find them. The main time you need to use bean names is when you use the @Bean annotation to define multiple beans for the same class. In this case, names let you find a specific instance of a bean that you want to use as a dependency for another bean. Beans are objects that can be utilized to do a variety of tasks by utilizing other beans. Whenever Spring builds an object with defined dependencies, the framework must first generate those dependencies. There may be further requirements within these dependencies. Object-oriented programming typically involves dealing with a massive network of interconnected items. We do not need to deliberate on the details of building this graph. Object creation doesn’t require us to consider a certain timeline. For us, spring accomplishes all of this. The spring container is in charge of the entire bean life cycle. The spring container is the first thing that gets started whenever we run the programme. The container then constructs the requested bean instance and injects any necessary dependencies. When the spring container is closed, the bean is permanently closed. To implement the bean life cycle in Spring, the Spring framework gives you two annotations: In most cases, the names of annotations explain what they are for. This method is called «postConstructs» because it is performed after the class’s properties have been constructed. The term «PreDestroy» refers to the action taken just before the bean is destroyed. 1) Start up your Eclipse IDE and go to new > project > Maven Project > next. 2) Click «Create a simple project (skip archetype selection)» 3) Tell me what the Group Id and Artifact Id are. Group Id uniquely identifies your project among all. So it could be something like com.dev.spring.lifecycle. Artifact Id could be something like a project name, so we could put SpringLifeCycleExample as the artifact id here. 4) Hit the «Finish» button. You’ll get a new project based on Maven. A pom.xml file is generated whenever a new project is started. Providing the dependencies for the most fundamental Spring jars is what this file is about. The @PostConstruct and @PreDestroy annotations are not part of the Spring, which is a key distinction to make. They are included in the common-annotations.jar file that is part of the Java EE (JEE) package. In order to use these annotations after Java EE was deprecated in Java 9 and deleted in Java 11, an additional dependency, javax.annotation-api, is required. There is no need to add this dependency if you are not using Java 8 or higher. Put the following code inside the dependencies> element in your pom.xml file. The most recent stable release of your choosing can be provided if you so want. When the pom.xml file is saved, the necessary jars are downloaded immediately. Now, we must now develop a POJO class with two life cycle methods that use annotations: @PostConstruct and @PreDestroy. Don’t forget to annotate the two life cycle approaches you to deploy. In addition to defining the bean, the tag must be specified. Annotations within the programme will become functional. This tag is required for using annotations in your application. Spring’s documentation on XSD Configuration can be consulted for the necessary schema definitions before constructing an XML configuration file. To facilitate our planned use of the Spring framework’s beans and context module, we have extended the schema to include only those components. Let’s define a main() method in a test class and see if our code works. In order to use the destruct function of the spring bean life cycle, we need to call registerShutdownHook() method. AbstractApplicationContext provides access to this function. Hence, we are utilizing AbstractApplicationContext interface in place of ApplicationContext interface to access spring context. When the programme is executed, the results will appear as shown below: From the output, it’s clear that the init() method is called after all the properties have been set. At the last call to the destroy() method. In a Spring boot application, a bean is just a Java object that is made when the application starts by the Spring framework. The object could be a configuration, a service, a database connection factory, or just about anything else. Example: We can use both annotation-based and XML-based configurations for stand-alone apps. The only rule is that the context has to be set up somewhere in the programme before it can be used. Here, we’ll go through three distinct methods for creating a bean in spring boot. The scope of spring beans enables more granular control over the production of bean instances. Sometimes we want to construct a bean instance as a singleton, while in other instances, we may want it to be created once per request or session. IoC Spring (Inversion of Control) is the heart of the Spring Framework as the container. Everything about the objects’ creation, configuration, and assembly, as well as their full life cycle, is handled by this. The Container employs Dependency Injection(DI) to manage the components that make up the application. The spring container takes care of the life cycle of beans. Java Bean is serializable at all times. Serializable does not declare any methods; instead, it serves as a marker to inform the Object Serialization tools that your bean class is serializable. Marking a class as Serializable indicates to the Java Virtual machine (JVM) that the class is compatible with the default serialization method. JavaBeans is a model that can be moved around and works on any platform. It is written in the Java Programming Language. The parts of it are called «beans.» JavaBeans are classes that put together a group of objects into a single object. It makes it easier to get to these objects from more than one place. Enterprise JavaBeans or EJB is one piece of technology that may seem old-fashioned now that APIs are becoming more popular, but it is still used by more than 23,500 companies around the world. To wrap up, this article has covered the Java spring bean definition and configuration metadata. In addition, we saw how the Spring IoC container handles them. A working example was also defined in order to obtain a comprehensive analysis of the topic. Now, you are also familiar with how Spring builds objects based on the bean definitions you provide. Feel free to ask any questions in the comment section.To enrich your career in Java spring, enroll your names on Java Spring Training Course to gain in-depth knowledge of the spring configurations.Описание работы IoC контейнера

Настройка IoC контейнера
package lessons; import org.springframework.context.annotation.Configuration; /** * Конфигурационный класс Spring IoC контейнера */ @Configuration public class LessonsConfiguration
public class Starter < private static final Logger logger = LogManager.getLogger(Starter.class); public static void main(String[] args) < logger.info("Starting configuration. "); ApplicationContext context = new AnnotationConfigApplicationContext(LessonsConfiguration.class); >>public class Starter < private static final Logger logger = LogManager.getLogger(Starter.class); public static void main(String[] args) < logger.info("Starting configuration. "); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(LessonsConfiguration.class); context.refresh(); >>Использование @Bean аннотации
package lessons.services; public interface GreetingService
package lessons.services; public class GreetingServiceImpl implements GreetingService < @Override public String sayGreeting() < return "Greeting, user!"; >> @Configuration public class LessonsConfiguration < @Bean GreetingService greetingService() < return new GreetingServiceImpl(); >> public class Starter < private static final Logger logger = LogManager.getLogger(Starter.class); public static void main(String[] args) < logger.info("Starting configuration. "); ApplicationContext context = new AnnotationConfigApplicationContext(LessonsConfiguration.class); GreetingService greetingService = context.getBean(GreetingService.class); logger.info(greetingService.sayGreeting()); // "Greeting, user!" >>@Bean(name = "gServiceName")@Bean(name = )@Bean @Description("Текстовое описание бина greetingService") GreetingService greetingService()
Жизненный цикл бина
public interface Lifecycle
public interface LifecycleProcessor extends Lifecycle
public interface SmartLifecycle extends Lifecycle, Phased
public class Starter < private static final Logger logger = LogManager.getLogger(Starter.class); public static void main(String[] args) < logger.info("Starting configuration. "); AbstractApplicationContext context = new AnnotationConfigApplicationContext(LessonsConfiguration.class); GreetingService greetingService = context.getBean(GreetingService.class); logger.info(greetingService.sayGreeting()); // "Greeting, user!" context.registerShutdownHook(); >>@Configuration @ComponentScan public class LessonsConfiguration < @Bean GreetingService greetingService() < return new GreetingServiceImpl(); >>package lessons.services; public class GreetingServiceImpl implements GreetingService < private ApplicationContext context; @Required public void setContext(ApplicationContext context) < this.context = context; >>Области видимости(scopes) бинов
@Component @Scope("prototype") public class GreetingServiceImpl implements GreetingService < //. >Использование @Configuration аннотации
@Configuration @ComponentScan public class LessonsConfiguration < @Bean BeanWithDependency beanWithDependency() < return new BeanWithDependency(greetingService()); >@Bean GreetingService greetingService() < return new GreetingServiceImpl(); >>public abstract class CommandManager
@Configuration @ComponentScan public class LessonsConfiguration < @Bean @Scope("prototype") public Object asyncCommand() < return new Object(); >@Bean public CommandManager commandManager() < // возвращаем новую анонимную реализацию CommandManager // с новым объектом return new CommandManager() < protected Object createCommand() < return asyncCommand(); >>; > >@Configuration public class AnotherConfiguration < @Bean BeanWithDependency beanWithDependency() < return new BeanWithDependency(); >>@Configuration @ComponentScan @Import(AnotherConfiguration.class) public class LessonsConfiguration < @Bean GreetingService greetingService() < return new GreetingServiceImpl(); >>public class Starter < private static final Logger logger = LogManager.getLogger(Starter.class); public static void main(String[] args) < logger.info("Starting configuration. "); ApplicationContext context = new AnnotationConfigApplicationContext(LessonsConfiguration.class); GreetingService greetingService = context.getBean(GreetingService.class); BeanWithDependency withDependency = context.getBean(BeanWithDependency.class); logger.info(greetingService.sayGreeting()); // "Greeting, user!" logger.info(withDependency.printText()); // "Some text!" >>@Configuration public class AnotherConfiguration < @Autowired GreetingService greetingService; @Bean BeanWithDependency beanWithDependency() < //что-нибудь делаем с greetingService. return new BeanWithDependency(); >>@Configuration @ComponentScan @Import(AnotherConfiguration.class) public class LessonsConfiguration < @Bean GreetingService greetingService() < return new GreetingServiceImpl(); >>@Configuration @ImportResource("classpath:/lessons/xml-config.xml") public class LessonsConfiguration < @Value("$") String url; //. >jdbc.url=jdbc:hsqldb:hsql://localhost/xdbПроцесс разрешения зависимостей
public class AutowiredClass < @Autowired //к полям класса @Qualifier("main") //@Autowired(required = false) //чтобы не бросалось исключение, //если не с кем связать //рекомендуется использовать @Required private GreetingService greetingService; @Autowired //к полям класса в виде массива или коллекции private GreetingService[] services; @Autowired //к Map, где ключами являются имена бинов, значения - сами бины private MapserviceMap; @Autowired //к конструктору public AutowiredClass(@Qualifier("main") GreetingService service) <> @Autowired //к обычным методам с произвольным названием аргументов и их количеством public void prepare(GreetingService prepareContext)* что-то делаем. */> @Autowired //к "традиционному" setter-методу public void setContext(GreetingService service) < this.greetingService = service; >>public class AutowiredClass < //. @Autowired //к полям класса @Qualifier("main") private GreetingService greetingService; @Autowired //к отдельному аргументу конструктора или метода public void prepare(@Qualifier("main") GreetingService greetingService)< /* что-то делаем. */ >; //. >@Component @Qualifier("main") public class GreetingServiceImpl implements GreetingService < //. >public class AutowiredClass < //. @Resource //По умолчанию поиск бина с именем "context" private ApplicationContext context; @Resource(name="greetingService") //Поиск бина с именем "greetingService" public void setGreetingService(GreetingService service) < this.greetingService = service; >//. >Использование стандартных JSR-330 аннотаций
Spring под капотом
Но как оно работает под капотом ? Я задался тем же вопросом./** Map of bean definition objects, keyed by bean name */
private final MapString, BeanDefinition> beanDefinitionMap = new ConcurrentHashMapString, BeanDefinition>(64);
Можно представить его в виде map, который в качестве ключа хранит id бина, а в качестве значения — его декларацию.
Что такое декларация ? Это то, из какого класса надо создавать бин, есть ли у него init() метод и как он называется, какие у него проперти и все остальные подробности бина, которые мы прописываем в XML. Другими словами это интерфейс, через который можно получить доступ к метаданным будущего бина.
Основные из них, это:
— AbstractBeanDefinition
— AnnotatedBeanDefinition — если используем аннотацию @Component
— ConfigurationClassBeanDefinition — с помощью него мы можем узнать то, что нам нужно (какого типа и из какого класса создался бин).
— GenericBeanDefinition
— ScannedGenericBeanDefinition
…
После создания BeanDefinition, BeanFactory начинает по ним работать, создает из классов объекты и все бины складывает в контейнер.
— postProcessBeforeInitialization() — вызывается до init() метода.
— postProcessAfterInitialization() — вызывается после init() метода.
Как я уже упоминал выше, интерфейс BeanPоstProcessor имеет 2 метода, которые вам необходимо переопределить.
1) Если ваш BeanPоstProcessor настраивает объект — пишите логику в методе postProcessBeforeInitialization().
— Затем выполняется init() метод.
Почему именно здесь ?
Т.к. мы хотим вначале полностью настроить объект а потом заворачивать его в proxy. Т.к. например если мы попытаемся искать аннотацию @PostConstruct после того, как сделали proxy, то мы ее не найдем, потому что в proxy ее не будет.
2) Если ваш BeanPоstProcessor меняет поведение (аспекты, транзакции, и т.д. — некая бизнес логика) методов бина путем создания proxy — пишите логику в методе postProcessAfterInitialization().
1. CGLib (Code Generation Library)— он должен наследовать от оригинального класса и переопределять его методы и добавлять туда свою логику (считается хуже, т.к. мы не от любого класса можем наследовать, также есть такое понятие как final методы и т.д.).
2. Dynamic proxy (JDK) — он должен имплементировать те же самые интерфейсы.
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTestAnnotation Class newImplMyTestAnnotation();
>
CLASS — (по умолчанию) видна в скомпилированном файле. Аннотация в байткод попасть должна, но через Reflection в рантайме мы ее считать не сможем, т.е. присутствует только в момент выполнения компилятора.
RUNTIME — видна в процессе выполнения и можно считать через Reflection.
ElementType.ANNOTATION_TYPE — наша аннотация может присутствовать только над другими аннотациями
ElementType.CONSTRUCTOR — аннотация может присутствовать только над конструкторами
ElementType.FIELD — аннотация может присутствовать только над полем класса (любым)
ElementType.LOCAL_VARIABLE — аннотация может присутствовать только над локальной переменной метода
ElementType.METHOD — аннотация может присутствовать только над методом
ElementType.PACKAGE — аннотация может присутствовать только над пакетом (будет распространяться на все классы в пакете)
ElementType.PARAMETER — аннотация может присутствовать только над аргументом метода
ElementType.TYPE — аннотация может присутствовать только над классом или интерфейсом
@Documented — в Javadoc попадает информация о том, что данный класс помечен данной аннотацией
Зачем нам использовать этот интерфейс если можно сделать getBean() ?
Потому что только ConfigurableListableBeanFactory умеет делать getBeanDefinition() — получаем все BeanDefinitions.
Получать бин через getBean() бесполезно и неправильно.
Например: кто-то создал бин lazy (“singleton” по умолчанию создаются все и сразу, но если он lazy, то он не будет сразу создаваться, а только тогда, когда его попросят). Может получится неприятная ситуация когда мы создадим бин, который не должен сейчас создаваться.
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
for (String name : names) BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
String beanClassName = beanDefinition.getBeanClassName();
try Class beanClass = Class.forName(beanClassName);
DeprecatedClass annotation = beanClass.getAnnotation(DeprecatedClass.class);
if (annotation != null) beanDefinition.setBeanClassName(annotation.newImplMyTestAnnotation().getName());
>
> catch (Exception e) e.printStackTrace();
>
>
>
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) Class targetClass = config.getTargetClass();
if (targetClass == null) throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
>
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) return new JdkDynamicAopProxy(config);
>
return new ObjenesisCglibAopProxy(config);
>
else return new JdkDynamicAopProxy(config);
>
>public class Test
@Transactional
public void A() B();
>
@Transactional
public void B() System.out.println("B");
>
>
public class Test
@MyAnnotation
public void A() System.out.println("A");
B();
>
@MyAnnotation
public void B() System.out.println("B");
>
>@Aspect
@Component
public class MyAspect1
@Pointcut("@annotation(com.example.AOPTest.MyAnnotation)")
public void annotated() <>
@Before("annotated()")
public void printABit() System.out.println("Aspect1-Lalala");
>
>Aspect1-Lalala
A
BAspect1-Lalala
Aspect1-Lalala
A
Aspect1-Lalala
Aspect1-Lalala
B@SpringBootApplication
@EnableLoadTimeWeaving
public class YourMainClass .
>Aspect1-Lalala
A
Bpublic void addTransformer(ClassFileTransformer transformer)
public class Test
@Autowired
private Test test;
@MyAnnotation
public void A() System.out.println("A");
test.B();
>
@MyAnnotation
public void B() System.out.println("B");
>
>
— вначале postProcessBeforeInitialization()
— потом PostConstruct (метод init())
— потом postProcessAfterInitialization() — на этом этапе уже все настроилось!
— contextRefreshedEvent — когда контекст заканчивает свое построение, он всегда делает refresh.
— contextStoppedEvent
— contextClosedEvent
1) XML
2) Аннотации
3) Java-config
4) Groovy-config
1. использовать неймспейс: и указываем package, который надо просканировать
2. new AnnotationConfigApplicationContext(“…”);
private final ClassPathBeanDefinitionScanner scanner;
1) регистрирует все @Configuration для дальнейшего парсирования
2) происходит регистрация BeanFactoryPostProcessor (BeanDefinitionRegistryPostProcessor), который парсирует Java-сonfig при помощи класса ConfigurationClassParser и создает BeanDefinition.
Дает возможность настраивать наши бины какой-то своей логикой, т.е. мы пишем код, который будет создавать наши объекты. В таком случае нам не нужны BeanFactory и т.д. Используется аннотация @Configuration.
Если метод помечен аннотацией @Bean мы не сможем узнать тип возвращаемого значения.
В Java-config нет Reflection: Spring сам знает, для того, чтобы создать этот бин надо вызвать этот код. Этот код вернет ему объект и только тогда, когда объект будет получен можно узнать из какого класса он был создан.
Парсируется с помощью GroovyBeanDefinitionReader.
С помощью Groovy-config мы пишем код, который описывает то, как надо создавать BeanDefinition. Мы никак не можем прописать то, каким образом надо создавать сам бин!
bean.scope = ‘prototype’
bean.initMethod = ‘init’
bean.destroyMethod = ‘closeResources’
>
>
2. FileSystemXmlApplicationContext
3. XmlWebApplicationContext
4. AnnotationConfigApplicationContext
5. GenericGroovyApplicationContext
6. StaticApplicationContext
…
— ClassPathXmlApplicationContext — метаданные конфигурируются XML и они лежат в classpath, т. е. в ресурсах модуля
— FileSystemXmlApplicationContext — метаданные тоже конфигурируются XML, но они находятся где-то в файловой системе
— AnnotationConfigApplicationContext — для аннотаций и для Java-Config
— GenericGroovyApplicationContext — для Groovy-ConfigWhat is Bean in Java Spring
search here
search here
Java Spring Articles
Java Spring Quiz
Table of ContentsWhat is bean in java spring?
Inversion of Control (IOC)
1. Domain Classes
public class Company < private Address address; public Company(Address address) < this.address = address; >// getter, setter and other properties > Copypublic class Address < private String street; private int number; public Address(String street, int number) < this.street = street; this.number = number; >// getters and setters >2. Traditional Approach
Address address = new Address("High Street", 1000); Company company = new Company(address);3. Bean Configuration
@Component public class Company < // this body is the same as before >Here's a configuration class supplying bean metadata to an IoC container: @Configuration @ComponentScan(basePackageClasses = Company.class) public class Config < @Bean public Address getAddress() < return new Address("High Street", 1000); >>4. IoC in Action
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);Company company = context.getBean("company", Company.class); assertEquals("High Street", company.getAddress().getStreet()); assertEquals(1000, company.getAddress().getNumber());Get Beans
By name:
#by name Object animal = context.getBean("animal"); By name and type:
#by name and type Animal animal = context.getBean("animal", Animal.class); By type:
#by type Animal animal = context.getBean(Animal.class); By name with constructor parameters:
#by name and constructor parameters Animal fuffyAnimal = (Animal) context.getBean("animal", "Fuffy", 5); By type with constructor parameters:
#by type and constructor parameters Animal fuffyAnimal = (Animal) context.getBean(Animal.class, "Fuffy", 5);Most used Spring Bean annotations
1. @Component
@Component class CarUtility < // . >2. @Repository
@Repository class VehicleRepository < // . >3. @Service
@Service public class VehicleService < // . >4. @Controller
@Controller public class VehicleController < // . >5. @Configuration
@Configuration class VehicleFactoryConfig < @Bean Engine engine() < return new Engine(); >>Bean Scopes In Spring
SINGLETON
Singleton Java configuration:
@Configuration public class AppConfiguration < @Bean @Scope("singleton") //This statement is redundant - singleton is default scope public class BeanClass < System.out.println("A new BeanClass instance created"); return new BeanClass(); >>PROTOTYPE
Prototype Java configuration:
@Bean @Scope("prototype") public class BeanClass
REQUEST
Request Java configuration:
@Bean @Scope("request") public class BeanClass
SESSION
Session Java configuration:
@Bean @Scope("session") public class BeanClass
APPLICATION
Application Java configuration:
@Bean @Scope("application") public class BeanClass
WebSocket
WebSocket Java configuration:
@Component @Scope("websocket") public class BeanClass
Populating Bean properties
Bean class
Bean name
Bean dependencies
Spring Bean Life Cycle annotations?
Step#1: Create a Project in Eclipse/STS
Step#2: Modify pom.xml
Step#3: Create a Pojo Class
package com.dev.spring.bean.lifecycle; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class Person < private String name; private Integer age; public Person() < super(); >public String getName() < return name; >public void setName(String name) < this.name = name; >public Integer getAge() < return age; >public void setAge(Integer age) < System.out.println("Setting Person's Age"); this.age = age; >@Override public String toString() < return "Person [name=" + name + ", age=" + age + "]"; >@PostConstruct public void initMethod() < System.out.println("Inside init method"); >@PreDestroy public void destroyMethod() < System.out.println("Inside destroy method"); >>Step#4: Create a configuration file (config.xml)
Step#5: Create a Test class
package com.dev.spring.bean.lifecycle; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test < public static void main(String[] args) < // ApplicationContext ctx= new ClassPathXmlApplicationContext("com/dev/spring/bean/lifecycle/config.xml"); AbstractApplicationContext ctx= new ClassPathXmlApplicationContext("com/dev/spring/bean/lifecycle/config.xml"); Person person = (Person) ctx.getBean("person"); System.out.println(person); //registering shutdown hook ctx.registerShutdownHook(); >>Step#6: Test Output
Setting Person's Age Inside init method Person [name=Person-1, age=24] Inside destroy method Bean in Java Spring FAQs
1. What is a bean in spring boot with example?
package com.journaldev.spring.main; import java.util.Date; public class MyService < public void log(String msg)< System.out.println(new Date()+"::"+msg); >>2. How to create beans in spring boot?
3. What is the scope of beans in spring?
4. What is IOC in spring?
5. How do beans work in Spring?
6. Why are Java Beans Serializable?
7. What is JavaBean used for?
8. Are JavaBeans still used?
9. What is the difference between JavaBean and Spring bean?
10. What are the features of JavaBeans?
Conclusion
