Поля одноплодных бобов весной не заселены
Мне нужен сервис (синглтон подходит) с некоторыми внутренними полями, такими как список ожидающих потоков (да, все написано, чтобы быть потокобезопасными), проблема в том, что если я @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() в другом экземпляре, в который введен монитор.