Поля одноплодных бобов весной не заселены

Мне нужен сервис (синглтон подходит) с некоторыми внутренними полями, такими как список ожидающих потоков (да, все написано, чтобы быть потокобезопасными), проблема в том, что если я @autowire этот бин, поля кажутся пустыми. Отладка Я вижу, что прокси правильно связывается с экземпляром (поля CGLIB$CALLBACK_X правильно связаны с заполненным компонентом) с заполненными полями, но предлагаемые им поля пусты.

Следующие строки кодов дают общее представление о том, о чем я говорю.

@Service
public class myService{

   @Autowired
   private Monitor monitor;

   public List getSomething(){
       return monitor.getList();
   }
}


@Service
public class myStatefulService{

   //This field will be populated for sure by someone before getSomething() is called
   private List list;

   public synchronized List getSomething(){
       return this.list;
   }

   //Called by other services that self inject this bean 
   public synchronized void addToList(Object o){
      this.list.add(o);
   }
}

Отладка переменной monitor во время вызова getList я получаю

monitor => instance of correct class
 fields:
   CGLIB$BOUND => true
   CGLIB$CALLBACK_0.advised => proxyFactory (correct)
   CGLIB$CALLBACK_1.target (reference to the correct instance of myStatefulService class)
        fields:
          list => [.........] (correctly populated)
   CGLIB$CALLBACK_2 ..... 
   ......
   ......
   ......
   list => [] (the list that would be populated is empty instead)

3 ответа

Решение

Вам любопытно или у вас есть реальная проблема? Тем не менее вот объяснение.

При использовании CGLIB для прокси-классов Spring создаст подкласс, который называется myService$EnhancerByCGLIB, Этот расширенный класс переопределит некоторые, если не все ваши бизнес-методы, чтобы применить сквозные проблемы к вашему фактическому коду.

Здесь приходит настоящий сюрприз. Этот дополнительный подкласс не вызывает super методы базового класса. Вместо этого он создает второй экземпляр myService и делегаты к нему. Это означает, что теперь у вас есть два объекта: ваш реальный объект и расширенный объект CGLIB, указывающий на него (обертывающий).

Расширенный класс - просто фиктивный прокси. Он все еще имеет те же поля, что и ваш базовый класс (унаследованный от него), но они не используются. Когда вы звоните addToList() на myService$EnhancerByCGLIB возражать, он сначала будет применять некоторую логику АОП, вызов addToList() из myService (который оборачивает) и применяет оставшуюся логику AOP по возвращении. myService$EnhancerByCGLIB.list поле никогда не трогается.

Почему Spring не может использовать тот же класс и делегировать через super? Я предполагаю для простоты: сначала создайте "необработанный" компонент, а затем примените прокси AOP во время постобработки.

"Это поле будет обязательно заполнено кем-то до вызова getSomething()"

Кем-то? Нет, фабрика бобов весны. Если вы не настроите его, ничего не будет заполнено.

Не каждый боб должен быть под контролем Spring. Похоже, вы хотите иметь List что клиенты могут добавлять и удалять элементы потокобезопасным способом. Если это правда, удалите @Autowired аннотация, создать новый Listи выставить методы для добавления и удаления.

Я бы порекомендовал Список из новых параллельных коллекций.

CGLIB будет защищен прокси получателями.

Таким образом, вы можете иметь:

@Autowired
private Monitor monitor;

protected Monitor getMonitor() { return monitor; }

public List getSomething(){
    return getMonitor().getList();
}

getMonitor() будет проксирован для вызова getMonitor() в другом экземпляре, в который введен монитор.

Другие вопросы по тегам