注册方法在我的控制器类中抛出空指针异常

Posted

技术标签:

【中文标题】注册方法在我的控制器类中抛出空指针异常【英文标题】:Signup method throw null pointer Exception in my controller class 【发布时间】:2021-10-07 22:15:00 【问题描述】:

我想在我的数据库中使用UserService 类创建用户。为此,我使用 findByUserEmail 方法检查用户电子邮件是否不存在。我使用Optional 来面对NullpointerException 问题。 signup 方法的目的是如果在数据库中找不到提供的电子邮件,则在数据库中创建一个新用户。我只是删除了 userService 类中的其余方法,只留下了有问题的方法,即 findUserByEmail 方法. ComanLineRuner 接口用于将用户添加到数据库。我想知道为什么我使用的这种方法不起作用。

这个 UserService 实现类:

@Service
@Transactional
@Slf4j
public class UserServiceImpl implements UserService 

    private final UserRepository userRepository;

    private final RoleRepository roleRepository;

    private final ModelMapper modelMapper;

    private final BCryptPasswordEncoder passwordEncoder;


    @Autowired
    public UserServiceImpl(UserRepository userRepository, RoleRepository roleRepository, ModelMapper modelMapper,
            BCryptPasswordEncoder bCryptPasswordEncoder) 
        super();
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.modelMapper = modelMapper;
        this.passwordEncoder = bCryptPasswordEncoder;
    

    @Override
    public UserDto findUserByEmail(String email) 

        Optional<User> user = userRepository.findUserByEmail(email);

        if (user.isPresent()) 
            log.debug("findUserByEmail:", user.get());
            return UserMapper.userToUserDto(user.get());
        

        throw exception(EntityType.USER, ExceptionType.ENTITY_NOT_FOUND, "User with this: " + email + "Not found");

    

    @Override
    @SneakyThrows
    public UserDto signup(UserDto userDto) 

        Optional<User> user = userRepository.findUserByEmail(userDto.getEmail());

        Role userRole = new Role();
        if (user.isPresent()) 

            if (userDto.isAdmin()) 

                userRole = roleRepository.findRoleByUserRoleName(UserRole.ADMIN);
                
             else 
                userRole = roleRepository.findRoleByUserRoleName(UserRole.STUDENT);
                
            

            user = Optional.ofNullable(new User().setEmail(userDto.getEmail()).setFirstName(userDto.getFirstName())
                    .setLastName(userDto.getLastName()).setPassword(passwordEncoder.encode(userDto.getPassword()))
                    .setRoles(new HashSet<>(Arrays.asList(userRole))).setMobileNumber(userDto.getMobileNumber()));
            log.debug("signup new user:", UserMapper.userToUserDto(userRepository.save(user.get())));
            return UserMapper.userToUserDto(userRepository.save(user.get()));
        
        throw exception(EntityType.USER, ExceptionType.ENTITY_NOT_FOUND, userDto.getEmail());
    

用户类别是:

public class User implements Serializable 

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "user_id",nullable = false,unique = true)
    private Long id;

    @Column(name = "email", unique = true, updatable = true)
    @NotNull
    private String email;

    @Column(name = "firstName")
    @NotNull
    private String firstName;

    @Column(name = "lastName")
    @NotNull
    private String lastName;

    @Column(name = "password")
    @NotNull
    private String password;
    

    @Column(name = "mobile_number")
    private String mobileNumber;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_role", joinColumns =  @JoinColumn(name = "user_id") , inverseJoinColumns = 
            @JoinColumn(name = "role_id") )
    private Set<Role> roles = new HashSet<Role>();

    public String getFullName() 
        return firstName != null ? firstName.concat(" ").concat(lastName) : "";
    

用户 Dto 类是:

public class UserDto 

    private String email;

    private String firstName;

    private String lastName;

    private String password;

    private boolean isAdmin;

    private String mobileNumber;
    
    private Set<RoleDto> roleDtos = new HashSet<>();
    public String getFullName() 
        return firstName != null ? firstName.concat(" ").concat(lastName) : "";
    

我的控制器类是:

