Parcourir la source

update 优化 修改spring源码上下文持有者 支持线程切换传递上下文数据 支持一切异步获取用户信息等操作

疯狂的狮子Li il y a 11 mois
Parent
commit
77f44574c0

+ 0 - 1
ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java

@@ -71,7 +71,6 @@ public class UserActionListener implements SaTokenListener {
         logininforEvent.setUsername(username);
         logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
         logininforEvent.setMessage(MessageUtils.message("user.login.success"));
-        logininforEvent.setRequest(ServletUtils.getRequest());
         SpringUtils.context().publishEvent(logininforEvent);
         // 更新登录信息
         loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);

+ 0 - 1
ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java

@@ -139,7 +139,6 @@ public class SysLoginService {
         logininforEvent.setUsername(username);
         logininforEvent.setStatus(status);
         logininforEvent.setMessage(message);
-        logininforEvent.setRequest(ServletUtils.getRequest());
         SpringUtils.context().publishEvent(logininforEvent);
     }
 

+ 0 - 2
ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java

@@ -11,7 +11,6 @@ import org.dromara.common.core.exception.user.CaptchaException;
 import org.dromara.common.core.exception.user.CaptchaExpireException;
 import org.dromara.common.core.exception.user.UserException;
 import org.dromara.common.core.utils.MessageUtils;
-import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.log.event.LogininforEvent;
@@ -108,7 +107,6 @@ public class SysRegisterService {
         logininforEvent.setUsername(username);
         logininforEvent.setStatus(status);
         logininforEvent.setMessage(message);
-        logininforEvent.setRequest(ServletUtils.getRequest());
         SpringUtils.context().publishEvent(logininforEvent);
     }
 

+ 7 - 0
ruoyi-common/ruoyi-common-core/pom.xml

@@ -99,6 +99,13 @@
             <artifactId>transmittable-thread-local</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>jakarta.faces</groupId>
+            <artifactId>jakarta.faces-api</artifactId>
+            <version>4.1.0</version>
+            <optional>true</optional>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 161 - 0
ruoyi-common/ruoyi-common-core/src/main/java/org/springframework/web/context/request/RequestContextHolder.java

@@ -0,0 +1,161 @@
+/*
+ * Copyright 2002-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.context.request;
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+import jakarta.faces.context.FacesContext;
+import org.springframework.lang.Nullable;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Holder class to expose the web request in the form of a thread-bound
+ * {@link RequestAttributes} object. The request will be inherited
+ * by any child threads spawned by the current thread if the
+ * {@code inheritable} flag is set to {@code true}.
+ *
+ * <p>Use {@link RequestContextListener} or
+ * {@link org.springframework.web.filter.RequestContextFilter} to expose
+ * the current web request. Note that
+ * already exposes the current request by default.
+ *
+ * 修改 spring 上下文存储方式 将 ThreadLocal 替换为 TransmittableThreadLocal
+ * 支持线程上下文切换变量传递 异步获取 spring 上下文
+ *
+ * @author Juergen Hoeller
+ * @author Rod Johnson
+ * @since 2.0
+ * @see RequestContextListener
+ * @see org.springframework.web.filter.RequestContextFilter
+ */
+public abstract class RequestContextHolder {
+
+	private static final boolean jsfPresent =
+			ClassUtils.isPresent("jakarta.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
+
+    // ThreadLocal 替换为 TransmittableThreadLocal
+	private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
+			new TransmittableThreadLocal<>();
+
+	private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
+			new TransmittableThreadLocal<>();
+
+
+	/**
+	 * Reset the RequestAttributes for the current thread.
+	 */
+	public static void resetRequestAttributes() {
+		requestAttributesHolder.remove();
+		inheritableRequestAttributesHolder.remove();
+	}
+
+	/**
+	 * Bind the given RequestAttributes to the current thread,
+	 * <i>not</i> exposing it as inheritable for child threads.
+	 * @param attributes the RequestAttributes to expose
+	 * @see #setRequestAttributes(RequestAttributes, boolean)
+	 */
+	public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
+		setRequestAttributes(attributes, false);
+	}
+
+	/**
+	 * Bind the given RequestAttributes to the current thread.
+	 * @param attributes the RequestAttributes to expose,
+	 * or {@code null} to reset the thread-bound context
+	 * @param inheritable whether to expose the RequestAttributes as inheritable
+	 * for child threads (using an {@link InheritableThreadLocal})
+	 */
+	public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
+		if (attributes == null) {
+			resetRequestAttributes();
+		}
+		else {
+			if (inheritable) {
+				inheritableRequestAttributesHolder.set(attributes);
+				requestAttributesHolder.remove();
+			}
+			else {
+				requestAttributesHolder.set(attributes);
+				inheritableRequestAttributesHolder.remove();
+			}
+		}
+	}
+
+	/**
+	 * Return the RequestAttributes currently bound to the thread.
+	 * @return the RequestAttributes currently bound to the thread,
+	 * or {@code null} if none bound
+	 */
+	@Nullable
+	public static RequestAttributes getRequestAttributes() {
+		RequestAttributes attributes = requestAttributesHolder.get();
+		if (attributes == null) {
+			attributes = inheritableRequestAttributesHolder.get();
+		}
+		return attributes;
+	}
+
+	/**
+	 * Return the RequestAttributes currently bound to the thread.
+	 * <p>Exposes the previously bound RequestAttributes instance, if any.
+	 * Falls back to the current JSF FacesContext, if any.
+	 * @return the RequestAttributes currently bound to the thread
+	 * @throws IllegalStateException if no RequestAttributes object
+	 * is bound to the current thread
+	 * @see #setRequestAttributes
+	 * @see ServletRequestAttributes
+	 * @see FacesRequestAttributes
+	 * @see jakarta.faces.context.FacesContext#getCurrentInstance()
+	 */
+	public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
+		RequestAttributes attributes = getRequestAttributes();
+		if (attributes == null) {
+			if (jsfPresent) {
+				attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
+			}
+			if (attributes == null) {
+				throw new IllegalStateException("No thread-bound request found: " +
+						"Are you referring to request attributes outside of an actual web request, " +
+						"or processing a request outside of the originally receiving thread? " +
+						"If you are actually operating within a web request and still receive this message, " +
+						"your code is probably running outside of DispatcherServlet: " +
+						"In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
+			}
+		}
+		return attributes;
+	}
+
+
+	/**
+	 * Inner class to avoid hard-coded JSF dependency.
+ 	 */
+	private static class FacesRequestAttributesFactory {
+
+		@Nullable
+		public static RequestAttributes getFacesRequestAttributes() {
+			try {
+				FacesContext facesContext = FacesContext.getCurrentInstance();
+				return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
+			}
+			catch (NoClassDefFoundError err) {
+				// typically for com/sun/faces/util/Util if only the JSF API jar is present
+				return null;
+			}
+		}
+	}
+
+}

