这篇文章主要分享一下项目里遇到的获取request对象为null的问题,具体是在登录的时候触发的邮箱提醒,获取客户端ip地址,然后通过ip地址定位获取定位信息,从而提示账号在哪里登录。
但是登录却发现获取request对象的时候报错了。
具体的代码如下:这个异常是自己手动抛出的。
package cn.edu.sgu.www.mhxysy.util;import cn.edu.sgu.www.mhxysy.consts.MimeType;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.restful.JsonResult;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import com.alibaba.fastjson.JSON;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** http工具类* @author heyunlin* @version 1.0*/
public class HttpUtils {/*** 获取HttpServletRequest对象* @return HttpServletRequest*/public static HttpServletRequest getRequest() {RequestAttributes attributes = RequestContextHolder.getRequestAttributes();if (attributes != null ) {return ((ServletRequestAttributes) attributes).getRequest();}throw new GlobalException(ResponseCode.ERROR, "获取request对象失败");}}
在项目其他地方也有用这个工具了获取HttpServletRequest对象,都能获取到,觉得很是奇怪。点进去RequestContextHolder这个类的代码里看了一下,好像找到问题了~
这是基于ThreadLocal实现的,所以有了子线程无法访问父线程中设置的数据问题,不知道开发者为什么不用InheritThreadLocal来避免这个问题0v0
于是,把涉及获取request对象ip地址获取的代码放在线程外面,这样就避免了空指针问题了~
package cn.edu.sgu.www.mhxysy.chain.login.impl;import cn.edu.sgu.www.mhxysy.chain.login.UserLoginHandler;
import cn.edu.sgu.www.mhxysy.config.property.EmailProperties;
import cn.edu.sgu.www.mhxysy.config.property.SystemSettingsProperties;
import cn.edu.sgu.www.mhxysy.entity.location.Location;
import cn.edu.sgu.www.mhxysy.util.EmailUtils;
import cn.edu.sgu.www.mhxysy.util.IpUtils;
import cn.edu.sgu.www.mhxysy.util.LocationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;/*** @author heyunlin* @version 1.0*/
@Component
public class EmailSendHandler implements UserLoginHandler {private Object params;private UserLoginHandler next;private final EmailUtils emailUtils;private final EmailProperties emailProperties;private final SystemSettingsProperties systemSettingsProperties;@Autowiredpublic EmailSendHandler(EmailUtils emailUtils,EmailProperties emailProperties,SystemSettingsProperties systemSettingsProperties) {this.emailUtils = emailUtils;this.emailProperties = emailProperties;this.systemSettingsProperties = systemSettingsProperties;}@Overridepublic void handle() {if (emailProperties.isEnable()) {String ip = IpUtils.getIp();String username = (String) params;String zoneId = systemSettingsProperties.getZoneId();// 定义日期格式DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");new Thread(() -> {try {String address = "广东广州";Location location = LocationUtils.getLocation(ip);if (systemSettingsProperties.isUseRealLocation()) {String locationAddress = location.getAddress();if (locationAddress != null) {address = locationAddress;}}String text = "您的账号" + username + "在" + address + "登录了。" +"[" + LocalDateTime.now(ZoneId.of(zoneId)).format(formatter) + "]";emailUtils.sendMessage(text);} catch (Exception e) {e.printStackTrace();}}).start();}if (next != null) {next.handle();}}@Overridepublic void setNext(UserLoginHandler next) {this.next = next;}@Overridepublic void setParams(Object params) {this.params = params;}}
总结:遇到这类问题,就把获取request对象的代码放在主线程中,避免因为ThreadLocal的缺陷导致程序异常。
好了,文章就分享到这里了,看完如果对你有所帮助,不要忘了点赞+收藏哦~