As I explained to you in my comments to the other question, Spring AOP can use both CGLIB and JDK proxies depending on the situation. The default are JDK proxies for classes implementing interfaces, but you can enforce CGLIB usage for them too. For classes not implementing interfaces only CGLIB remains because JDK proxies can only create dynamic proxies based on interfaces.
So looking at your case 1, you explicitly say you want interface proxies, i.e. JDK proxies:
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
But MyBeanA
does not implement any interfaces. Consequently you get the error message you see in this case.
In case 2 however you use ApplicationContext.getBean(..)
in order to create a proxy. Here you are relying on Spring to determine which proxy type to choose, you are not trying to enforce anything. Thus, proxying via CGLIB succeeds.
No surprises here.
If you want to avoid the error message in case 1, maybe you ought to use ScopedProxyMode.TARGET_CLASS
.
Update: Sorry, I was irritated by your similar and nondescript class names MyBeanA
and MyBeanB
. It would make sense to use more descriptive, clean-code-like class names next time, ideally ones describing the roles ob the classes in your scenario like MyService
, MyInterface
, MyScopedBean
.
Anyway, I read your question and the error message again. The error message says that according to your annotation an interface-based proxy is being generated but you are trying to inject it into a class type. You can fix that by declaring it like this:
@Autowired
private MyBeanBInterface myBeanB;
In case/usage 2 you are again explicitly declaring a class and not an interface type for your bean. So as I said, Spring tries to satisfy your requirement by the only way possible, i.e. creating a CGLIB proxy for the class. You can fix this by declaring an interface type and you will get the expected JDK proxy:
MyBeanBInterface myBeanBInterface = appContext.getBean(MyBeanBInterface.class);
System.out.println(myBeanBInterface.getCounter());
System.out.println(myBeanBInterface.getClass());
Update 2: Something I think you still do not understand according to your comments is this basic fact of OOP: If you have
- class
Base
and class Sub extends Base
or
- interface
Base
and class Sub implements Base
you can declare Base b = new Sub()
but of course not Sub s = new Base()
because a Sub
is also a Base
, but not every Base
is a Sub
. For example, if you also have OtherSub extends Base
, when trying to assign a Base
object to a Sub
variable it could be an OtherSub
instance. This is why this does dot even compile without using Sub s = (Sub) myBaseObject
.
So far, so good. Now look at your code again:
In usage 1 you have @Autowired private MyBeanB myBeanB;
but configured MyBeanB
to produce a JDK proxy, i.e. a new proxy class with parent class Proxy
directly implementing MyBeanBInterface
will be created. I.e. you have two different classes, both directly implementing the same interface. Those classes are assignment-incompatible to each other for the reason I explained above. With regard to the interface we have the class hierarchy
MyBeanBInterface
MyBeanB
MyBeanB_JDKProxy
Thus you cannot inject MyBeanB_JDKProxy
into a MyBeanB
field because a proxy object is not an instance of MyBeanB
. Don't you understand? The problem sits in front of the computer, there is no mysterious Spring bug. You configured it to fail.
This is why I told you to change the code to @Autowired private MyBeanBInterface myBeanB;
because then of course it works because the proxy implements the interface and everything is fine. I also told you that alternatively you can keep @Autowired private MyBeanB myBeanB;
if you use proxyMode = ScopedProxyMode.TARGET_CLASS
for your scope declaration.
In usage 2 the problem is the same: You are saying getBean(ClassB.class)
, i.e. you are explicitly instructing Spring to create a proxy for that class. But for a class you cannot create a JDK proxy, only a CGLIB proxy, which is what Spring does. Again, I gave you the solution by instructing you to use getBean(MyBeanBInterface.class)
instead. Then you get the expected JDK proxy.
Spring is smart enough to both
- make the JDK proxy in usage 1 find the scoped service bean
MyClassB
and delegate method calls to it (note: delegation, not inheritance!) and
- make the CGLIB proxy extend
MyClassB
(note: inheritance here, no delegation necessary).