public class AdminController 
    
    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @GetMapping(value = "/signup")
    public ModelAndView signup() 

        ModelAndView modelAndView = new ModelAndView("signup");
        modelAndView.addObject("adminSignupCommand", new AdminSignupCommand());
        return modelAndView;
    

    /**
     * @param adminSignupCommand
     * @param bindingResult
     * @param redirectAttributes
     * @return modelAndView
     */

    @PostMapping(value = "/signup")
    public ModelAndView creatnewUserAdmin(
            @Valid @ModelAttribute("adminSignupCommand") AdminSignupCommand adminSignupCommand,
            BindingResult bindingResult, RedirectAttributes redirectAttributes) 
         

      // *Check user email in database before to save it*

        UserDto userDto = userService.findUserByEmail(adminSignupCommand.getEmail());
        ModelAndView modelAndView = new ModelAndView("signup");

        if (userDto !=null) 

            bindingResult.reject("There is already a user registered with the email provided");
            log.debug("There is already a user registered with the email provided");
        

        if (bindingResult.hasErrors()) 

            return modelAndView;
         else 

            registerUserAdmin(adminSignupCommand);
            redirectAttributes.addFlashAttribute("message", "SuccesFully added the new user with admin role");
            modelAndView.addObject("successMessage", redirectAttributes);
            log.debug(" A user registered with the email provided");
        
        return  new ModelAndView("login");
    

    
    /**
     * @param adminSignupCommand
     * @return userDto
     */
    private UserDto registerUserAdmin(@Valid AdminSignupCommand adminSignupCommand) 
        UserDto userDto = new UserDto().setEmail(adminSignupCommand.getEmail())
                .setFirstName(adminSignupCommand.getFirstName()).setLastName(adminSignupCommand.getLastName())
                .setPassword(adminSignupCommand.getPassword()).setAdmin(true);
        UserDto userDto2 = userService.signup(userDto);
        return userDto2;

    



*CommandLineRunner*

    public class KalanblowApplication 

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

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;
    @Bean
    public CommandLineRunner addNewRole(RoleRepository roleRepository, UserRepository userRepository,
            UserService userService) 
        return (args) -> 
            log.debug("CommandLineRunner start");

            // create role in database
            Role adminRole = roleRepository.findRoleByUserRoleName(UserRole.ADMIN);

            

            // Create new User with role

            UserDto adminDto = userService.findUserByEmail("admin3@example.com");

            if (adminDto ==null) 
                adminDto = new UserDto();

                adminDto.setEmail("admin3@example.com");
                adminDto.setFirstName("admin3");
                adminDto.setLastName("admin3");
                adminDto.setPassword(passwordEncoder.encode("Example2021!"));
                adminDto.setMobileNumber("0256369645");
                adminDto.setRoleDtos(new HashSet<>(Arrays
                        .asList(new ModelMapper().map(adminRole, RoleDto.class))));
                
                System.out.println("adminDto is:" + adminDto);
                userService.signup(adminDto);
             
        ;
    



StackTrace return a java.lang.NullPonterException when saving Spring Entity
 **java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:794) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:775) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:345) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) ~[spring-boot-2.5.3.jar:2.5.3]
    at ml.kalanblowSystemManagement.KalanblowApplication.main(KalanblowApplication.java:34) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.5.3.jar:2.5.3]
Caused by: java.lang.NullPointerException: null
    at ml.kalanblowSystemManagement.exception.KalanblowSystemManagementException.format(KalanblowSystemManagementException.java:92) ~[classes/:na]
    at ml.kalanblowSystemManagement.exception.KalanblowSystemManagementException.throwException(KalanblowSystemManagementException.java:80) ~[classes/:na]
    at ml.kalanblowSystemManagement.exception.KalanblowSystemManagementException.throwException(KalanblowSystemManagementException.java:38) ~[classes/:na]
    at ml.kalanblowSystemManagement.service.impl.UserServiceImpl.exception(UserServiceImpl.java:286) ~[classes/:na]
    at ****ml.kalanblowSystemManagement.service.impl.UserServiceImpl.findUserByEmail(UserServiceImpl.java:86) ~[classes/:na]****
    at ml.kalanblowSystemManagement.service.impl.UserServiceImpl$$FastClassBySpringCGLIB$$d9d5886b.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.9.jar:5.3.9]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.9.jar:5.3.9]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.9.jar:5.3.9]
    at ml.kalanblowSystemManagement.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$aac12850.findUserByEmail(<generated>) ~[classes/:na]
    at ml.kalanblowSystemManagement.KalanblowApplication.lambda$0(KalanblowApplication.java:68) ~[classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:791) ~[spring-boot-2.5.3.jar:2.5.3]
    ... 10 common frames omitted**

