В настоящее время я переношу одну из моих проектов на «самонастраиваемую пружину» на весеннюю загрузку. в то время как большая часть материала уже работает, у меня есть проблема с методом @Transactional
, где, когда он называется контекстом, отсутствует, как установлено ранее, из-за вызова экземпляра «target» вместо экземпляра «proxy» (я буду попытайтесь подробно остановиться ниже).Метод с @Transactional вызван на цель не на экземпляр прокси
Сначала урезанная вид моей иерархии классов:
@Entity public class Config { // fields and stuff } public interface Exporter { int startExport() throws ExporterException; void setConfig(Config config); } public abstract class ExporterImpl implements Exporter { protected Config config; @Override public final void setConfig(Config config) { this.config = config; // this.config is a valid config instance here } @Override @Transactional(readOnly = true) public int startExport() throws ExporterException { // this.config is NULL here } // other methods including abstract one for subclass } @Scope("prototype") @Service("cars2Exporter") public class Cars2ExporterImpl extends ExporterImpl { // override abstract methods and some other // not touching startExport() } // there are other implementations of ExporterImpl too // in all implementations the problem occurs
код вызова выглядит так:
@Inject private Provider<Exporter> cars2Exporter; public void scheduleExport(Config config) { Exporter exporter = cars2Exporter.get(); exporter.setConfig(config); exporter.startExport(); // actually I'm wrapping it here in a class implementing runnable // and put it in the queue of a `TaskExecutor` but the issue happens // on direct call too. :( }
Что именно проблема?
При звонке в startExport()
поле config
из ExporterImpl
имеет значение null, хотя оно было установлено ранее.
Что я нашел: С точкой останова на exporter.startExport();
Я проверил идентификатор экземпляра экспортера, показанный отладчиком eclipse. В раунде debbug при написании этого сообщения это 16585
. Продолжение выполнения в строке вызова/первой строки startExport()
, где я снова проверил идентификатор(), ожидая, что он будет таким же, но осознает, что это не так. Здесь 16606
... так что вызов startExport()
выполняется в другом экземпляре класса ... в предыдущем раунде отладки, который я проверил на экземпляр/id, вызов setConfig()
идет ... к первому на (16585 в этом случае). Это объясняет, почему поле конфигурации имеет значение null в экземпляре 16606.
Чтобы понять, что происходит между строкой, где я звоню exporter.startExport();
, и первой линией возбуждения startExport()
, я нажал на шаги между этими двумя строками в отладчике eclipse.
Там я пришел к line 655 in CglibAopProxy который выглядит следующим образом:
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
проверки аргументов здесь я обнаружил, что proxy
является экземпляром с идентификатором 16585 и target
той с 16606.
к сожалению, я не так глубоко в springs, чтобы знать, как это должно быть ...
Мне просто интересно, почему есть два экземпляра, которые вызываются в разных методах. вызов setConfig()
отправляется на экземпляр прокси, а вызов do startExport()
достигает целевого экземпляра и, следовательно, не имеет доступа к ранее установленной конфигурации ...
Как уже упоминалось, проект был перенесен на весеннюю загрузку, но мы, где раньше уже используя версию весенней платформы весом Athens-RELEASE
. Из того, что я могу сказать там, где нет специальных конфигураций АОП до миграции, и явным образом не заданы значения после миграции.
Чтобы получить эту проблему фиксированной (или, по крайней мере, как-то работает) я уже пробовал несколько вещей:
- удалить @Scope из вспомогательного класса
- шаг @Transactional от уровня метода для класса
- переопределения startExport() в подклассе и положить @Transactional здесь
- добавить @EnableAspectJAutoProxy к классу приложений (я не смог даже не войти в систему - не сообщение об ошибке)
- набор spring.aop.proxy-деготь получить класс к истинным
- выше в diffrent комбинаций ...
В настоящее время я из подсказок о том, как получить это обратно рабочие ...
Заранее спасибо
* надеется, что кто-то может помочь *
Что произойдет, если вы не вызываете это поле напрямую, а 'getConfig()'? –
'getConfig()' вызывается в том же экземпляре, что и 'startExport()', и возвращает null. – Dodge
Удалите ключевое слово 'final' на' setConfig'. Прокси создается, но это прокси-сервер на основе класса. Для вызова прокси-сервера необходимо вызвать 'setConfig' для прокси-сервера и' startExport'. Или переключите 'spring.aop.proxy-target-class' на' false', чтобы иметь прокси-сервер на основе интерфейса. –