如何解决‘Cannot subclass final class java.lang.reflect.Method’异常?
解决Spring HATEOAS中Cannot subclass final class java.lang.reflect.Method异常
问题场景
测试代码:
@InjectMocks ReportAssembler reportAssembler; private ReportingReturnResponse reportingReturnResponse; private ReturnReport returnReport; private StaticPathLinkBuilder staticPathLinkBuilder; @Test public void testMergeReturnCompletesSuccessfully() { ReportingResource reportResource = reportAssembler.mergeReturn(returnReport, this.reportingResource); assertNotNull(reportResource.getReturnResource()); }
被调用的业务代码:
public ReportingResource mergeReturn(ReturnReport returnReport, ReportingResource reportingResource) { Link link = staticPathLinkBuilder.linkTo( methodOn(ReturnController.class).getReturnById( returnReport.getAccount().getId(), returnReport.getReturn().getId()) ).withSelfRel(); return reportingResource; } public LinkBuilder linkTo(Object dummyInvocation) { LastInvocationAware lastInvocationAware = (LastInvocationAware) dummyInvocation; MethodInvocation methodInvocation = lastInvocationAware.getLastInvocation(); StaticPathLinkBuilder staticPathLinkBuilder = getThis(); return staticPathLinkBuilder.linkTo(methodInvocation.getMethod(), methodInvocation.getArguments()); }
运行测试时,return staticPathLinkBuilder.linkTo(methodInvocation.getMethod(), methodInvocation.getArguments())抛出异常:
org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class java.lang.reflect.Method: Common causes of this problem include using a final class or a non-visible class ... Caused by: java.lang.IllegalArgumentException: Cannot subclass final class java.lang.reflect.Method ...
原因分析
异常核心原因:
java.lang.reflect.Method是final类,CGLIB无法为final类生成代理子类;- 测试中
staticPathLinkBuilder是Mockito创建的Mock对象,且配置了调用真实方法(比如@Mock(answer = Answers.CALLS_REAL_METHODS)),导致执行到linkTo(methodInvocation.getMethod(), methodInvocation.getArguments())时,Spring HATEOAS内部尝试用CGLIB代理Method对象,触发了final类不能被代理的限制。
解决方案
方案1:MocklinkTo方法的返回值,跳过真实逻辑
直接MockStaticPathLinkBuilder的linkTo方法,返回预先构造的LinkBuilder或Link对象,避免进入触发异常的真实代码:
@ExtendWith(MockitoExtension.class) class ReportingAssemblerReturnTest { @InjectMocks ReportAssembler reportAssembler; @Mock private StaticPathLinkBuilder staticPathLinkBuilder; private ReturnReport returnReport; private ReportingResource reportingResource; @BeforeEach void setUp() { // 初始化测试数据 returnReport = new ReturnReport(); Account account = new Account(); account.setId(1L); ReturnEntity returnEntity = new ReturnEntity(); returnEntity.setId(2L); returnReport.setAccount(account); returnReport.setReturn(returnEntity); reportingResource = new ReportingResource(); // Mock linkTo方法的行为,返回模拟的LinkBuilder LinkBuilder mockLinkBuilder = Mockito.mock(LinkBuilder.class); Link mockLink = Link.of("/accounts/1/returns/2"); when(mockLinkBuilder.withSelfRel()).thenReturn(mockLink); when(staticPathLinkBuilder.linkTo(any(Object.class))).thenReturn(mockLinkBuilder); } @Test public void testMergeReturnCompletesSuccessfully() { ReportingResource result = reportAssembler.mergeReturn(returnReport, reportingResource); assertNotNull(result.getReturnResource()); } }
方案2:改用Spring HATEOAS测试工具构造Link
如果需要构造真实的Link,可使用Spring HATEOAS的MockMvcLinkBuilder替代业务中的StaticPathLinkBuilder,或在测试中直接构造Link对象:
// 在测试中直接构造符合要求的Link并mock返回 when(staticPathLinkBuilder.linkTo(any(Object.class))).thenReturn( MockMvcLinkBuilder.linkTo(MockMvcLinkBuilder.methodOn(ReturnController.class).getReturnById(1L, 2L)) );
方案3:调整Mock配置,禁止调用真实方法
如果不需要调用StaticPathLinkBuilder的真实方法,移除CALLS_REAL_METHODS配置,让Mockito默认返回null或自定义mock行为,避免触发异常逻辑。
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

