NoSuchBeanDefinitionException when implementing a new interface
I had the following set-up which worked perfectly fine:
@Service
public class MyService {
}
public class Test {
@Autowired
MyService service;
}
I changed MyService to implement a new interface, like this
@Service
public class MyService implements NewInterface {
}
There's nothing special in this interface, it's just a normal Java interface without any annotation and 2 very simple methods.
Now, Spring is not able to autowire this bean anymore, it gives a NoSuchBeanDefinitionException
. I also tried to define it explicitly in the XML but it gave the same exception.
In case it's relevant, I开发者_C百科'm using Spring 2.5 and build with Maven, the class Test is a unit test class. When I try to run the real application, it's using applicationContext.getBean()
to get this service, and it gives the following exception: java.lang.ClassCastException: $Proxy81 cannot be cast to MyService
.
What am I missing and what should I do?
When you see a class with a name like $Proxy81
, it's telling you that Spring has auto-generated a proxy object for one of your beans, in this case a proxy object for the MyService
bean. This uses java.lang.reflect.Proxy
to generate the proxy object. This proxy object will implement the same interfaces as the class that's being proxied, but it will not be type-compatible with the target class itself.
Now, if the target class doesn't implement any interfaces, then Spring will instead use CGLIB to generate the proxy. This proxy will be a subclass of the target class, so the proxy object can be safely cast to the original type of the target object.
Now, when using the lower-level proxy-generation stuff in Spring, you can often override this behaviour, and tell it to always use CGLIB proxies, but I'm assuming that since you're using @Service
, then you're also using <context:component-scan>
, in which case I think you have to stick with the default behaviour.
It's not bad thing, though. This encourages you to not couple your classes together, but instead to program to interfaces. Any interaction with MyService
should be expressible via interfaces, although this concept can get a little fuzzy when talking about unit testing.
It looks like you're autowiring by interface instead of autowiring by class name.
I'd simply code my test against the Interface:
public class Test {
@Autowired
NewInterface service;
}
Also, check this bug, it might be relevant to you since it appears like your class is being proxied.
精彩评论