SpringBoot 之AOP实现过滤器、拦截器、切面

文章目录

  • AOP概述
  • AOP底层实现机制
  • 过滤器、拦截器、切面区别
  • 过滤器 Filter
    • 使用过滤器统一请求耗时
  • 拦截器 Interceptor
    • 使用拦截器判断是否登录
  • AspectJ 简介
  • @AspectJ 注解开发 AOP
    • AOP 注解说明
    • 案例代码
  • JoinPoint 对象
  • ProceedingJoinPoint 获取方法上的注解
  • 使用AOP打印Http请求入参、返回值、接口耗时
  • 多个切面的执行顺序
  • 自己实现一个AOP
  • 使用AOP解决接口防刷

AOP概述

AOP Aspect Oriented Programing 面向切面编程。
AOP 是一种编程思想,不是一项技术。
AOP 使用横向抽取机制实现代码复用,继承方式是纵向的重复性代码。
AOP 思想: 基于代理,对原来目标对象,创建代理对象,在代理对象中,对原有业务方法进行增强。
AOP 实现方式: 过滤器、拦截器、切面。
AOP实际企业应用 : 打印方法运行时间、统一打印入参和出参、事务管理、日志记录、 权限控制、 缓存。

AOP在项目中的应用: 判断用户是否登录、统一打印入参和出参、记录请求运行时间。

AOP底层实现机制

AOP 需要对目标进行代理对象创建, Spring AOP采用哪种技术对目标进行代理对象的创建: JDK动态代理、CGLIB动态代理

JDK 动态代理: 针对接口生成代理。
CGLIB 动态代理 直接对目标对象(可以没有接口)生成代理对象,原理: 对目标类创建子类对象。

Spring AOP的代理小结:

  • 如果目标对象有接口,Spring优先使用 JDK动态代理。
  • 如果目标对象没有接口,Spring 采用Cglib 动态代理。
  • 被代理增强的方法,不能为final修饰。

过滤器、拦截器、切面区别

过滤器 拦截器 Aspect
关注的点 所有web请求 部分web请求 偏向于业务层面的拦截
实现原理 函数回调 JAVA反射机制(动态代理) 动态代理
Servlet提供的支持 Filter接口
Spring提供的支持 HandlerInterceptorAdapter类、HandlerInterceptor接口 @Aspect
可能需要实现的方法 void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) Object around(ProceedingJoinPoint joinPoint)

过滤器 Filter

简单来讲就是用来过滤东西的,比如:统一设置编码,统一设置所有请求头信息。

实现: 实现 javax.Servlet.Filter 接口就可以创建一个过滤器。

使用过滤器统一请求耗时

/**
 * 统一记录请求耗时
 */
