解决controller层注入的service为null


1. 问题描述

由于过于自信,写的接口没有测试就给到了前端,前端在调用后接口出现NullPointerException空指针异常:

2. 问题排查

根据详细的错误日志发现,代码中通道@Autowired注解注入的Service类对象为NULL。
有人可能会说会不会是Service层的实现类上忘记加了@Service注解,显然不是。如果Service层的实现类没有加@Service注解,而又在Controller层进行注入,程序在启动时就会报错,以下是报错信息:
在这里插入图片描述

我做了一个测试,以下是测试Service层实现类代码:

//@Service
public class UserServiceImpl implements UserService {
    /**
     * 获取用户姓名
     */
    @Override
    public String getUserName() {
        return "张三";
    }
}

都不用到启动这一步,如果你使用了IDEA开发中举,IDEA就会直接给出警告:

无法自动装配。找不到 'UserService' 类型的 Bean。

在这里插入图片描述
所以肯定不是实现类没有加注解的原因。
后来经过筛查以及和其他Controller层的类进行对比差异,发现有个接口方法的修饰符是private,而且只有这一个方法是被private修饰的。于是我将其改为public,最后进行测试,不会报错了。

在这里插入图片描述
错误示例:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/getUserName")
    private String getUserName() {
        return userService.getUserName();
    }
}

3. 问题复现

在上面我把private改成public后,再去测试就好了,但原因是什么呢?
下面我自己建了一个测试demo进行了问题复现。

  • Service层
public interface UserService {
    /**
     * 获取用户姓名
     */
    String getUserName();
}
  • Service实现类
@Service
public class UserServiceImpl implements UserService {
    /**
     * 获取用户姓名
     */
    @Override
    public String getUserName() {
        return "张三";
    }
}
  • Controller层(直接将Controller类的接口方法修饰符改为private用于测试)
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/getUserName")
    private String getUserName() {
        return userService.getUserName();
    }
}
  • HTTP请求
GET http://localhost:8080/user/getUserName

运行后,请求结果如下:

在这里插入图片描述
根据结果可以看出,改成private后也是可以正常运行的呀!这是怎么回事啊?
我又排查了一遍,发现项目使用的框架有全局日志切面。难道AOP无法代理private方法吗?在百度求证查了半天有些人是这么说过。

下来就来测试一下,写一个测试AOP:

@Aspect
@Component
@Slf4j
public class AopTest {
    @Pointcut("execution(* com.example.demo.demos.web.controller..*(..))")
    public void controllerPointcut() {
    }

    // 在切入点之前执行的通知
    @Before("controllerPointcut()")
    public void beforeControllerMethod() {
        System.out.println("Before executing Controller method");
        // 可以在这里添加自定义的拦截逻辑
    }
}

在上述代码中,controllerPointcut()方法使用execution表达式定义了一个切入点,该表达式匹配com.example.demo.demos.web.controller包及其子包下的所有类的所有方法。然后,在beforeControllerMethod()方法中使用@Before("controllerPointcut()")注解指定在切入点匹配的方法执行之前执行的通知。

然后再测试一下:

在这里插入图片描述
OK,问题复现出来了。下面进行原理分析。

4. 原因分析

加上了动态代理以后,空指针的异常问题被复现出来了,那默认的代理是什么呢?背过八股文的都知道肯定是:cglib

cglib的代理方式是setSuperClass,是不会代理父类的private方法的。也就是说AOP无法代理private,那么到底是不是这原因导致bean=null呢?

下面做一个对比测试,Controller层的方法先改为public修饰,然后在return userService.getUserName();打上断点

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/getUserName")
    public String getUserName() {
        return userService.getUserName();
    }
}

这个时候userService不为空,证明是可以正常调用并输出结果的

在这里插入图片描述
结果如下:
在这里插入图片描述
在这里插入图片描述
然后将Controller层的方法改为private修饰,在return userService.getUserName();打上断点观察

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/getUserName")
    private String getUserName() {
        return userService.getUserName();
    }
}

在这里插入图片描述
从上图可以看出,这时候的userService为NULL。