【问题讨论】:

通常当你使用 optional 和 isPresent 时,你做事的方式是错误的。我们还必须猜测代码中的第 86 行是什么?请注意,这与您的控制器无关,而是与您编写的启动代码有关(CommandLIneRunner. 第 86 行对应的是 throw exception(EntityType.USER, ExceptionType.ENTITY_NOT_FOUND, "User with this:" + email + "Not found");,就是触发的异常方法。对不起,我刚刚添加了 CommandLinerRuner 哪个是方法调用,哪个做什么? findUserByemail throw null pointer exception if email is not found 是异常原因 【参考方案1】:

您的方法架构存在一些缺陷。

首先,如果没有找到UserfindUserByEmail 方法总是会引发Exception

您的Exception 似乎扩展了NullPointerException,我会考虑改用自定义异常。

所以当你调用findUserByEmail 方法,而User 不存在时,总是会引发异常。假设您想保持异常引发,您需要在每次调用该方法时捕获异常,以便应用对错误的缓解。 例如,您的 CommandLineRunner 可能是:

@Bean
public CommandLineRunner addNewRole(RoleRepository roleRepository, UserRepository userRepository,
        UserService userService) 
    return (args) -> 
        log.debug("CommandLineRunner start");

        // create role in database
        Role adminRole = roleRepository.findRoleByUserRoleName(UserRole.ADMIN);     

        // Create new User with role
        UserDto adminDto = null;
        
        try 
            adminDto = userService.findUserByEmail("admin3@example.com");
         catch (Exception ex) 
            adminDto = null;
               

        if (adminDto ==null) 
            adminDto = new UserDto();

            adminDto.setEmail("admin3@example.com");
            adminDto.setFirstName("admin3");
            adminDto.setLastName("admin3");
            adminDto.setPassword(passwordEncoder.encode("Example2021!"));
            adminDto.setMobileNumber("0256369645");
            adminDto.setRoleDtos(new HashSet<>(Arrays
                    .asList(new ModelMapper().map(adminRole, RoleDto.class))));
            
            System.out.println("adminDto is:" + adminDto);
            userService.signup(adminDto);
         
    ;

我宁愿建议不要在您的 findUserByEmail 方法上引发 Exception。请改用Optional&lt;UserDto&gt;,以使您的流程更清晰。

【讨论】:

【参考方案2】:

你说的很对,但我通过添加一个布尔方法 emailExist(String email) 解决了这个问题,我在其中调用了 findUserByEmail 方法。感谢大家的建议和反馈。 `public boolean emailExist(String email)

    return userRepository.findUserByEmail(email) != null;

所以我修改了注册方式

@Override
@SneakyThrows
public UserDto signup(UserDto userDto) 

    if (!emailExist(userDto.getEmail())) 

        throw exception(EntityType.USER, ExceptionType.DUPLICATE_ENTITY,
                "An user with that email adress already exists:" + userDto.getEmail());
    
    Role userRole = new Role();

    if (userDto.isAdmin()) 
        userRole = roleRepository.findRoleByUserRoleName(UserRole.ADMIN);
     else 
        userRole = roleRepository.findRoleByUserRoleName(UserRole.STUDENT);
    
    User user = new User().setBirthDate(userDto.getBirthDate()).setEmail(userDto.getEmail())
            .setFirstName(userDto.getFirstName()).setLastName(userDto.getLastName())
            .setMatchingPassword(userDto.getMatchingPassword()).setPassword(userDto.getPassword())
            .setMobileNumber(userDto.getMobileNumber()).setRoles(new HashSet<>(Arrays.asList(userRole)));

    return UserMapper.userToUserDto(userRepository.save(user));

【讨论】:

以上是关于注册方法在我的控制器类中抛出空指针异常的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的数组列表即使在初始化后也会抛出空指针异常?

HashCode 抛出空指针异常

Android - 接口callBack抛出空指针异常

称为验证的 Mockito Kotlin 单元测试方法抛出空指针异常

JSONObject 创建抛出空指针异常

SearchView 展开抛出空指针异常