You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决‘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());
    }
}

如果需要构造真实的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:34:55