最近遇到了这样一个问题:

java.lang.NullPointerException: Cannot invoke "com.atguigu.ioc_02.UserService.sayHello()" because "userService" is null

一开始我以为是Spring的对象没有注册,于是费了老半天在Test类中不停getBean来测试这个方法对应的类到底有没有注册成功:

import com.atguigu.ioc_02.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceTest {

    @Test
    public void testUserService() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-02.xml");

        // 测试userService(单个参数)
        try {
            UserService userService = (UserService) context.getBean("userService");
            System.out.println("✅ userService 创建成功");
            userService.sayHello();
        } catch (Exception e) {
            System.out.println("❌ userService 创建失败: " + e.getMessage());
        }

        // 测试userService1(三个参数)
        try {
            UserService userService1 = (UserService) context.getBean("userService1");
            System.out.println("✅ userService1 创建成功");
            userService1.sayHello();
        } catch (Exception e) {
            System.out.println("❌ userService1 创建失败: " + e.getMessage());
        }
    }
}

测试的结果没有问题,   确实是注册成功的。

但是问题其实根本就不是有没有注册成功的问题,而是在于一开始注入的依赖是空的:

package com.atguigu.ioc_02;

public class UserService {
    
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    private int age;

    private String name;

    public UserService(int age , String name ,UserDao userDao) {
        this.userDao = userDao;
        this.age = age;
        this.name = name;
    }

    public void sayHello(){

        System.out.println("Hello!");
    }


}

xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--引用和被引用的组件 必须全在ioc容器-->

    <!--1.单个构造函数全部注入-->
    <!--
        springioc容器是一个高级容器,内部会有缓存动作   1.先创建对象【ioc】 2.再进行属性赋值【di】
    -->

    <!--步骤1:将它们都存放在ioc容器-->
    <bean id="userService" class="com.atguigu.ioc_02.UserService">
        <!--构造函数传值 di的配置
        <constryctor-arg 构造函数传值的di配置
            value = 直接属性赋值 String name = “张三”  int age = “18”
            ref   = 引用其他的bean  beanid值
        -->
        <constructor-arg ref="userDao"/>
    </bean>

    <bean id="userDao" class="com.atguigu.ioc_02.UserDao"/>



    <!--2.多个构造函数的顺序填写-->
    <bean id="userService1" class="com.atguigu.ioc_02.UserService">
        <!--方案1:构造函数的顺序填写 value直接赋值 ref引用其他的beanid-->
        <constructor-arg type="int" value="18"/>
        <constructor-arg type="java.lang.String" value="张三"/>
        <constructor-arg ref="userDao"/>
    </bean>

    <bean id="userService2" class="com.atguigu.ioc_02.UserService">
        <!--方案2:构造函数的名称填写 不用考虑顺序 name = 构造函数的名字-->
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="age" value="18"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>


    <!--3.触发setter方法进行注入-->
    <bean id="movieFinder" class="com.atguigu.ioc_02.MovieFinder"/>

    <bean id="simpleMovieLister" class="com.atguigu.ioc_02.SimpleMovieLister">
        <!--
            name->属性名 setter方法的 去set和首字母小写的值 调用set方法的名
            setMovieFinder -> movieFinder
            value / ref 二选一 value=“直接属性值” ref=“其他bean的id
        -->
        <property name="movieName" value="消失的她"/>
        <property name="movieFinder" ref="movieFinder"/>
    </bean>
</beans>

这里看到其实已经注册成功的

其实是构造器注入的时候注入的字段是空值:(Autowired省略了)

package com.atguigu.ioc_02;

import org.springframework.beans.factory.annotation.Autowired;

public class UserService {
    
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    private int age;

    private String name;
    
    public UserService(int age , String name ,UserDao userDao) {
        this.userDao = userDao;
        this.age = age;
        this.name = name;
    }

    public void sayHello(){

        System.out.println("Hello!");
    }


}

需要进行修改:添加一个允许加入null值依赖的注解参数:

package com.atguigu.ioc_02;

import org.springframework.beans.factory.annotation.Autowired;

public class UserService {
    
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    private int age;

    private String name;

    @Autowired(required = false)
    public UserService(int age , String name ,UserDao userDao) {
        this.userDao = userDao;
        this.age = age;
        this.name = name;
    }

    public void sayHello(){

        System.out.println("Hello!");
    }


}

这样就行了!!

总结:

一定要注意区分到底是依赖注册有问题,还是依赖已经注册了但是状态为空的问题!!!!!

(1)注册问题:NoSuchBeanDefinitionException,启动时报错

(2)依赖状态问题:NullPointerException,运行时报错

如何测试呢?

(1)注册问题:使用getBean进行测试

(2)状态问题:空依赖状态测试即可,测试到底是不是为null

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