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

Spring Data接口默认方法重写未生效,仍执行默认实现求助

Spring Data 接口默认方法优先调用,重写实现未生效问题解决

问题场景

定义如下Spring Data接口:

public interface MyInterface<T, H extends Serializable, R extends Serializable> extends Repository<T, ID> {
  default List<T> findAll(H key) {
    return Collections.EMPTY_LIST;
  }
}

对应的实现类:

public class RepositoryImpl<T, H extends Serializable, R extends Serializable>
    extends ACConcreateClassWhichImplementRepository<T, H> implements MyInterface<T, H, R> {
  @Override
  public List<T> findAll(H key) {
    return findSome(hashKey); // 自定义业务方法
  }
}

异常现象

通过Controller调用findAll方法时,请求命中的是MyInterface中的默认方法,而非RepositoryImpl的重写实现,调用堆栈如下:

java.lang.Thread.State: RUNNABLE
      at com.MyInterface.findAll(MyInterface.java:12)
      at java.lang.invoke.LambdaForm$DMH.1294483354.invokeSpecialIFC(LambdaForm$DMH:-1)
      at java.lang.invoke.LambdaForm$MH.995808242.invoke(LambdaForm$MH:-1)
      at java.lang.invoke.LambdaForm$MH.1531802663.invoke(LambdaForm$MH:-1)
      at java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder:-1)
      at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:710)
      at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:86)
      
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
      at com.sun.proxy.$Proxy167.findAll(Unknown Source:-1)

验证有效场景

若将接口中的findAll改为抽象方法,调用可正常命中RepositoryImpl的实现,堆栈如下:

at com.RepositoryImpl.findAll(RepositoryImpl.java:16)
      at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
      at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:566)
      at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
      at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker$$Lambda$1357.2068454901.invoke(Unknown Source:-1)
      at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
      at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
      at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:529)
      at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
      at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:639)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163)
      at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
      
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
      at com.sun.proxy.$Proxy167.findAll(Unknown Source:-1)

使用版本:Spring 5.3.21、Spring Boot 2.7.0


解决方案

方案1:移除接口默认方法实现(最直接)

将MyInterface中的findAll改为抽象方法,沿用已验证有效的方式:

public interface MyInterface<T, H extends Serializable, R extends Serializable> extends Repository<T, ID> {
  List<T> findAll(H key); // 改为抽象方法
}

方案2:调整AOP拦截器优先级

Spring Data的DefaultMethodInvokingMethodInterceptor会优先处理接口默认方法,导致实现类重写方法被跳过。可通过自定义RepositoryFactoryBean调整拦截器顺序,让实现类方法拦截器先执行:

  1. 创建自定义RepositoryFactoryBean:
public class CustomRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
        extends RepositoryFactoryBeanSupport<T, S, ID> {

    public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
        super(repositoryInterface);
    }

    @Override
    protected RepositoryFactorySupport createRepositoryFactory() {
        return new CustomRepositoryFactory();
    }

    private static class CustomRepositoryFactory extends RepositoryFactorySupport {

        @Override
        protected Object getTargetRepository(RepositoryInformation information) {
            // 返回你的实现类实例,或沿用原有逻辑
            return new RepositoryImpl<>();
        }

        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return RepositoryImpl.class;
        }

        @Override
        protected void configureRepositoryProxy(AopProxyFactory proxyFactory) {
            super.configureRepositoryProxy(proxyFactory);
            // 调整拦截器顺序,让实现类方法拦截器优先执行
            proxyFactory.addAdvisor(0, new DefaultPointcutAdvisor(new RepositoryMethodInvocationPointcut(),
                    new ImplementationMethodExecutionInterceptor(getRepositoryInformation())));
        }
    }
}
  1. 在接口上指定自定义FactoryBean:
@RepositoryFactoryBean(CustomRepositoryFactoryBean.class)
public interface MyInterface<T, H extends Serializable, R extends Serializable> extends Repository<T, ID> {
  default List<T> findAll(H key) {
    return Collections.EMPTY_LIST;
  }
}

方案3:使用Repository片段组合

将默认方法与实现类方法拆分为不同片段,确保实现类方法被优先调用:

  1. 创建包含默认方法的片段接口:
public interface MyDefaultMethods<T, H extends Serializable> {
    default List<T> findAll(H key) {
        return Collections.EMPTY_LIST;
    }
}
  1. 修改主接口继承片段接口:
public interface MyInterface<T, H extends Serializable, R extends Serializable> 
        extends Repository<T, ID>, MyDefaultMethods<T, H> {
}
  1. 实现类保持原有重写逻辑即可。

方案4:升级依赖版本

该问题在Spring Data后续版本中可能已修复,可尝试升级Spring Data Commons到2.7.x最新补丁版,或直接升级Spring Boot到2.7.x系列的最新稳定版本。


内容的提问来源于stack exchange,提问作者SUBHASHC37

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:40:40