Как я объяснил вам в моих комментариях к другому вопросу , Spring AOP может использовать оба прокси-сервера CGLIB и JDK в зависимости от ситуации. По умолчанию используются прокси-серверы JDK для классов, реализующих интерфейсы, но вы также можете использовать CGLIB для них. Для классов, не реализующих интерфейсы, остается только CGLIB, поскольку прокси-серверы JDK могут создавать только динамические прокси-серверы на основе интерфейсов.
Итак, рассматривая ваш случай 1, вы явно говорите, что хотите использовать прокси-интерфейсы интерфейса, то есть прокси-серверы JDK:
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
Но MyBeanA
не реализует никаких интерфейсов. Следовательно, вы получаете сообщение об ошибке, которое вы видите в этом случае.
В случае 2, однако, вы используете ApplicationContext.getBean(..)
для создания прокси. Здесь вы полагаетесь на Spring, чтобы определить, какой тип прокси выбрать, вы не пытаетесь принудительно применить что-либо. Таким образом, проксирование через CGLIB завершается успешно.
Здесь никаких сюрпризов.
Если вы хотите избежать сообщения об ошибке в случае 1, возможно, вам следует использовать ScopedProxyMode.TARGET_CLASS
.
Обновление: Извините, меня раздражали ваши похожие и неописуемые имена классов MyBeanA
и MyBeanB
. В следующий раз имело бы смысл использовать более описательные имена, подобные чистому коду, в идеале те, которые описывают роли классов в вашем сценарии, например MyService
, MyInterface
, MyScopedBean
.
В любом случаеЯ прочитал ваш вопрос и сообщение об ошибке снова. В сообщении об ошибке говорится, что согласно вашей аннотации создается прокси на основе интерфейса, но вы пытаетесь внедрить его в тип класса. Вы можете исправить это, объявив его так:
@Autowired
private MyBeanBInterface myBeanB;
В случае / использовании 2 вы снова явно объявляете класс, а не тип интерфейса для вашего компонента. Как я уже сказал, Spring пытается удовлетворить ваши требования единственным возможным способом, то есть путем создания прокси CGLIB для класса. Вы можете исправить это, объявив тип интерфейса, и вы получите ожидаемый JDK-прокси:
MyBeanBInterface myBeanBInterface = appContext.getBean(MyBeanBInterface.class);
System.out.println(myBeanBInterface.getCounter());
System.out.println(myBeanBInterface.getClass());
Обновление 2: Что-то, что, я думаю, вы все еще не понимаете в соответствии с вашимкомментарии это базовый факт ООП: если у вас есть
- класс
Base
и класс Sub extends Base
или - интерфейс
Base
и класс Sub implements Base
Вы можете объявить Base b = new Sub()
, но, конечно, не Sub s = new Base()
, потому что Sub
также является Base
, но не каждый Base
является Sub
. Например, если у вас также есть OtherSub extends Base
, при попытке назначить объект Base
переменной Sub
это может быть экземпляр OtherSub
. Вот почему это даже компилируется без использования Sub s = (Sub) myBaseObject
.
Пока все хорошо. Теперь посмотрите на ваш код еще раз:
При использовании 1 у вас есть @Autowired private MyBeanB myBeanB;
, но вы настроили MyBeanB
для создания прокси JDK, то есть нового прокси-класса с родительским классом Proxy
напрямуюреализация MyBeanBInterface
будет создана. Т.е. у вас есть два разных класса, оба напрямую реализующие один и тот же интерфейс. Эти классы несовместимы друг с другом по причине, которую я объяснил выше. Что касается интерфейса, у нас есть иерархия классов
MyBeanBInterface
MyBeanB
MyBeanB_JDKProxy
Таким образом, вы не можете внедрить MyBeanB_JDKProxy
в поле MyBeanB
, поскольку прокси-объект не является экземпляром MyBeanB
. Ты не понимаешь? Проблема сидит перед компьютером, там нет таинственной ошибки Spring. Вы настроили его на сбой.
Вот почему я сказал вам изменить код на @Autowired private MyBeanBInterface myBeanB;
, потому что тогда, конечно, он работает, потому что прокси реализует интерфейс, и все в порядке. Я также сказал вам, что в качестве альтернативы вы можете оставить @Autowired private MyBeanB myBeanB;
, если вы используете proxyMode = ScopedProxyMode.TARGET_CLASS
для декларации области действия.
При использовании 2 проблема та же: вы говорите getBean(ClassB.class)
то есть вы явно указываете Spring создать прокси для этого класса. Но для класса нельзя создать JDK-прокси, только CGLIB-прокси, что делает Spring. Опять же, я дал вам решение, указав вместо этого использовать getBean(MyBeanBInterface.class)
. Тогда вы получите ожидаемый JDK-прокси.
Spring достаточно умен для обоих
- заставляет прокси-сервер JDK при использовании 1 находить служебный бин области действия
MyClassB
и вызывает для него вызовы методов делегата (примечание: делегирование, а не наследование!) И - расширяет прокси-сервер CGLIB
MyClassB
(примечание: наследование здесь, делегирование не требуется).