我正在构建一个用户登录页面。当我输入正确的用户手机和密码时,服务器返回以下空指针异常(如下)。从 GlobalExceptionHandler 打印“其他异常”
.m.m.a.ExceptionHandlerExceptionResolver:已解决 [java.lang.NullPointerException]
此外,LoginController 下的 public RespBean doLogin(@Valid LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) 函数也不会运行。 (System.out.println(“LoginController doLogin running”);不会打印出来)。空指针异常发生在从服务器接收到手机和密码之前。
在添加 HttpServletRequest 请求、HttpServletResponse 响应作为函数的输入之前,用户登录工作正常,并且 doLogin 函数得到完整执行。添加这两个输入后,出现空指针异常。
我能否获得一些有关如何识别导致空指针异常的原因的指导?
登录.html
<!DOCTYPE HTML>
<html lang = "en"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
<!-- jquery -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" />
<script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
<!-- jquery-validator -->
<script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script>
<script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script>
<!-- layer -->
<!-- pop ups-->
<script type="text/javascript" th:src="@{/layer/layer.js}"></script>
<!-- md5.js -->
<script type="text/javascript" th:src="@{/js/md5.min.js}"></script>
<!-- common.js -->
<script type="text/javascript" th:src="@{/js/common.js}"></script>
<style type="text/css">
html,body{
height:100%;
width:100%;
}
body{
background:url('@{/img/bg.jpg}') no-repeat;
background-size:100% 100%;
padding-top:100px;
}
</style>
</head>
<body>
<form name="loginForm" id="loginForm" method="post" style="width:50%; margin:0 auto;">
<h2 style="text-align:center; margin-bottom: 20px">User Login</h2>
<div class="form-group">
<div class="row">
<label class="form-label col-md-4">Please input your mobile number</label>
<div class="col-md-5">
<!-- mobile len requirement 11 digits-->
<input id="mobile" name = "mobile" class="form-control" type="text" placeholder="mobile-number" required="true" minlength="11" maxlength="11" />
</div>
<div class="col-md-1">
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="form-label col-md-4">Please input your password</label>
<div class="col-md-5">
<!-- password len requirement 6-16 digits-->
<input id="password" name="password" class="form-control" type="password" placeholder="password" required="true" minlength="6" maxlength="16" />
</div>
</div>
</div>
<div class="row" style="margin-top:40px;">
<div class="col-md-5">
<button class="btn btn-primary btn-block" type="reset" onclick="reset()">reset</button>
</div>
<div class="col-md-5">
<button class="btn btn-primary btn-block" type="submit" onclick="login()">login</button>
</div>
</div>
</form>
</body>
<script>
function login(){
// validate content in the table first, after validation passes, call doLogin function
$("#loginForm").validate({
submitHandler:function(form){
doLogin();
}
});
}
function doLogin(){
g_showLoading();
//get password input from user and do the first MD5 encoding
var inputPass = $("#password").val();
var salt = g_passsword_salt;
var str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
var password = md5(str);
$.ajax({
url: "/login/doLogin",
type: "POST",
data:{
mobile:$("#mobile").val(),
password: password
},
success:function(data){
layer.closeAll();
if(data.code == 200){
layer.msg("login successful");
window.location.href="/goods/toList";
}else{
layer.msg("something wrong")
layer.msg(data.message); // data.msg won't work
}
},
error:function(){
layer.closeAll();
}
});
}
</script>
</html>
LoginController.java
package com.xxxx.seckill.controller;
import com.xxxx.seckill.service.IUserService;
import com.xxxx.seckill.vo.LoginVo;
import com.xxxx.seckill.vo.RespBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
@Controller
//should not use RestController, should use Controller; RestController will add response body to all methods under this class, will return a body, not jumping to another page
@RequestMapping("/login")
@Slf4j //lombok, for logging output e.g. log infor, log warning, log error
public class LoginController {
@Autowired
private IUserService userService;
@RequestMapping("/toLogin") //to log in page 跳转登录页
public String toLogin(){
return "login";
}
@RequestMapping("/doLogin")
@ResponseBody
//公用返回对象 response bean, to return response bean, need to @ResponseBody
public RespBean doLogin(@Valid LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
System.out.println("LoginController doLogin running");
// 接受传参 receive parameter from users with LoginVo
log.info("{}", loginVo); //log can be used directly as it is included in Slf4j
return userService.doLogin(loginVo, request, response);
// return null;
}
}
用户服务 doLogin 函数不会被执行。 System.out.println 都没有被打印。
public interface IUserService extends IService<User> {
RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
System.out.println("userService doLogin");
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
// find user with mobile
User user = userMapper.selectById(mobile);
System.out.println("user is not null, user mapper injected");
if (null == user) {
System.out.println("user doesn't exist");
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
// verify whether user password is correct
if (!MD5Util.frontPassToDBPass(password, user.getSalt()).equals(user.getPassword())) {
System.out.println("wrong password");
throw new GlobalException(RespBeanEnum.LOGIN_ERROR);
}
System.out.println("generating cookie");
//generate cookie
String ticket = UUIDUtil.uuid();
request.getSession().setAttribute(ticket, user);
CookieUtil.setCookie(request, response, "userTicket", ticket);
return RespBean.success();
}
}
全局异常处理程序
@RestControllerAdvice // will return a response body
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public RespBean ExceptionHangler(Exception e) {
if (e instanceof GlobalException) {
System.out.println("GlobalException");
GlobalException ex = (GlobalException) e;
return RespBean.error(ex.getRespBeanEnum());
} else if (e instanceof BindException) {
System.out.println("BindException");
BindException ex = (BindException) e;
RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
respBean.setMessage("Validation error: " + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return respBean;
}
System.out.println("Other Exception");
return RespBean.error(RespBeanEnum.ERROR);
}
}
我尝试在IDEA中重建项目。我添加了所有这些 system.out.println 来确定原因。除了 GlobalExceptionHandler 中的“其他异常”之外,没有打印任何 system.out.println。
我想通了。空指针异常来自public RespBean doLogin(@Valid LoginVo loginVo, HttpServletRequest request, HttpServletResponse response)中的loginVo = null。如果没有“@Valid”,程序运行正常。所以问题发生在验证/异常处理过程中。我意识到 ExceptionHangler(Exception e) 中有一个类型。应该是“异常处理程序”。 ExceptionHandler 和 ControllerAdvice 等注释使用反射来发现和应用类中定义的功能。这些注释依赖正确命名的方法来处理特定的异常。