Отслеживание причины того, что Spring «не подходит для автоматического проксирования»
Вопрос
Когда вы начинаете возиться с автопрокси Spring, вы часто сталкиваетесь с таким поведением, как описано:
Классы, которые реализуют интерфейс BeanpostProcessor, являются особенными, и поэтому они по -разному обрабатываются контейнером.Все Beanpostprocessors и их непосредственно ссылаемые бобы будут создаваться на стартапе, как часть специальной фазы запуска ApplicationContext, тогда все эти Beanpostprocessors будут зарегистрированы в сортированном виде - и применяться ко всем дальнейшим бобам.Поскольку AOP Автопроксимирование реализуется в качестве самого бобовогопроцессора, ни один из бобовых процессоров или непосредственно упоминаемых бобов не имеет права на автопроксимирование (и, следовательно, не будут иметь в них аспекты.
Для любого такого боба вы должны увидеть сообщение журнала информации:«Bean 'foo» не имеет права на то, чтобы обрабатывать все Beanpostprocessors (например:не имеет права на автопроксимирование) ».
Другими словами, если я напишу свой собственный BeanPostProcessor, и этот класс напрямую ссылается на другие bean-компоненты в контексте, то эти bean-компоненты, на которые ссылаются, не будут иметь права на автоматическое проксирование, и об этом будет зарегистрировано сообщение.
Моя проблема заключается в том, что отследить, где находится эта прямая ссылка, может быть очень сложно, поскольку «прямая ссылка» на самом деле может быть цепочкой транзитивных зависимостей, которая в конечном итоге занимает половину компонентов в контексте приложения.Все, что дает вам Spring, — это одно информационное сообщение, и оно не особо помогает, если не считать сообщения о том, что компонент попал в эту паутину ссылок.
Разрабатываемый мною BeanPostProcessor имеет прямые ссылки на другие bean-компоненты, но это очень ограниченный набор ссылок.Несмотря на это, согласно сообщениям журнала, практически каждый компонент в моем контексте исключается из автоматического проксирования, но я не вижу, где происходит эта зависимость.
Кто-нибудь нашел лучший способ отследить это?
Решение 2
Просто для того, чтобы завершить этот вопрос, крах неинициализированного графа объектов был вызван BeanPostProcessor
, использующим @Autowired
для получения его зависимостей, и механизмом автопроводки фактически заставил инициализировать все остальные определения bean-компонента до того, как мой BeanPostProcessor
получил возможность высказаться по этому вопросу. Решение не в том, чтобы использовать автопроводку для ваших BPP.
Другие советы
Следуйте этому рецепту:
- Открыть
BeanPostProcessorChecker
в вашей IDE (это внутренний классAbstractApplicationContext
) - Установите точку останова на
if (logger.isInfoEnabled()) {
в методеpostProcessAfterInitialization
- Запустите свой код
Когда вы достигнете точки останова, найдите вызовы
getBean(String,Class<T>)
в трассировке вашего стека.Один из этих вызовов попытается создать
BeanPostProcessor
.Виновником должен быть этот компонент.
Фон
Представьте себе такую ситуацию:
public class FooPP implements BeanPostProcessor {
@Autowire
private Config config;
}
Когда Spring должен создать config
(поскольку это зависимость от FooPP
), есть проблема:В договоре указано, что все BeanPostProcessor
должен применяться к каждому создаваемому компоненту.Но когда весна нуждается config
, существует хотя бы один ПП (а именно FooPP
), который не готов к эксплуатации!
Ситуация ухудшается, когда вы используете @Configuration
класс для определения этого bean-компонента:
@Configuration
public class BadSpringConfig {
@Lazy @Bean public Config config() { return new Config(); }
@Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Каждый класс конфигурации является компонентом.Это означает построить фабрику по производству бобов из BadSpringConfig
, Spring необходимо применить постпроцессор fooPP
но для этого ему сначала нужна фабрика по производству бобов...
В этом примере можно разорвать одну из циклических зависимостей.Ты можешь сделать FooPP
осуществлять BeanFactoryAware
чтобы получить Spring, введите BeanFactory
в постпроцессор.Таким образом, вам не понадобится автоматическое подключение.
Далее в коде вы можете лениво запросить компонент:
private LazyInit<Config> helper = new LazyInit<Config>() {
@Override
protected InjectionHelper computeValue() {
return beanFactory.getBean( Config.class );
}
};
@Override
public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
String value = helper.get().getConfig(...);
}
Чтобы разорвать цикл между фабрикой компонентов и постпроцессором, вам необходимо настроить постпроцессор в файле конфигурации XML.Spring может прочитать это и построить все структуры, не запутавшись.
Не уверен, поможет ли это, но Spring IDE в Eclipse представление графика выглядит так, как будто оно может быть полезно при сортировке ссылок на компоненты.