+ 0 - 14
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java

@@ -13,7 +13,6 @@ import org.aspectj.lang.annotation.AfterReturning;
 import org.aspectj.lang.annotation.AfterThrowing;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
-import org.dromara.common.core.domain.model.LoginUser;
 import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
@@ -21,7 +20,6 @@ import org.dromara.common.json.utils.JsonUtils;
 import org.dromara.common.log.annotation.Log;
 import org.dromara.common.log.enums.BusinessStatus;
 import org.dromara.common.log.event.OperLogEvent;
-import org.dromara.common.satoken.utils.LoginHelper;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.http.HttpMethod;
 import org.springframework.validation.BindingResult;
@@ -88,16 +86,6 @@ public class LogAspect {
 
             // *========数据库日志=========*//
             OperLogEvent operLog = new OperLogEvent();
-            operLog.setTenantId(LoginHelper.getTenantId());
-            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
-            // 请求的地址
-            String ip = ServletUtils.getClientIP();
-            operLog.setOperIp(ip);
-            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
-            LoginUser loginUser = LoginHelper.getLoginUser();
-            operLog.setOperName(loginUser.getUsername());
-            operLog.setDeptName(loginUser.getDeptName());
-
             if (e != null) {
                 operLog.setStatus(BusinessStatus.FAIL.ordinal());
                 operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
@@ -106,8 +94,6 @@ public class LogAspect {
             String className = joinPoint.getTarget().getClass().getName();
             String methodName = joinPoint.getSignature().getName();
             operLog.setMethod(className + "." + methodName + "()");
-            // 设置请求方式
-            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
             // 处理设置注解上的参数
             getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
             // 设置消耗时间

+ 0 - 7
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java

@@ -2,8 +2,6 @@ package org.dromara.common.log.event;
 
 import lombok.Data;
 
-import jakarta.servlet.http.HttpServletRequest;
-
 import java.io.Serial;
 import java.io.Serializable;
 
@@ -39,11 +37,6 @@ public class LogininforEvent implements Serializable {
      */
     private String message;
 
-    /**
-     * 请求体
-     */
-    private HttpServletRequest request;
-
     /**
      * 其他参数
      */

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java

@@ -55,7 +55,7 @@ public class SysLogininforServiceImpl implements ISysLogininforService {
     @Async
     @EventListener
     public void recordLogininfor(LogininforEvent logininforEvent) {
-        HttpServletRequest request = logininforEvent.getRequest();
+        HttpServletRequest request = ServletUtils.getRequest();
         final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
         final String ip = ServletUtils.getClientIP(request);
         // 客户端信息

+ 20 - 3
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java

@@ -3,18 +3,23 @@ package org.dromara.system.service.impl;
 import cn.hutool.core.util.ArrayUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.model.LoginUser;
 import org.dromara.common.core.utils.MapstructUtils;
-import org.dromara.common.mybatis.core.page.PageQuery;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.core.utils.ip.AddressUtils;
+import org.dromara.common.log.enums.BusinessStatus;
 import org.dromara.common.log.event.OperLogEvent;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.system.domain.SysOperLog;
 import org.dromara.system.domain.bo.SysOperLogBo;
 import org.dromara.system.domain.vo.SysOperLogVo;
 import org.dromara.system.mapper.SysOperLogMapper;
 import org.dromara.system.service.ISysOperLogService;
-import lombok.RequiredArgsConstructor;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -44,6 +49,18 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
     @EventListener
     public void recordOper(OperLogEvent operLogEvent) {
         SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class);
+        operLog.setTenantId(LoginHelper.getTenantId());
+        operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
+        // 请求的地址
+        String ip = ServletUtils.getClientIP();
+        operLog.setOperIp(ip);
+        HttpServletRequest request = ServletUtils.getRequest();
+        operLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 255));
+        LoginUser loginUser = LoginHelper.getLoginUser();
+        operLog.setOperName(loginUser.getUsername());
+        operLog.setDeptName(loginUser.getDeptName());
+        // 设置请求方式
+        operLog.setRequestMethod(request.getMethod());
         // 远程查询操作地点
         operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
         insertOperlog(operLog);