Как уже стало известно, в основе процесса создания Spring бинов лежит CGLib - очень распространенная библиотека для создания классов в режиме реального времени. Ее используют такие библиотеки как Hibernate, iBATIS и многие другие.
Иногда в spring’e появляется необходимость переопределить некоторые методы бина. Типичный случай - разные жизненные циклы связанных бинов. Например, singleton bean должен работать при каждом вызове своих методов с новым экземпляром связанного с ним не singleton бина. В данном случае Spring не сможет обеспечить такую логику, т.к. связанный бин заинжектируется в момент создания singleton бина.
В Spring’е есть возможность указать родной для первого бина метод, при вызове которого будет создаваться и возвращаться новый экземпляр связанного бина. Помимо этого есть возможность переопределять методы с помощью
MethodReplacer
‘a. Подробнее здесь.
Эти фичи требуют создания proxy объектов бинов, а реализуется все это безобразие с помощью всем известной библиотеки CGLib. После того, как beanFactory определил все метаданные для создания бина и не нашел его в кеше, то он пытается его создать следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
В методе проверяется есть ли в BeanDefinition
объекте переопределенные методы. Если нет, то объект просто инстанцируется с помощью reflection‘а, иначе вызывается метод instantiateWithMethodInjection
, в котором и реализована работа создания proxy объектов бинов через CglibSubclassCreator
. SubclassCreator
имеет основной метод instantiate
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
В этом методе создается Enhancer - объект библиотеки CGLib, который конструирует proxy классы. Указывается класс, от которого стоит унаследоваться. В данном случае это класс самого бина. Далее указываются обработчики переопределяемых методов. На данный момент это три вида MethodInterceptor
‘a:
NoOp.INSTANCE
- константа библиотеки CGLib, передающая управление реальным методамLookupOverrideMethodInterceptor
оборабатывает методы, возвращающие новые экземпляры бинов.ReplaceOverrideMethodInterceptor
обрабатывает методы, переопределенные с помощьюMethodReplacer
‘a.
Оба эти класса объявляены внутри SubclassCreator
‘а. Метод enhancer.create()
конструирует класс и создает экземпляр объекта этого класса.
Стоит заметить, что beanDefintion
и beanFactory
хранятся в полях SubclassCreator
‘а, и объявленные в нем классы имеют доступ к ним. Эти поля помечены модификатором final, что гарантирует доступ только на чтение к значениям этих полей.
Объект бина создан и его можно вернуть для инжектирования в клиентскую программу. При вызове методов бинов, созданных с помощью CGLib, передается управление обработчикам методов, которые указаны у proxy объекта. При создании бина были указаны 3 обработчика и CallbackFilter
. При добавлении обработчика он получает индекс, как в массиве. CallbackFilter
определяет какой из обработчиков выполнится, указывая его индекс. В spring’e создается свой фильтр обработчиков CallbackFilterImpl
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Как можно заметить, переопределяемые методы представляют собой MethodOverride
объект, который определяет его имя, имя объекта, которому он принадлежит и другую мета информацию о методе. Но сам по себе этот класс абстрактный, а наследуют его два класса:
LookupOverride
для объявления порождающих методовReplaceOverride
для объявления методов, переопределяемых произвольной логикой
Определяя тип переопределяемого метода, определяется индекс обработчика. Индекс обработчика задан в виде константы.
При вызове обработчика LookupOverrideMethodInterceptor
вызывается создание бина с помощью beanFactory текущего бина по имени, которое задано в LookupOverride. Это имя попадает туда из соответствующей директивы конфигурации бинов:
1
|
|
При вызове ReplaceOverrideMethodInterceptor
получается указанный в ReplaceOverride
MethodReplacer
и вызывается его основной метод с логикой, куда передается информация о вызванном методе, аргументах и самом объекте. Объект MethodReplacer
также определяется в конфигурации директивой:
1
|
|
Как видно из строчки конфигурации, задается имя бина MethodReplacer
‘a. Поэтому в ReplaceOverride
объекте хранится просто имя, а при обработке вызова метода данным обработчиком по имени бина получается объект с помощью все той же BeanFactory
.
Как видно из данного обзора, подход оперирования именами бинов во всех частях конфигурации в некоторых ситуациях может вызывать достаточно большой overhead при работе с proxy объектами.