@Slf4j
@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/*")
public class ApiAccessFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        long start = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        long duration = System.currentTimeMillis() - start;
        if (duration > 200) {
            log.info("Api Access uri: {}, method: {}, client: {}, duration: {}ms , time is too long",
                    request.getRequestURI(), request.getMethod(), getIP(request), System.currentTimeMillis() - start);
        }else{
            log.info("Api Access uri: {}, method: {}, client: {}, duration: {}ms",
                    request.getRequestURI(), request.getMethod(), getIP(request), System.currentTimeMillis() - start);
        }

    }

    private String getIP(HttpServletRequest request) {
        if (request == null) {
            return "0.0.0.0";
        }
        String Xip = request.getHeader("X-Real-IP");
        String XFor = request.getHeader("X-Forwarded-For");
        String UNKNOWN_IP = "unknown";
        if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = XFor.indexOf(",");
            if (index != -1) {
                return XFor.substring(0, index);
            } else {
                return XFor;
            }
        }
        XFor = Xip;
        if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            return XFor;
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getRemoteAddr();
        }
        return XFor;
    }
}

拦截器 Interceptor

在执行方法之前,进行拦截,然后在之前或之后加入一些操作。

使用场景:

  • 日志记录: 记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
  • 权限检查: 如登录检测,进入处理器检测检测是否登录。
  • 性能监控: 通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。
  • 通用行为: 读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,只要是多个处理器都需要的即可使用拦截器实现。

实现拦截器: 实现 HandlerInterceptor 接口就可以创建一个拦截器。

HandlerInterceptor 接口的源码如下:

public interface HandlerInterceptor {
  	// 在请求处理之前进行调用(Controller方法调用之前)
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
  	// 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
  	// 在整个请求结束之后被调用,也就是在 DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}
}

使用拦截器判断是否登录

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotCheckToken {
}

@Component
public class AuthenticationIntercept extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            NotCheckToken methodAnnotation = method.getMethodAnnotation(NotCheckToken.class);
            if (Objects.nonNull(methodAnnotation)) {
                return true;
            }
        }
        String userId = request.getHeader("userId");
        if (StringUtils.isBlank(userId)) {
            throw new Exception("未登录");
        }
        request.setAttribute("userId", userId);
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
       
    }
}

拦截器注册:

@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
    @Autowired
    private AuthenticationIntercept authenticationIntercept;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration registration = registry.addInterceptor(authenticationIntercept);
        // 拦截所有请求
        registration.addPathPatterns("/**");
        // 添加不拦截路径
        registration.excludePathPatterns("/login", "/error", "/logout","/login.html");
    }
}

AspectJ 简介

AspectJ 是第三方 AOP 框架, 现阶段企业 AOP 开发,使用 AspectJ

AspectJ 提供的几种Advice类型 :

  • before : 在目标方法运行前增强, 前置通知。应用场景: 日志记录、权限控制。
  • afterReturing : 在目标方法返回值获取后进行增强,后置通知。应用场景:和业务相关, 网上营业厅查询余额后,自动下发短信。
  • around : 在目标方法运行前后进行增强,环绕通知。应用场景:日志、缓存、权限、性能监控、 事务管理。
  • afterThrowing: 在目标方法抛出异常后进行增强,抛出通知。应用场景: 处理异常 ,记录日志。
  • After (新增): 不管是否发生异常,该通知都执行,类似 finally。应用场景 : 关闭和释放一些资源

@AspectJ 注解开发 AOP

AOP 注解说明

  • @Before 前置通知 (方法前)
  • @AfterReturning 后置通知 (方法返回后)
  • @Around 环绕通知 (方法前后)
  • @AfterThrowing 抛出通知 (异常发生后)
  • @After 最终通知 (一定在方法后执行)

@Aspect: 标识为切面类,被容器识别。

@Pointcut: 配置切入点:

  • 拦截 com.demo.controller 包下的所有类型所有方法:@Pointcut(value = "execution(* com.demo.controller.*.*(..))")
  • 拦截 com.demo.controller 包和子包下的所有类型所有方法:@Pointcut(value = "execution(* com.demo.controller..*.*(..))")
  • 拦截 com.demo.controller 包下 DemoController 类型下的所有方法:@Pointcut(value = "execution(* com.demo.controller.DemoController.*.*(..))")
  • 方法上标注了Inter注解就会拦截:@Pointcut(value = "@annotation(com.demo.Inter)")
  • 类上标注了Inter注解就会拦截:@Pointcut(value = "@within(com.demo.Inter)")
  • 且的关系,类和方法上同时标注这个注解才会拦截:@Pointcut(value = "@within(com.demo.Inter) && @annotation(com.demo.Inter)")
  • 或的关系,类或者方法上有标注这个注解就会拦截,类上标注拦截所有方法,方法上标注类上没有只拦截标注的方法:@Pointcut(value = "@within(com.demo.Inter) || @annotation(com.demo.Inter)")

案例代码

1、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)。
2、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)。
3、开启基于注解的aop模式;@EnableAspectJAutoProxy

1、导入依赖

<dependency>
   <groupId>org.springframework.bootgroupId>
   <artifactId>spring-boot-starter-aopartifactId>
dependency>

2、编写切面类

@Aspect
@Component
@Slf4j
public class WebLogAspect {

    //切入点描述 这个是controller包的切入点
    @Pointcut("execution(public * com.example.demo.controller..*.*(..))")
    //签名,可以理解成这个切入点的一个名称
    public void controllerLog(){}

    @Pointcut("execution(public * com.example.demo.service..*.*(..))")
    public void serviceLog(){}

    //在切入点的方法run之前要干的
    @Before("controllerLog()||serviceLog()")
    public void logBeforeController(JoinPoint joinPoint) {
        //这个RequestContextHolder是SpringMvc提供来获得请求的东西
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        // 记录下请求内容
        log.info("################URL : " + request.getRequestURL().toString());
    }

    //在切入点的方法run之后要干的
    @After("controllerLog()||serviceLog()")
    public void logAfterController(JoinPoint joinPoint) {
        System.out.println("after");
    }
}

切面类示例

@Aspect  
@Component  
public class LogAspect {  
    @Pointcut("execution(public * com.example.controller.*.*(..))")  
    public void webLog(){}  
  
    @Before("webLog()")  
    public void deBefore(JoinPoint joinPoint) throws Throwable {  
        // 接收到请求,记录请求内容  
        ServletRequestAttributes attributes = 
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();  
        HttpServletRequest request = attributes.getRequest();  
        // 记录下请求内容  
        System.out.println("URL : " + request.getRequestURL().toString());  
        System.out.println("HTTP_METHOD : " + request.getMethod());  
        System.out.println("IP : " + request.getRemoteAddr());  
        System.out.println("CLASS_METHOD : " +
         joinPoint.getSignature().getDeclaringTypeName() + 
        "." + joinPoint.getSignature().getName());  
        System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));  
  
    }  
  
    @AfterReturning(returning = "ret", pointcut = "webLog()")  
    public void doAfterReturning(Object ret) throws Throwable {  
        // 处理完请求,返回内容  
        System.out.println("方法的返回值 : " + ret);  
    }  
  
    //后置异常通知  
    @AfterThrowing("webLog()")  
    public void throwss(JoinPoint jp){  
        System.out.println("方法异常时执行.....");  
    }  
  
    //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行  
    @After("webLog()")  
    public void after(JoinPoint jp){  
        System.out.println("方法最后执行.....");  
    }  
  
    //环绕通知,环绕增强,相当于MethodInterceptor  
    @Around("webLog()")  
    public Object arround(ProceedingJoinPoint pjp) {  
        System.out.println("方法环绕start.....");  
        try {  
            Object o =  pjp.proceed();  
            System.out.println("方法环绕proceed,结果是 :" + o);  
            return o;  
        } catch (Throwable e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  

JoinPoint 对象

JoinPoint 对象封装了SpringAop中切面方法的信息,在切面方法中添加 JoinPoint 参数,就可以获取到封装了该方法信息的JoinPoint对象。
常用API:

方法名 功能
Signature getSignature() 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs() 获取传入目标方法的参数对象
Object getTarget() 获取被代理的对象
Object getThis() 获取代理对象
System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
System.out.println("目标方法所属类的简单类名:" +        joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
//获取传入目标方法的参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
    System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + joinPoint.getTarget());
System.out.println("代理对象自己:" + joinPoint.getThis());

ProceedingJoinPoint 获取方法上的注解

Class<?> classTarget = joinPoint.getTarget().getClass();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method objMethod = classTarget.getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
GpDbSetAppAnnotation annotation = objMethod.getAnnotation(GpDbSetAppAnnotation.class);

使用AOP打印Http请求入参、返回值、接口耗时

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class WebAspect {

    @Pointcut("execution(public * com.example.controller.*.*(..))")
    public void pointCut() {
    }

    @AfterReturning(value = "pointCut()", returning = "returnVal")
    public void afterReturning(JoinPoint joinPoint, Object returnVal) {
        log.info("{} after return, returnVal: {}", joinPoint.getSignature().getName(), returnVal);
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getName();
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Map<String, Object> paramMap = new HashMap<>();
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        log.info("path:{} {}.{} start, param:{}", request.getServletPath(), className, methodName, paramMap.toString());
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            log.error("around error", e);
        }
        long endTime = System.currentTimeMillis();
        log.info("{}.{} end execute time:{} ms", className, methodName, endTime - startTime);
        return result;
    }
}

多个切面的执行顺序

自己实现一个AOP

业务类。

public interface HelloService {
	public void sayHello(String name);
}
//
import com.demo.service.HelloService;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService {
	@Override
	public void sayHello(String name) {
		if (name == null || name.trim() == "") {
			throw new RuntimeException ("parameter is null!!");
		}
		System.out.println("hello " + name);
	}
}

定义一个以反射的形式去调用原有的方法工具。

import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Getter
@Setter
public class Invocation {
	private Object[] params;
	private Method method;
	private Object target;
	public Invocation(Object target, Method method, Object[] params) {
		this.target = target;
		this.method = method;
		this.params = params;
	}
	public Object proceed() throws InvocationTargetException, IllegalAccessException {
		return method.invoke(target, params);
	}
}

定义一个自己的拦截器。

import com.demo.invoke.Invocation;
import java.lang.reflect.InvocationTargetException;
public interface Interceptor {
	//事前方法
	public boolean before();
	//事后方法
	public void after();
	/**
	 * 取代原有事件方法
	 * @param invocation -- 回调参数,可以通过它的proceed方法,回调原有事件
	 * @return 原有事件返回对象
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 */
	public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;	
	//是否返回方法。事件没有发生异常执行
	public void afterReturning();	
	//事后异常方法,当事件发生异常后执行
	public void afterThrowing();
	//是否使用around方法取代原有方法
	boolean useAround();	
}
///
import com.demo.invoke.Invocation;
import java.lang.reflect.InvocationTargetException;
public class MyInterceptor implements Interceptor {
	@Override
	public boolean before() {
		System.out.println("before ......");
		return true;
	}
	@Override
	public boolean useAround() {
		return true;
	}
	@Override
	public void after() {
		System.out.println("after ......");
	}
	@Override
	public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
		System.out.println("around before ......");
		Object obj = invocation.proceed();
		System.out.println("around after ......");
		return obj;
	}
	@Override
	public void afterReturning() {
		System.out.println("afterReturning......");

	}
	@Override
	public void afterThrowing() {
		System.out.println("afterThrowing 。。。。。。");
	}
}

