Перейти к основному содержимому

Устранение предупреждения Spring «не подходит для автоматического проксирования»

· 5 мин. чтения

1. Обзор

В этом кратком руководстве мы увидим, как отследить причину сообщения Spring « не подходит для автоматического проксирования » и как это исправить. ``

Во-первых, мы создадим простой пример кода из реальной жизни, который вызывает появление сообщения во время запуска приложения. Затем мы объясним причину, по которой это происходит.

Наконец, мы представим решение проблемы, показав пример рабочего кода.

2. Причина сообщения «не подходит для автоматического проксирования»

2.1. Пример конфигурации

Прежде чем мы объясним причину сообщения, давайте создадим пример, в котором сообщение появляется во время запуска приложения.

Во-первых, мы создадим пользовательскую аннотацию RandomInt . Мы будем использовать его для аннотирования полей, в которые должно быть вставлено случайное целое число из указанного диапазона:

@Retention(RetentionPolicy.RUNTIME)
public @interface RandomInt {
int min();

int max();
}

Во-вторых, давайте создадим класс DataCache , который является простым компонентом Spring. Мы хотим назначить для кэширования случайную группу, которая может использоваться, например, для поддержки сегментирования. Для этого мы аннотируем это поле нашей пользовательской аннотацией:

@Component
public class DataCache {
@RandomInt(min = 2, max = 10)
private int group;
private String name;
}

Теперь давайте посмотрим на класс RandomIntGenerator . Это компонент Spring, который мы будем использовать для вставки случайных значений int в поля, аннотированные аннотацией RandomInt :

@Component
public class RandomIntGenerator {
private Random random = new Random();
private DataCache dataCache;

public RandomIntGenerator(DataCache dataCache) {
this.dataCache = dataCache;
}

public int generate(int min, int max) {
return random.nextInt(max - min) + min;
}
}

Важно отметить, что мы автоматически подключаем класс DataCache к RandomIntGenerator посредством внедрения конструктора .

Наконец, давайте создадим класс RandomIntProcessor , который будет отвечать за поиск полей, аннотированных аннотацией RandomInt, и вставку в них случайных значений:

public class RandomIntProcessor implements BeanPostProcessor {
private final RandomIntGenerator randomIntGenerator;

public RandomIntProcessor(RandomIntGenerator randomIntGenerator) {
this.randomIntGenerator = randomIntGenerator;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
RandomInt injectRandomInt = field.getAnnotation(RandomInt.class);
if (injectRandomInt != null) {
int min = injectRandomInt.min();
int max = injectRandomInt.max();
int randomValue = randomIntGenerator.generate(min, max);
field.setAccessible(true);
ReflectionUtils.setField(field, bean, randomValue);
}
}
return bean;
}
}

Он использует реализацию интерфейса org.springframework.beans.factory.config.BeanPostProcessor для доступа к аннотированным полям прямо перед инициализацией класса.

2.2. Тестирование нашего примера

Несмотря на то, что все компилируется правильно, когда мы запускаем наше приложение Spring и смотрим его журналы, мы увидим сообщение « не подходит для автоматического проксирования », сгенерированное классом Spring BeanPostProcessorChecker :

INFO org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'randomIntGenerator' of type [com.foreach.autoproxying.RandomIntGenerator] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Более того, мы видим, что наш bean-компонент DataCache , который зависит от этого механизма, не был инициализирован, как мы предполагали:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, RandomIntGenerator.class})
public class NotEligibleForAutoProxyingIntegrationTest {

private RandomIntProcessor randomIntProcessor;

@Autowired
private DataCache dataCache;

@Test
public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenNotEligibleLogShouldShow() {
assertEquals(0, dataCache.getGroup());
}
}

Однако стоит отметить, что даже при появлении сообщения приложение не падает.

2.3. Анализ причины

Предупреждение вызвано классом RandomIntProcessor и его автосвязанными зависимостями. Классы, реализующие интерфейс BeanPostProcessor , создаются при запуске, как часть специальной фазы запуска ApplicationContext, перед любыми другими компонентами.

Более того, механизм автопроксирования АОП также является реализацией интерфейса BeanPostProcessor . В результате ни реализации BeanPostProcessor , ни bean-компоненты, на которые они напрямую ссылаются, не подходят для автоматического проксирования. Это означает, что функции Spring, использующие АОП, такие как автосвязывание, безопасность или транзакционные аннотации, не будут работать должным образом в этих классах.

В нашем случае мы смогли без проблем автоматически подключить экземпляр DataCache к классу RandomIntGenerator . Однако поле группы не было заполнено случайным целым числом.

3. Как исправить ошибку

Чтобы избавиться от сообщения « не подходит для автоматического проксирования» , нам нужно разорвать цикл между реализацией BeanPostProcessor и его bean-зависимостями . В нашем случае нам нужно указать контейнеру IoC лениво инициализировать bean- компонент RandomIntGenerator . Мы можем использовать ленивую аннотацию Spring:

public class RandomIntProcessor implements BeanPostProcessor {
private final RandomIntGenerator randomIntGenerator;

@Lazy
public RandomIntProcessor(RandomIntGenerator randomIntGenerator) {
this.randomIntGenerator = randomIntGenerator;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//...
}
}

Spring инициализирует bean-компонент RandomIntGenerator , когда RandomIntProcessor запрашивает его в методе postProcessBeforeInitialization . В этот момент контейнер IoC Spring создает экземпляры всех существующих bean-компонентов, которые также подходят для автоматического проксирования.

На самом деле, если мы запустим наше приложение, мы не увидим в логах сообщения « не подходит для автоматического проксирования» . Более того, компонент DataCache будет иметь поле группы, заполненное случайным целым числом:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, RandomIntGenerator.class})
public class NotEligibleForAutoProxyingIntegrationTest {

private RandomIntProcessor randomIntProcessor;

@Autowired
private DataCache dataCache;

@Test
public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenGroupFieldShouldBePopulated() {
assertNotEquals(0, dataCache.getGroup());
}
}

4. Вывод

В этой статье мы узнали, как отследить и исправить причину сообщения Spring «не подходит для автоматического проксирования» . Ленивая инициализация прерывает цикл зависимостей во время создания bean-компонента.

Как всегда, код примера доступен на GitHub .