在Jenkins中出现Spring错误“Bean named 'x' must be of type [y], but was actually of type [$Proxy]”。

21

我已经调试了一段时间了,希望有人能在这里解决问题。

我有一个Maven项目添加到Jenkins中,使用JDK 1.6。我在这个项目中使用AOP来处理数据库事务。

当我在Jenkins中运行构建时,我的测试用例会失败,并显示以下异常:

Caused by: org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'dataHandlerClassificationImpl': 
Injection of resource dependencies failed; nested exception is 
org.springframework.beans.factory.BeanNotOfRequiredTypeException: 
Bean named 'writerDataLocationImpl' must be of type [xxx.script.WriterData], 
but was actually of type [$Proxy17]
    ...
    ...
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: 
Bean named 'writerDataLocationImpl' must be of type [xxx.script.WriterData], 
but was actually of type [$Proxy17]
    ...
    ...

DataHandlerClassificationImpl类的代码大致如下:

@Service
public class DataHandlerClassificationImpl extends DataHandler {

    @Resource(name="writerDataLocationImpl")
    private WriterData writerData;

    ...
}       

WriterData是一个具有多个实现的接口。

我可以从IDE中执行代码而没有问题。为了确定它是Maven问题还是Jenkins问题,我使用命令行导航到Jenkins项目作业文件夹,并能够运行mvn test而没有任何错误。

我知道代理错误与AOP有关,并且只能将自动装配应用于接口而不是具体类...但这里并非如此,因为我能够在Jenkins之外正常运行我的代码。

有什么想法吗?谢谢。


1
在Jenkins的“目标和选项”下,我设置了clean cobertura:cobertura site。只是出于好奇心,我将其改为clean test,而它也能正常工作。但当我把它改为clean site时,我又遇到了同样的异常。看起来这与site有关。有任何想法吗?谢谢。 - limc
@Thomasz,我应该如何转储writerData.getClass().getInterfaces?Intellij基本上给了我一个常规的编译错误。 - limc
还有一件事 - 你是否已经为“site”(报告)启用了Cobertura?我非常确定这是Cobertura的问题,而不是“site”的问题。 - Tomasz Nurkiewicz
<aop:config proxy-target-class="true"> 确实解决了问题!非常感谢。如果您可以在下面发布您的最后一条评论作为答案,我会点赞并将其标记为已完成。再次感谢。 - limc
还可以看一下我的回答http://stackoverflow.com/a/8216926/1836,那是一个类似的问题。 - Matthew Farwell
显示剩余5条评论
3个回答

47

以上是来自问题评论的摘录:

您在Jenkins上运行Cobertura、Sonar或其他代码仪器工具吗?请注意,mvn site可能还配置了在生成的site中包含Cobertura报告。

Cobertura的问题在于它执行相当重的字节码仪器操作,包括添加一些自定义接口。当Spring启动时,会为bean生成代理。如果bean至少有一个接口,则使用标准Java代理。否则,它会尝试创建基于类的代理。

我猜测在您的情况下,使用了CGLIB类代理,但在Cobertura仪器化之后,Spring退回到了Java代理。这导致启动错误,因为依赖注入期望类(或CGLIB子类)。

简而言之,强制使用CGLIB类代理,你就会没问题了:

<aop:config proxy-target-class="true"/>

2
你太棒了Tomasz :-) 你的解释帮助了我。 - K. Siva Prasad Reddy
aop 的命名空间是什么? - Christian Schlichtherle
1
Spring Boot 中的这个属性是 spring.aop.proxy-target-class=true。 - Vikky

1

0

使用AspectJ时遇到了相同的问题。

有一个bean w

@Configuration public class MyConfig{

@Value("classpath:some.properties")
private Resource theResource;

@Bean
public  SomeResource getSomeResource()
{
    return  SomeResource.getOne(theResource);
}
/******/
 
  @Component
public class SomeResource{
   public SomeResource(Resource r) {...}
   public static getOne(Resource r} { return new SomeResource(r); }

这在AOP/AspectJ启用之前都正常工作。注入验证了SomeResource bean是来自于SomeResource类,但由于它是一个代理,所以导致崩溃。

解决方法:使用GLIBC代理而不是AspectJ代理来处理该Bean。

@EnableAspectJAutoProxy(proxyTargetClass=false)
public class SomeResource{...}

毫无意义,但现在收到了更清晰的信息

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils
 (file:/path/spring-core/5.2.10.RELEASE/spring-core-5.2.10.RELEASE.jar) to method
java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils

这意味着Java防止对此方法进行反射。Spring或Java需要解决这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接