编写生成代理业务类。

import com.demo.intercept.Interceptor;
import com.demo.invoke.Invocation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyBean implements InvocationHandler {
	private Object target = null;
	private Interceptor interceptor = null;
	/**
	 * 绑定代理对象
	 * @param target 被代理对象
	 * @param interceptor 拦截器
	 * @return 代理对象
	 */
	public static Object getProxyBean(Object target, Interceptor interceptor) {
		ProxyBean proxyBean = new ProxyBean();
		// 保存被代理对象
		proxyBean.target = target;
		// 保存拦截器
		proxyBean.interceptor = interceptor;
		// 生成代理对象
		Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				proxyBean);
		// 返回代理对象
		return proxy;
	}
	/**
	 * 处理代理对象方法逻辑
	 * @param proxy 代理对象
	 * @param method 当前方法
	 * @param args  运行参数
	 * @return 方法调用结果
	 * @throws Throwable 异常
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)  {
		//异常标识
		boolean exceptionFlag = false;
		Invocation invocation = new Invocation(target, method, args);
		Object retObj = null; 
		try {
			if (this.interceptor.before()) {
				retObj = this.interceptor.around(invocation);
			} else {
				retObj = method.invoke(target, args);
			}
		} catch (Exception ex) {
			//产生异常
			exceptionFlag = true;
		}
		this.interceptor.after();
		if (exceptionFlag) {
			this.interceptor.afterThrowing();
		} else {
			this.interceptor.afterReturning();
			return retObj;
		}
		return null;
	}
}

测试类。

import com.demo.intercept.MyInterceptor;
import com.demo.proxy.ProxyBean;
import com.demo.service.HelloService;
import com.demo.service.impl.HelloServiceImpl;

public class AopMain {
	public static void main(String[] args) {
		HelloService helloService = new HelloServiceImpl();
		HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
		proxy.sayHello("Tom");
	}
}

测试结果。

before ......
around before ......
hello Tom
around after ......
after ......
afterReturning......

使用AOP解决接口防刷

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Prevent {
    int seconds() default 60;
    int maxCount() default 1;
    String message() default "";
}
import cn.hutool.json.JSONUtil;
import com.boot.common.BusinessException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

/**
 * 防刷切面实现类
 */
