2010-04-26 47 views
23

我有一个奇怪的行为自动装配时@Autowire奇怪的问题

我有一个类似这样的代码之一,它的工作原理

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

@Service 
@Transactional 
public class Class2{ 
    ... 
} 

的问题是,我需要的是Class2中实现了一个接口,这样我只改变了Class2中,所以它现在想:

@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); 
} 

与此代码我得到一个org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type for Class2。 看来@Transitional注释与界面不兼容,因为如果我删除@Transitional注释或者问题消失并注入bean(虽然我需要在这个类中都有)。如果我将注释@Transitional放在方法中而不是在类中,也会发生这种情况。

如果有帮助,我使用Spring 3.0.2。

与事务性方法的接口不兼容吗? 可能是Spring bug吗?

回答

28

的问题是,你的Class1的需要IServiceReference参考,而不是等级2

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

的原因,这是春天正在为您所标记@Transactional类动态代理的具体参考。因此,当创建Class2时,它包装在明显不是Class2类型但是IServiceReference类型的Proxy对象中。

如果你想使用的Class2与代理支持的行为,你将不得不打开CGLIB 阅读以下内容:

泉水文件:

Spring缺省使用标准 J2SE动态代理对于AOP代理。 这可以代理任何接口(或一组接口)。

Spring AOP也可以使用CGLIB代理。 这对于代理类, 而不是接口是必需的。默认情况下,如果业务对象 未实现接口,则使用CGLIB 。由于 是编程接口 而不是类的良好惯例,因此业务类 通常会实现一个或多个业务接口。它可以 强制使用CGLIB的,在你需要 告知未 在接口中声明的方法,或者那些 (希望罕见)情况下,您 需要一个代理对象传递给 方法作为具体类型。

重要的是要了解Spring AOP是基于代理的事实 。请参阅 标题为“了解AOP代理”的章节6.6.1, ,其中详细说明了 这个实现细节实际上是 的意思。

13

Transactional注解指示Spring在带注释的bean周围生成代理对象,以实现事务性语义。生成的代理将实现与目标bean相同的接口。所以如果你的目标bean实现了IServiceReference,那么生成的代理也是如此。

如果目标bean没有实现的接口,则生成的代理将改为目标bean类型的子类

在您的原始示例中,事务代理将是Class2的子类,因为Class2未实现任何接口。当您更改Class2以实施IServiceReference时,生成的代理不再延长Class2,而是实施IServiceReference。这导致您的ClassCastException

这种情况的最佳方法是将参考从Class1删除到Class2,而是纯粹通过其接口与Class2进行交谈。 Class2可以实现尽可能多的接口,代理将实现所有这些接口。

可以强制Spring生成子类代理而不管接口如何,但这是额外的复杂性,我建议不要这样做。

+5

基本上我说的同样的东西:) – 2010-04-26 12:49:15

1

您可以强制其代理加入

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 

也看到this documentation

+0

这正是必要的,当实现的接口实际上不涉及组件的接线。可能发生的情况是,您希望将某个组件作为参数传递到某个位置,但输入为接口。与Spring无关。如果有多个接口,没问题。如果只有一个,春天的事情对他来说就是他的工作 - 而这个@范围完美地固定了他的固定。 :-) – virgo47 2015-09-11 11:51:05