Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Autowire strange problem

Tags:

I have a strange behaviour when autowiring

I have a similar code like this one, and it works

@Controller public class Class1 {     @Autowired     private Class2 object2;     ... }  @Service @Transactional public class Class2{    ... } 

The problem is that I need that the Class2 implements an interface so I've only changed the Class2 so it's now like:

@Controller public class Class1 {     @Autowired     private Class2 object2;     ... }  @Service @Transactional public class Class2 implements IServiceReference<Class3, Long>{    ... }  public interface IServiceReference<T, PK extends Serializable> {     public T reference(PK id); } 

with this code I get a org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type for Class2. It seems that @Transitional annotation is not compatible with the interface because if I remove the @Transitional annotation or the implements IServiceReference<Class3, Long> the problem disapears and the bean is injected (though I need to have both in this class). It also happens if I put the annotation @Transitional in the methods instead of in the Class.

I use Spring 3.0.2 if this helps.

Is not compatible the interface with the transactional method? May it be a Spring bug?

like image 518
Javi Avatar asked Apr 26 '10 11:04

Javi


People also ask

Why is @autowired not working?

When @Autowired doesn't work. There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.

Why my Autowired Bean is null?

The field annotated @Autowired is null because Spring doesn't know about the copy of MileageFeeCalculator that you created with new and didn't know to autowire it.

What is the purpose of @autowire and @qualifier annotation?

The @Qualifier annotation is used to resolve the autowiring conflict, when there are multiple beans of same type. The @Qualifier annotation can be used on any class annotated with @Component or on methods annotated with @Bean . This annotation can also be applied on constructor arguments or method parameters.


2 Answers

The problem is that your Class1 needs a reference to IServiceReference and not the concrete reference of Class2

@Controller public class Class1 { @Autowired private IServiceReference object2;     ... } 

The reason this is that Spring is creating a dynamic proxy for classes that you marked @Transactional. Thus when Class2 is created its wrapped in a Proxy object that is obviously not of type Class2 but is of type IServiceReference.

If you want the behavior of using Class2 with proxy support you will have to turn on CGLIB Read below:

From Springs Doc:

Spring AOP defaults to using standard J2SE dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.

Spring AOP can also use CGLIB proxies. This is necessary to proxy classes, rather than interfaces. CGLIB is used by default if a business object does not implement an interface. As it is good practice to program to interfaces rather than classes, business classes normally will implement one or more business interfaces. It is possible to force the use of CGLIB, in those (hopefully rare) cases where you need to advise a method that is not declared on an interface, or where you need to pass a proxied object to a method as a concrete type.

It is important to grasp the fact that Spring AOP is proxy-based. See the section entitled Section 6.6.1, “Understanding AOP proxies” for a thorough examination of exactly what this implementation detail actually means.

like image 167
Adam Gent Avatar answered Nov 14 '22 04:11

Adam Gent


The Transactional annotation instructs Spring to generate proxy objects around the annotated beans, to implement the transactional semantics. The generated proxy will implement the same interfaces as the target bean. So if your target bean implements IServiceReference, then so will the generated proxy.

If the target bean has no implemented interfaces, then the generated proxy will instead be a subclass of the target bean type.

In your original example, the transactional proxy will be a subclass of Class2, because Class2 implemented no interfaces. When you changed Class2 to implement IServiceReference, the generated proxy no longer extended Class2, and instead implemented IServiceReference. This caused your ClassCastException.

The best approach to this situation is to remove the reference from Class1 to Class2, and instead talk to Class2 purely through its interfaces. Class2 can implement as many interfaces as you like, the proxy will implement all of them.

You can force Spring to generate subclass proxies regardless of the interfaces, but it's additional complexity, and I'd recommend against it.

like image 27
skaffman Avatar answered Nov 14 '22 04:11

skaffman