Tapestry结合Spring Around切面的类加载冲突问题求助
问题根源
出现java.lang.ClassCastException: class XYZ cannot be cast to class XYZ的核心原因是:同一类XYZ被两个不同的类加载器(应用类加载器和Tapestry的PlasticClassLoader)加载,JVM中不同类加载器加载的类会被视为完全独立的类型,导致类型转换时失败。Spring的Around切面采用编译后织入(CTW)时会修改类字节码,而Tapestry的PlasticClassLoader会动态生成组件的代理类,两者叠加后引发类加载重复问题。
解决方案
1. 限定Spring事务切面的织入范围
将Spring的@Transactional切面仅织入业务服务类,排除Tapestry组件类(因为组件会被PlasticClassLoader动态处理)。通过调整切面的PointCut实现:
@Aspect @Component public class TransactionalAspect { @Pointcut("execution(* com.yourcompany.service..*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)") private void transactionalServiceMethods() {} @Around("transactionalServiceMethods()") public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable { // 事务逻辑实现 } }
这里将织入范围限定在com.yourcompany.service包下的类,避免Tapestry组件被Spring切面修改字节码。
2. 组件内部避免直接调用同类事务方法
将组件中带@Transactional的方法抽离到独立的Spring服务Bean中,组件通过注入该Bean调用事务方法,而非直接调用同类方法。
- 原错误写法(组件内直接调用):
public class MyComponent { void onSuccessFromForm() { this.doTransactionalWork(); // 触发类加载冲突 } @Transactional private void doTransactionalWork() { // 事务操作 } } - 修正写法:
// 独立的Spring服务类 @Service public class WorkService { @Transactional public void doTransactionalWork() { // 事务操作 } } // Tapestry组件 public class MyComponent { @Inject private WorkService workService; void onSuccessFromForm() { workService.doTransactionalWork(); // 通过Spring代理对象调用,无冲突 } }
3. 配置PlasticClassLoader共享应用类加载器的类
修改Tapestry的ApplicationModule,让PlasticClassLoader优先使用应用类加载器加载已存在的类,避免重复加载:
public class ApplicationModule { @Contribute(PlasticProxyFactory.class) public static void configurePlasticClassLoader(MappedConfiguration<Class<?>, ClassTransformation> config) { config.add(Object.class, transformation -> { // 设置PlasticClass使用应用类加载器 transformation.getPlasticClass().setClassLoader(Thread.currentThread().getContextClassLoader()); }); } }
该配置全局调整PlasticClassLoader的类加载策略,优先复用应用类加载器加载的类定义。
4. 切换为Spring运行时织入(LTW)替代编译后织入(CTW)
如果编译后织入的冲突无法避免,改用Spring的运行时织入:
- 在JVM启动参数中添加:
-javaagent:path/to/spring-instrument.jar(替换为实际的jar路径) - 在Spring配置类中启用织入:
@Configuration @EnableLoadTimeWeaving public class SpringConfig {}
运行时织入在类加载阶段动态织入切面,与Tapestry的PlasticClassLoader代理机制兼容性更好,不会产生类重复加载问题。
内容的提问来源于stack exchange,提问作者bednee

