【问题标题】:Getting Spring Error "Bean named 'x' must be of type [y], but was actually of type [$Proxy]" in Jenkins在Jenkins中出现Spring错误“名为'x'的Bean必须是[y]类型,但实际上是[$Proxy]类型”
【发布时间】:2012-01-13 14:02:02
【问题描述】:

我已经调试了一段时间了,我希望有人可以在这里提供一些启示。

我有一个使用 JDK 1.6 添加到 Jenkins 中的 Maven 项目。我在这个项目中使用 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 之外正常运行我的代码.

有什么想法吗?谢谢。

【问题讨论】:

  • 您是否在 Jenkins 上运行 Cobertura、Sonar 或其他代码检测工具?确切的 mvn 构建命令是什么?在处理编译错误后,您可以尝试将注入的文件替换为private Object writerData 并转储writerData.getClass().getInterfaces 吗?这可能会给我们一个线索,让我们知道这个不需要的和奇怪的代理的性质是什么。
  • 在 Jenkins 的“目标和选项”下,我有 clean cobertura:cobertura site。出于好奇,我将其更改为 clean test 并且效果很好。当我将其更改为clean site 时,我又遇到了同样的异常。看起来它与site 有关。有任何想法吗?谢谢。
  • @Thomasz,我究竟该如何转储writerData.getClass().getInterfaces? Intellij 基本上给了我一个常规的编译错误。
  • 肯定和mvn site有关。我从命令行运行了该命令,并且得到了确切的异常。我不确定为什么这与 test 的行为不同。
  • 试试这个:Arrays.asList(new Object().getClass().getInterfaces())afterPropertiesSet@PostConstruct 中。我有点惊讶site 导致这个问题出现,因为我知道 Cobertura/Sonar 倾向于引入这个错误。您可以单独尝试mvn cobertura:cobertura 吗?也应该失败...

标签: spring unit-testing junit hudson jenkins


【解决方案1】:

摘自上述问题 cmets:

您是否在 Jenkins 上运行 Cobertura、Sonar 或其他代码检测工具?请注意,mvn site 也可能被配置为在生成的site 中包含 Cobertura 报告。

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

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

长话短说,强制使用 CGLIB 类代理就可以了:

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

【讨论】:

  • 你太棒了 Tomasz :-) 你的解释帮助了我。
  • aop 的命名空间是什么?
  • Spring boot中这个属性是spring.aop.proxy-target-class=true
【解决方案2】:

使用 AspectJ 遇到了同样的问题。

有一颗豆子

@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 类,但由于它是代理,它会崩溃。

解决方案:为该 Bean 使用 GLIBC 代理而不是 AspectJ 代理。

@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 都需要解决这个问题。

【讨论】:

    猜你喜欢
    • 2021-03-21
    • 2016-12-31
    • 2019-03-07
    • 2014-12-09
    • 2013-01-10
    • 2018-10-05
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    相关资源
    最近更新 更多