@Aspect
@Component
public class PreventAop {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Pointcut("@annotation(com.boot.aop.Prevent)")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void joinPoint(JoinPoint joinPoint) throws Exception {
        String requestStr = JSONUtil.toJsonStr(joinPoint.getArgs()[0]);
        if (StringUtils.isEmpty(requestStr) || requestStr.equalsIgnoreCase("{}")) {
            throw new BusinessException("[防刷]入参不允许为空");
        }

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(),
                methodSignature.getParameterTypes());

        Prevent preventAnnotation = method.getAnnotation(Prevent.class);
        String methodFullName = method.getDeclaringClass().getName() + method.getName();

        defaultHandle(requestStr, preventAnnotation, methodFullName);
    }

    private void defaultHandle(String requestStr, Prevent prevent, String methodFullName) throws Exception {
        String base64Str = toBase64String(requestStr);
        int expire = prevent.seconds();
        int maxCount = prevent.maxCount();
        String resp = redisTemplate.opsForValue().get(methodFullName + base64Str);
        int count = StringUtils.isEmpty(resp) ? 0 : Integer.parseInt(resp);
        if (StringUtils.isEmpty(resp)) {
            redisTemplate.opsForValue().set(methodFullName + base64Str, "1", expire, TimeUnit.SECONDS);
        } else if (count < maxCount) {
            redisTemplate.opsForValue().increment(methodFullName + base64Str);
        } else {
            String message = !StringUtils.isEmpty(prevent.message()) ? prevent.message() :
                    expire + "超出访问次数";
            throw new BusinessException(message);
        }
    }

    /**
     * 对象转换为base64字符串
     *
     * @param obj 对象值
     * @return base64字符串
     */
    private String toBase64String(String obj) throws Exception {
        if (StringUtils.isEmpty(obj)) {
            return null;
        }
        Base64.Encoder encoder = Base64.getEncoder();
        byte[] bytes = obj.getBytes(StandardCharsets.UTF_8);
        return encoder.encodeToString(bytes);
    }

}

你可能感兴趣的:(#,SpringBoot,rabbitmq,java,分布式)