【debug log】SpringDataJpa启动时报错UnsatisfiedDependencyException,无法注入相关repository对象

最近在疯狂点科技树,听说spring data jpa比mybatis方便太多,恰好本人自己的毕设项目iedu(基于spring cloud的在线教育平台)才处于初始阶段(只设计好了表和实体类),接下来需要设计一个单点登录系统,于是我干脆打算使用spring data作为持久层框架。

鲁迅说过万事应先从Hello World写起,所以我首先就写了个demo:

(以下为单点登录服务iedu-user-sso中的代码)

/**仓库类 - UserRepository
 * Spring Data JPA的基础使用,不解释。
*/

package cn.shadl.ieduusersso.repository;

import cn.shadl.ieducommonbeans.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}
/**
 * 启动类兼测试类(因为只是个demo懒得专门写一个测试类所以就直接在启动类搞啦~)
 */

package cn.shadl.ieduusersso;

import cn.shadl.ieducommonbeans.domain.User;
import cn.shadl.ieduusersso.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@SpringBootApplication
@RestController

public class IeduUserSsoApplication {

    public static void main(String[] args) {
        SpringApplication.run(IeduUserSsoApplication.class, args);
    }

    @Autowired
    private UserRepository userRepository;

    @RequestMapping("/")
    public String test(){
        List<User> users = userRepository.findAll();
        for(User user:users){
            System.out.println(user);
        }
        return "user list is displayed in console.";
    }
}

结果:启动失败

关键信息:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ieduUserSsoApplication': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class cn.shadl.ieducommonbeans.domain.User

翻译成人话,这个UnsatisfiedDependencyException报的信息大意是:application类启动失败,因为无法根据“userRepository”这个name注入对象。

能看懂报错信息的话debug就简单一半了。根据这信息基本就可以将问题定位到那个被@AutoWired注解修饰的userRepository成员变量那里。由于以前学过ssm,所以当时我就有点先入为主地认为要写一个UserRepository的实现类,然后建了个impl包写UserRepositoryImpl.java。。。写着写着突然觉得不对劲啊??如果我都把接口里的这些方法给实现了那JpaRepository这个接口做了什么??于是果断把写了10%的实现类及其包删了,此时我已经知道要给这个repository接口注入的实例对象肯定是不需要自己写的(当然是使用Spring Data提供的啦),但既然实例化有spring替我考虑好了,为什么userRepository会autowired失败呢?经上网搜索,发现有不少人也曾受过repository对象注入失败这个问题的困扰,情况主要有以下几种:

1.网友甲,这位仁兄给自己写的repository类加了@Repository注解(继承了jpa接口就不需要再加repository注解,加了就等于注入两个重复的bean,项目启动时会抛NoUniqueBeanDefinitionException异常)

2.网友乙,这位的报错信息大概是说所需注入的bean的类型没有找到,他的解决方式是给启动类加上@EnableJpaRepositories("com.example.repository")注解

3.网友丙,这位老兄的情况是自定义repository类在继承JpaRepository接口时没有指定泛型(XxxRepository extends JpaRepository<T,ID>,这人没写<xx,xx>)

4.网友丁,这位也是写了个demo,结果单元测试屡测屡报错。原来是因为他的repository类根本不在测试启动类的扫描范围内,springboot的默认扫描配置是扫描与application启动类同级或以下的包

我和以上四个情况都不同,没有加@Repository注解导致歧义、编译器没说找不到bean的类型、没有忘记指定泛型、repository类也在application类的扫描范围内,乍一看上面这四位网友犯的错我都没犯,但是!!!我随即反应过来我程序错误的地方,是网友丙的情况+网友丁的情况。


不卖关子,问题确实是出于这里:

public interface UserRepository extends JpaRepository<User, Long> {}

我并没有忘记指定泛型,但为什么是因为这里的问题而找不到bean呢?

请看文章开头给出的UserRepository仓库类源码,注意这个User是另一个服务里引入过来的:

import cn.shadl.ieducommonbeans.domain.User

由于是spring cloud项目,根据微服务项目的设计规范,我将通用的实体类都写在了iedu-common-beans这个服务里,但这里使用spring data jpa的是单点登录服务iedu-user-sso,所以这个User自然就不在sso服务启动类的默认扫描范围里了。

解决方法:

把User写在sso服务里

……

骗你的,这样可以解决问题,但就不符合微服务设计的初衷了

正解是定义sso服务启动类的扫描范围,覆盖默认配置。由于我这里common服务和sso服务的包名拥有共同前缀“cn.shadl”,所以我只需将@SpringBootApplication注解中的参数修改成:

@SpringBootApplication(scanBasePackages = {"cn.shadl"})

如果想扫描跨项目扫描但两个项目没有共同前缀,可以这样写:

@SpringBootApplication(scanBasePackages = {"com.example.A(这个服务自己所在的包)" , "cn.exp2.B(另一个服务里的包)"})

修改后,application可以扫描到另一个项目里的实体类User了,“UserRepository extends JpaRepository<User,Long>”识别成功,成功把UserRepository接口实例化并把引用userRepository指向该对象。


顺带一提,名称除了首字母小写以外其余部分与类名完全一致的变量之所以能被spring自动识别,是因为spring容器本质就是一个Map:key为变量名,value则为bean的实例对象,当编程者没有指定具体的key时,key默认就是类名首字母转小写。像@component及其衍生类(@Controller,@Service,@Repository等)如果没指定value,那么默认key就是类名首字母转小写,如果自定义了value值比如@Component("myBean")这样,则要变量名为myBean的引用才能autowired到这个bean(这是题外话了,spring基础原理)

【debug log】SpringDataJpa启动时报错UnsatisfiedDependencyException,无法注入相关repository对象【debug log】SpringDataJpa启动时报错UnsatisfiedDependencyException,无法注入相关repository对象 ShadL-17 发布了3 篇原创文章 · 获赞 0 · 访问量 58 私信 关注
上一篇:【死磕Spring】| Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?


下一篇:学习MVC第6部分:带有实体框架的MVC3应用程序中的通用存储库模式