由于代理而导致Spring Bean注入失败

Spring版本 :3.2.4.RELEASE和3.2.9.RELEASE

Mockito版本 :1.8.5

我一直在尝试将H2测试引入到旧项目进行集成测试,我遇到了一些问题。 由于事务的传播方式,我需要模拟一个自动连接的类。 我以前做过这个,但我现在遇到了严重的问题。 初始化测试时抛出以下错误消息:

org.springframework.beans.factory.BeanCreationException:创建名为’com.stuff.XMLITCase’的bean时出错:资源dependency injection失败; 嵌套exception是org.springframework.beans.factory.BeanNotOfRequiredTypeException: 名为’TheProcessor’的Bean必须是[com.stuff.XMLBatchFileProcessor]类型,但实际上是 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor中的[$ Proxy118]类型 。 postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:307)

深入研究这一点,事实certificatebean实际上是一个代理。 如果我们检查AbstractBeanFactory(第239行),我们可以看到代理:

sharedInstance = {$ Proxy117 @ 7035}“com.stuff.XMLBatchFileProcessor@66c540d0”h = {org.springframework.aop.framework.JdkDynamicAopProxy@7039}

唯一的问题是,我不知道这是从哪里来的。 我已经查看了配置和依赖项,无法找到应该发生的任何地方。

项目设置

不幸的是,我无法为此提供示例项目,但我会检查我的测试配置。 我有一个我为测试扩展的根类:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:/spring/spring-test-context.xml"}) @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) public abstract class AbstractIntegrationTest { } 

这只是在一些spring配置中加载并在每次测试后回滚事务。

spring配置也没什么奇怪的,虽然我的另一个模块和这个模块之间有一个区别。 这是事务管理器和会话工厂:

     ...  

在我的其他模块中,我使用的是entityManagerFactory,以及不同的事务管理器:

     ...  

实际的类有一些自动assembly的字段,以及通常的@Service注释:

 @Service(value = "TheProcessor") public final class XMLBatchFileProcessor extends BatchFileProcessor implements IXMLBatchProcessor { 

最后,实际测试如下:

 public class XMLITCase extends AbstractIntegrationTest { @Resource(name = "TheProcessor") @InjectMocks private XMLBatchFileProcessor xmlProcessor; @Mock private ProcessHelper processHelper; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } @Test public void test() throws Exception { Assert.assertNotNull(xmlProcessor); } } 

如果我用接口替换XMLBatchFileProcessor并自动assembly该字段,那么编译没有任何问题。 但是,Mockito永远不会将自动assembly的bean替换为模拟对象。 如果确实如此,那么我就不会打扰@Resource注释并命名服务,从而避免代理问题。

对此有任何帮助将是值得赞赏的。 我将专注于会话工厂及其中的差异,但很可能我完全错过了其他东西。

编辑

继续Sotirios的评论,我今天早上又看了一眼,确实错过了xmlProcessor有一个@Transactional注释,因此意味着该类需要被代理。 如果我删除final声明并让CGLib增强它,那么当initMocks(this)调用时,Mockito会替换bean。 但是,当调用一个方法时,CGLib似乎用Spring增强版替换所有bean,因此覆盖了Mockito版本。

对于具有@Transactional注释的类的集成测试,使用Mockito和Spring的正确方法是什么?

好吧,一旦我意识到由于@Transactional注释而导致该类被代理,问题的解决方案变得更加清晰。 我需要做的是解包代理,并直接在其上设置模拟对象:

所以在我的AbstractIntegrationTest

 /** * Checks if the given object is a proxy, and unwraps it if it is. * * @param bean The object to check * @return The unwrapped object that was proxied, else the object * @throws Exception */ public final Object unwrapProxy(Object bean) throws Exception { if (AopUtils.isAopProxy(bean) && bean instanceof Advised) { Advised advised = (Advised) bean; bean = advised.getTargetSource().getTarget(); } return bean; } 

然后在我的@Before

 @Mock private ProcessHelper processHelper; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); IXMLBatchProcessor iXMLBatchProcessor = (IXMLBatchProcessor) unwrapProxy(xmlProcessor); ReflectionTestUtils.setField(iXMLBatchProcessor , "processHelper", processHelper); } 

这使得所有@Autowired类保持原样,同时注入正确的模拟对象。