调用findBySubstring时执行接口默认方法而非实现方法的问题排查
当从ArticleAutocompleteServiceImpl中调用findBySubstring()方法时,执行的是接口的默认方法而非实现类中的实现方法。这是否是泛型导致的?我哪里操作有误?如何保留当前结构并让实现方法生效?
编辑补充:若移除FindBySubstringRepositorySupport中的默认方法仅保留方法签名,功能可正常运行,但缺点是所有实现FindBySubstringRepositorySupport的服务都需添加空实现,而我仅在少数服务中需要真实实现。
ArticleAutocompleteServiceImpl.java
@Service public class ArticleAutocompleteServiceImpl implements ArticleAutocompleteService { private final ArticleRepository articleRepository; public ArticleAutocompleteServiceImpl( final ArticleRepository articleRepository ) { this.articleRepository = articleRepository; } @Override public List<ArticleURINameDTO> autocomplete( final String q ) { // 期望调用实现类的方法,而非接口默认方法 final List<Article> lst = this.articleRepository.findBySubstring( q, 10, ObjectStatus.ENABLED ); final Locale locale = ... return lst.stream().map(a -> new ArticleURINameDTO(a, locale)).toList(); } }
ArticleRepositoryImpl.java
public class ArticleRepositoryImpl implements ArticleExtendRepository { @PersistenceContext private EntityManager entityManager; private final StoredProcService storedProcService; public ArticleRepositoryImpl( final StoredProcService storedProcService ) { this.storedProcService = storedProcService; } // 本应覆盖接口默认方法的实现 @Override public List<Article> findBySubstring( final String namePart, final Integer limit, final ObjectStatus objectStatus ) { return this.storedProcService.execute( ... ); } }
ArticleRepository.java
public interface ArticleRepository extends PagingAndSortingRepository<Article, Integer>, CrudRepository<Article, Integer>, ArticleExtendRepository { // 无额外方法 }
ArticleAutocompleteService.java
public interface ArticleAutocompleteService { List<ArticleURINameDTO> autocomplete(String q); }
FindBySubstringRepositorySupport.java
public interface FindBySubstringRepositorySupport<T> { // 无实现时执行的默认方法 default List<T> findBySubstring( final String namePart, final Integer limit, final ObjectStatus objectStatus ) { throw new IllegalStateException( "方法未实现" ); } }
ArticleExtendRepository.java
public interface ArticleExtendRepository extends FindBySubstringRepositorySupport<Article> { // 其他无关方法签名 long countByStatus( ObjectStatus objectStatus ); }
这不是泛型的问题,核心原因是Spring Data Repository的自定义实现类命名规则未被遵守。
Spring Data JPA在生成Repository代理类时,会自动识别对应扩展接口的实现类,但要求实现类的命名必须是「扩展接口名 + Impl」。你的情况中,ArticleExtendRepository是扩展接口,但对应的实现类叫ArticleRepositoryImpl,Spring无法将两者关联,因此会直接调用接口的默认方法,而非你写的自定义实现。
要保留当前结构并解决问题,推荐以下两种方案:
方案一:遵循命名规则重命名实现类
将ArticleRepositoryImpl改名为ArticleExtendRepositoryImpl,使其与扩展接口ArticleExtendRepository的命名匹配。这样Spring Data会自动将该实现类与ArticleExtendRepository关联,ArticleRepository继承该接口后,调用findBySubstring()时就会执行你的自定义实现,而非默认方法。
方案二:手动指定实现类关联
在配置类的@EnableJpaRepositories注解中,通过repositoryImplementationPostfix参数修改默认的实现类后缀(比如你想保留ArticleRepositoryImpl,可以设置repositoryImplementationPostfix = "Impl",同时让ArticleRepositoryImpl直接实现ArticleRepository),但这种方式会破坏当前的接口分层结构,不如方案一规范。
关于你担心的「所有实现FindBySubstringRepositorySupport的服务都要写空实现」的问题,保留默认方法的设计是合理的,只要确保需要自定义实现的扩展接口对应Impl类被Spring正确识别,就不会触发默认方法的异常。
内容的提问来源于stack exchange,提问作者user1034461

