问题描述
我有一个用 Java8 编写的 JEE 应用程序,它使用 Spring 4.3.24。由于我的应用程序的前端使用的是 JSF 2.x,我还使用了自定义 spring 范围 - 第三方库提供的对话访问,即 1.4 版中的 myfaces-orchestra
由于应用程序被 Selenium 测试广泛覆盖,我目前正在分析在多线程中运行测试的主题。详细说明:一个托管应用程序服务器的 JVM - 在我的例子中是 WebSphere 8.5.5,一个使用 JUnit 4.10 的 JVM 在多个线程中运行 selenium 测试。
我面临的问题,但仅在并行运行测试时,偶尔 ClassCastException
在尝试与对话访问 bean 交互时被抛出 cglib 类。
异常如下所示:
Exception: java.lang.classCastException: com.sun.proxy.$Proxy499 incompatible with some.package.PagebackingBean
at some.package.PagebackingBean$$FastClassBySpringcglib$$ecd1ff4d.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.cglibAopProxy$cglibMethodInvocation.invokeJoinpoint(cglibAopProxy.java:736)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.cglibAopProxy$DynamicAdvisedInterceptor.intercept(cglibAopProxy.java:671)
如前所述,异常只会不时发生。另外,可以注意到,这通常发生在两个或多个线程在同一毫秒内引用相同类型的会话 bean 的情况下(当然,由于这两个线程使用不同的会话,底层 bean 是不同的)
我已经排除的是:
- 这不是与底层 bean 初始化相关的问题。尽管 bean 具有 postconstruct,但它已成功初始化
- 我认为这个问题是在升级到 spring 4.x(从 3.x 开始)时提出的,所以我尝试禁用 Objenesis(通过将 spring.objenesis.ignore 设置为“true”),但这也没有帮助
解决方法
经过进一步调查,我想我找到了问题的根本原因 - 它位于 Orchestra 实现中
基本上, Orchestra 每次实例化会话访问 bean 时,都会操纵 bean 定义属性 - 它试图设置
AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE=Boolean.TRUE
据我所知,这个设置告诉 AOP 创建 bean 的 CGLIB 代理。由于 bean 定义中的 bean 属性保存在标准的 HashMap 中,因此这种方法会导致竞争条件,最终导致对话访问 bean 的双重代理