网站系统改教程,网站设计与管理,做网站怎么上传图片,网站备案模板承接上文#xff08;传送门 —《面试必考》 — AOP-CSDN博客#xff09;#xff0c;在被面试官拷打的时候#xff0c;会被问到一个致命问题#xff1a;“你了解aop吗#xff1f;有具体的使用经验吗#xff1f;”
你#xff1a;.........
言尽于此#xff0c;此篇…承接上文传送门 —《面试必考》 — AOP-CSDN博客在被面试官拷打的时候会被问到一个致命问题“你了解aop吗有具体的使用经验吗”
你.........
言尽于此此篇文章必能大补
自定义注解①连续点击注解
package com.ruoyi.profile.lock;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface RequestLock {String key() default ; // 主键前缀long duration() default 2000; // 默认防抖时间单位毫秒boolean useIp() default true; // 是否使用IP作为键的一部分boolean useUserId() default true; // 是否使用用户ID作为键的一部分
}package com.ruoyi.profile.lock;import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;import static com.ruoyi.common.constant.HttpStatus.WARN;
import static com.ruoyi.common.utils.SecurityUtils.getLoginUser;Aspect
Component
Order(2)
public class RequestLockAspect {Resourceprivate StringRedisTemplate stringRedisTemplate;Around(execution(public * * (..)) annotation(requestLock))public Object handleDebounce(ProceedingJoinPoint joinPoint, RequestLock requestLock) throws Throwable {String key generateKey(joinPoint, requestLock);Object proceed joinPoint.proceed();if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {stringRedisTemplate.opsForValue().set(key, true, requestLock.duration(), TimeUnit.MILLISECONDS);return proceed;} else {if (proceed instanceof TableDataInfo) {TableDataInfo rspData new TableDataInfo();rspData.setCode(WARN);rspData.setMsg(请求太频繁请稍后再试!!!);return rspData;} else if (proceed instanceof AjaxResult) {// 返回一个默认值或抛出异常根据业务需求决定return AjaxResult.error(请求太频繁请稍后再试!!!);} else {throw new RuntimeException(未知返回错误);}}}private String generateKey(ProceedingJoinPoint joinPoint, RequestLock requestLock) {HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();StringBuilder sb new StringBuilder();Object[] args joinPoint.getArgs();for (Object arg : args) {sb.append(:).append(arg.toString());}// 根据注解参数构建键sb.append(requestLock.key());if (requestLock.useIp()) {sb.append(:).append(request.getRemoteAddr()); // 获取客户端IP}if (requestLock.useUserId()) {// 假设用户ID可以从某个地方获取比如从session或token中Long userId getLoginUser().getUserId();sb.append(:).append(userId);}return sb.toString();}}自定义注解②限流注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.enums.LimitType;/*** 限流注解* * author */
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
Documented
public interface RateLimiter
{/*** 限流key*/public String key() default CacheConstants.RATE_LIMIT_KEY;/*** 限流时间,单位秒*/public int time() default 60;/*** 限流次数*/public int count() default 100;/*** 限流类型*/public LimitType limitType() default LimitType.DEFAULT;
}import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;/*** 限流处理** author */
Aspect
Component
public class RateLimiterAspect
{private static final Logger log LoggerFactory.getLogger(RateLimiterAspect.class);private RedisTemplateObject, Object redisTemplate;private RedisScriptLong limitScript;Autowiredpublic void setRedisTemplate1(RedisTemplateObject, Object redisTemplate){this.redisTemplate redisTemplate;}Autowiredpublic void setLimitScript(RedisScriptLong limitScript){this.limitScript limitScript;}Before(annotation(rateLimiter))public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable{int time rateLimiter.time();int count rateLimiter.count();String combineKey getCombineKey(rateLimiter, point);ListObject keys Collections.singletonList(combineKey);try{Long number redisTemplate.execute(limitScript, keys, count, time);if (StringUtils.isNull(number) || number.intValue() count){throw new ServiceException(访问过于频繁请稍候再试);}log.info(限制请求{},当前请求{},缓存key{}, count, number.intValue(), combineKey);}catch (ServiceException e){throw e;}catch (Exception e){throw new RuntimeException(服务器限流异常请稍候再试);}}public String getCombineKey(RateLimiter rateLimiter, JoinPoint point){StringBuffer stringBuffer new StringBuffer(rateLimiter.key());if (rateLimiter.limitType() LimitType.IP){stringBuffer.append(IpUtils.getIpAddr()).append(-);}MethodSignature signature (MethodSignature) point.getSignature();Method method signature.getMethod();Class? targetClass method.getDeclaringClass();stringBuffer.append(targetClass.getName()).append(-).append(method.getName());return stringBuffer.toString();}
}限流方式其实有很多种比如说Nginx漏桶算法、Gateway令牌桶算法、Sentinel、CircuitBreaker、自定义注解限流、自定义过滤器、自定义拦截器等等。
Gateway参考链接Spring Cloud Gateway 限流详解-腾讯云开发者社区-腾讯云 (tencent.com)
如果不明白最后三个的区别传送门 —《面试必考》 — AOP-CSDN博客 自定义注解③日志注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.OperatorType;/*** 自定义操作日志记录注解* * author **/
Target({ ElementType.PARAMETER, ElementType.METHOD })
Retention(RetentionPolicy.RUNTIME)
Documented
public interface Log
{/*** 模块*/public String title() default ;/*** 功能*/public BusinessType businessType() default BusinessType.OTHER;/*** 操作人类别*/public OperatorType operatorType() default OperatorType.MANAGE;/*** 是否保存请求的参数*/public boolean isSaveRequestData() default true;/*** 是否保存响应的参数*/public boolean isSaveResponseData() default true;/*** 排除指定的请求参数*/public String[] excludeParamNames() default {};
}package com.ruoyi.framework.aspectj;import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.filter.PropertyPreExcludeFilter;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.domain.SysOperLog;/*** 操作日志记录处理* * author */
Aspect
Component
public class LogAspect
{private static final Logger log LoggerFactory.getLogger(LogAspect.class);/** 排除敏感属性字段 */public static final String[] EXCLUDE_PROPERTIES { password, oldPassword, newPassword, confirmPassword };/** 计算操作消耗时间 */private static final ThreadLocalLong TIME_THREADLOCAL new NamedThreadLocalLong(Cost Time);/*** 处理请求前执行*/Before(value annotation(controllerLog))public void boBefore(JoinPoint joinPoint, Log controllerLog){TIME_THREADLOCAL.set(System.currentTimeMillis());}/*** 处理完请求后执行** param joinPoint 切点*/AfterReturning(pointcut annotation(controllerLog), returning jsonResult)public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult){handleLog(joinPoint, controllerLog, null, jsonResult);}/*** 拦截异常操作* * param joinPoint 切点* param e 异常*/AfterThrowing(value annotation(controllerLog), throwing e)public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e){handleLog(joinPoint, controllerLog, e, null);}protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult){try{// 获取当前的用户LoginUser loginUser SecurityUtils.getLoginUser();// *数据库日志*//SysOperLog operLog new SysOperLog();operLog.setStatus(BusinessStatus.SUCCESS.ordinal());// 请求的地址String ip IpUtils.getIpAddr();operLog.setOperIp(ip);operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));if (loginUser ! null){operLog.setOperName(loginUser.getUsername());SysUser currentUser loginUser.getUser();if (StringUtils.isNotNull(currentUser) StringUtils.isNotNull(currentUser.getDept())){operLog.setDeptName(currentUser.getDept().getDeptName());}}if (e ! null){operLog.setStatus(BusinessStatus.FAIL.ordinal());operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));}// 设置方法名称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);// 设置消耗时间operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());// 保存数据库AsyncManager.me().execute(AsyncFactory.recordOper(operLog));}catch (Exception exp){// 记录本地异常日志log.error(异常信息:{}, exp.getMessage());exp.printStackTrace();}finally{TIME_THREADLOCAL.remove();}}/*** 获取注解中对方法的描述信息 用于Controller层注解* * param log 日志* param operLog 操作日志* throws Exception*/public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception{// 设置action动作operLog.setBusinessType(log.businessType().ordinal());// 设置标题operLog.setTitle(log.title());// 设置操作人类别operLog.setOperatorType(log.operatorType().ordinal());// 是否需要保存request参数和值if (log.isSaveRequestData()){// 获取参数的信息传入到数据库中。setRequestValue(joinPoint, operLog, log.excludeParamNames());}// 是否需要保存response参数和值if (log.isSaveResponseData() StringUtils.isNotNull(jsonResult)){operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));}}/*** 获取请求的参数放到log中* * param operLog 操作日志* throws Exception 异常*/private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception{Map?, ? paramsMap ServletUtils.getParamMap(ServletUtils.getRequest());String requestMethod operLog.getRequestMethod();if (StringUtils.isEmpty(paramsMap) (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))){String params argsArrayToString(joinPoint.getArgs(), excludeParamNames);operLog.setOperParam(StringUtils.substring(params, 0, 2000));}else{operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));}}/*** 参数拼装*/private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames){String params ;if (paramsArray ! null paramsArray.length 0){for (Object o : paramsArray){if (StringUtils.isNotNull(o) !isFilterObject(o)){try{String jsonObj JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));params jsonObj.toString() ;}catch (Exception e){}}}}return params.trim();}/*** 忽略敏感属性*/public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames){return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));}/*** 判断是否需要过滤的对象。* * param o 对象信息。* return 如果是需要过滤的对象则返回true否则返回false。*/SuppressWarnings(rawtypes)public boolean isFilterObject(final Object o){Class? clazz o.getClass();if (clazz.isArray()){return clazz.getComponentType().isAssignableFrom(MultipartFile.class);}else if (Collection.class.isAssignableFrom(clazz)){Collection collection (Collection) o;for (Object value : collection){return value instanceof MultipartFile;}}else if (Map.class.isAssignableFrom(clazz)){Map map (Map) o;for (Object value : map.entrySet()){Map.Entry entry (Map.Entry) value;return entry.getValue() instanceof MultipartFile;}}return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse|| o instanceof BindingResult;}
}言尽于此记得按时吃饭。社会的边角料老妈的小骄傲你我都有光明的未来