Отслеживание причины Spring "не подходит для автоматического проксирования"
Когда вы начинаете возиться с файлом Spring auto-proxy, вы часто сталкиваетесь с этим поведением, как описано:
Классы, реализующие Интерфейс BeanPostProcessor - это специальные, и поэтому они обрабатываются иначе контейнером. Все BeanPostProcessors и их непосредственно ссылка на beans будет создана при запуске, как часть специального фаза запуска ApplicationContext, то все эти BeanPostProcessors будут зарегистрированы в порядке сортировки - и применяется к все дальше beans. Поскольку АОП автоматическое проксирование реализовано как BeanPostProcessor сам, нет BeanPostProcessors или напрямую ссылки beans имеют право на автоматическое проксирование (и, следовательно, не будет аспекты "вплетены" в них.
Для любого такого bean вы должны увидеть info log message: "Bean 'foo' не является право на получение всех BeanPostProcessors (например: not для автопроксирования)".
Другими словами, если я пишу свой собственный BeanPostProcessor, и этот класс напрямую ссылается на другой beans в контексте, тогда ссылки, на которые ссылаются beans, не будут иметь права на автопроксирование, и сообщение будет зарегистрировано на этот счет.
Моя проблема заключается в том, что отслеживание там, где эта прямая ссылка может быть очень сложной, поскольку "прямая ссылка" может фактически быть цепочкой транзитивных зависимостей, которая заканчивается тем, что занимает половину beans в контексте приложения. Все Spring дает вам это одноинформационное сообщение, и это не очень помогает, если не сказать вам, когда bean попал в эту сеть ссылок.
Созданный BeanPostProcessor имеет прямые ссылки на другие beans, но это очень ограниченный набор ссылок. Несмотря на это, почти каждый bean в моем контексте затем исключается из автоматического проксирования в соответствии с сообщениями журнала, но я не вижу, где эта зависимость происходит.
Кто-нибудь нашел лучший способ отслеживания этого?
Ответы
Ответ 1
Чтобы просто закрыть этот вопрос, крах неинициализированного графа объектов был вызван BeanPostProcessor
с помощью @Autowired
для получения его зависимостей, а механизм autowire эффективно вызвал инициализацию каждого другого определения bean до того, как мой BeanPostProcessor
получил шанс высказаться по этому вопросу. Решение - не используйте autowiring для ваших BPP...
Ответ 2
Следуйте этому рецепту:
- Откройте
BeanPostProcessorChecker
в вашей среде IDE (это внутренний класс AbstractApplicationContext
)
- Установите точку останова на
if (logger.isInfoEnabled()) {
в методе postProcessAfterInitialization
- Запустите свой код
-
Когда вы нажмете точку останова, найдите вызовы getBean(String,Class<T>)
в трассировке стека.
Один из этих вызовов попытается создать BeanPostProcessor
. Этот bean должен быть виновником.
Фон
Представьте себе эту ситуацию:
public class FooPP implements BeanPostProcessor {
@Autowire
private Config config;
}
Когда Spring должен создать config
(поскольку он зависит от FooPP
), у него есть проблема: контракт говорит, что все BeanPostProcessor
должны применяться к каждому создаваемому bean. Но когда Spring требуется config
, существует хотя бы один PP (а именно FooPP
), который не готов к обслуживанию!
Это становится хуже, если вы используете класс @Configuration
для определения этого bean:
@Configuration
public class BadSpringConfig {
@Lazy @Bean public Config config() { return new Config(); }
@Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Каждый класс конфигурации является bean. Это означает, что для создания bean factory из BadSpringConfig
, Spring должен применяться постпроцессор FooPP
, но для этого ему сначала требуется bean factory...
В этом примере можно разбить одну из циклических зависимостей. Вы можете сделать FooPP
реализовать BeanFactoryAware
, чтобы получить Spring вставить BeanFactory
в почтовый процессор. Таким образом, вам не требуется автоуправление.
Позже в коде вы можете лениво попросить bean:
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(...);
}
(источник для LazyInit)
Чтобы разбить цикл между bean factory и пост-процессором, вам нужно настроить почтовый процессор в файле конфигурации XML. Spring может прочитать это и построить все структуры, не запутавшись.
Ответ 3
Не уверен, что это поможет, но Eclipse Spring IDE
вид графика выглядит так, как будто это может быть полезно при сортировке bean ссылок..