@Autowire奇怪的问题

时间:2010-04-26 11:31:18

标签: java spring spring-mvc autowired

自动装配时我有一种奇怪的行为

我有类似这样的代码,它可以正常工作

@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注释或i mplements IServiceReference<Class3, Long>问题消失并注入bean(尽管我需要同时注入)这个班)。如果我将注释@Transitional放在方法而不是类中,也会发生这种情况。

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

与事务方法的接口不兼容吗? 可能是春天的错误吗?

3 个答案:

答案 0 :(得分:28)

问题是你的Class1需要引用IServiceReference而不是Class2的具体引用

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

这是因为Spring正在为您标记为@Transactional的类创建动态代理。因此,当创建Class2时,它包装在一个Proxy对象中,该对象显然不是Class2类型,但是类型为IServiceReference。

如果您希望使用具有代理支持的Class2的行为,则必须打开CGLIB 阅读以下内容:

来自Springs Doc:

  

Spring AOP默认使用标准   用于AOP代理的J2SE动态代理。   这可以启用任何接口(或一组   要代理的接口。

     

Spring AOP也可以使用CGLIB代理。   这是代理类所必需的,   而不是接口。使用CGLIB   默认情况下,如果业务对象   没有实现一个接口。原样   编程到接口的良好实践   而不是课程,商务课程   通常会实施一个或多个   业务接口。有可能   强制使用CGLIB   (希望很少见)您需要的案例   建议不是的方法   在界面或您所在的地方声明   需要将代理对象传递给   方法作为具体类型。

     

掌握这一事实很重要   Spring AOP是基于代理的。见   题为第6.6.1节的部分,   一个“理解AOP代理”   彻底检查究竟是什么   实际上这个实现细节   装置

答案 1 :(得分:13)

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

如果目标bean没有实现接口,那么生成的代理将是目标bean类型的子类

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

解决此问题的最佳方法是将引用从Class1移除到Class2,而不是纯粹通过其接口与Class2对话。 Class2可以实现任意数量的接口,代理将实现所有这些接口。

可以强制Spring生成子类代理,无论接口如何,但它的复杂性更高,我建议不要使用它。

答案 2 :(得分:1)

您可以通过添加

强制它代理
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)

另见this documentation