### 回答1: 可能是以下原因导致Spring Boot注入Servicenull: 1. 没有在Service类上添加@Service注解,导致Spring无法识别该类为Service类。 2. 没有在Controller类中添加@Autowired或@Resource注解,导致Service类无法被注入。 3. Service类的构造函数没有被正确实现,导致Spring无法正确实例化该类。 4. Service类的命名不符合Spring的命名规范,导致Spring无法正确识别该类。 5. Service类所在的包没有被正确扫描,导致Spring无法找到该类。 如果以上原因都不是导致注入Servicenull的原因,可以尝试重启应用程序或检查依赖项是否正确引入。 ### 回答2: Spring Boot是一个轻量级的开发框架,具有自动配置和快速启动等特点,适用于快速开发中小型应用程序。在使用Spring Boot的过程中,我们可以使用依赖注入(DI)来管理对象之间的依赖关系。但有时候,我们在注入service的时候会发现它为null。 1. 检查@Service注解 @Service注解用于标识该类是一个服务类,如果没有添加该注解或者注解名称有误,则Spring会无法识别服务类,导致注入servicenull。因此,需要检查@Service注解是否正确地添加到服务类上。 2. 检查@ComponentScan配置 @ComponentScan用于指定Spring框架需要扫描的包路径,如果没有配置或者指定的路径有误,则Spring会无法扫描到服务类,导致注入servicenull。因此,需要检查@ComponentScan配置是否正确地添加在程序的入口处。 3. 检查@Autowired注解 @Autowired注解用于标识需要自动装配依赖关系的类、属性或构造函数。如果没有使用该注解或者注解名称有误,则Spring会无法自动注入service,导致注入servicenull。因此,需要检查@Autowired注解是否正确地添加到需要注入service上。 4. 检查@Service类是否实现了接口 在服务类中,如果没有实现任何接口,则无法使用接口注入service。如果必须使用接口注入,则需要先定义接口,再在服务类中实现该接口。否则,在注入service时会发生空指针异常。 5. 检查@Autowired注解的位置是否正确 @Autowired注解有两种用法,一种是使用在字段上,如:@Autowired private UserService userService;另一种是使用在构造函数中,如: @Autowired public UserController(UserService userService) { this.userService = userService; } 如果在使用构造函数注入时,需要确保@Autowired注解在构造函数上,而非字段上。 总之,springboot注入servicenull的原因可能有很多,需要仔细检查以上几个方面。只要找到原因,解决问题也就不难了。 ### 回答3: 在使用Spring Boot注入Service时,遇到Servicenull的问题,常见的原因有以下几种: 1.未使用@ServiceService类注释为一个Spring组件 在Service类中,需要使用注解@Service来将其注释为一个Spring组件,才能被Spring Boot扫描并实例化。如果Service类未被注释为Spring组件,则在注入时会出现null指针异常。 2.未使用@Autowired注入Service 在使用Service时,需要在需要使用该Service的类中使用@Autowired注入Service,以便Spring Boot将其注入。如果未使用@Autowired来注入Service,则在使用该Service时会出现null指针异常。 3.未在配置文件中配置Service的实例化 在配置文件中,需要将Service类的实例化配置到Spring Boot的上下文中,以便Spring Boot能够创建关于该类的Bean定义,并将其实例化。如果未在配置文件中配置Service的实例化,则在注入时会出现null指针异常。 4.未正确书写Service类名 在注入时,需要正确书写Service类的名称。如果未正确书写Service类的名称,或者类名与文件名不一致,则在注入时会出现null指针异常。 5.循环依赖 在使用Spring Boot时,如果Service类存在循环依赖,则在注入时会出现null指针异常。此时需要使用@Lazy注解来解决循环依赖问题,同时需要注意循环依赖可能存在的性能问题。 综上所述,当出现Spring Boot注入Servicenull的情况时,应该检查是否正确使用了@Service和@Autowired注解,是否在配置文件中配置了Service的实例化,是否正确书写了类名,并且是否存在循环依赖等情况。找到问题所在后,进行相应的修正即可解决该问题。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七维大脑

打赏10元我只能获取8元...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值