自定义限流注解LimitAnno


1、pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.meihaocloud</groupId>
	<artifactId>SpringbootMongodb</artifactId>
	<version>0.0.1</version>
	<name>SpringbootMongodb</name>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<!-- springboot 整合 log4j -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j</artifactId>
			<version>1.3.8.RELEASE</version>
		</dependency>
		<!--end springboot 整合 log4j -->

		

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<!-- <version>3.6</version> -->
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
			<!-- 须应用commons-pool2 , 不然会报错 -->
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
			<version>2.5.0</version>
		</dependency>

	
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

application.properties

server.port=80

#spring.redis.cluster.nodes=192.168.8.115:8001,192.168.8.115:8002,192.168.8.115:8003,192.168.8.115:8004,192.168.8.115:8005,192.168.8.115:8006
#spring.redis.lettuce.pool.max-active=8
#spring.redis.lettuce.pool.max-idle=8
#spring.redis.lettuce.pool.min-idle=0
#spring.redis.lettuce.pool.max-wait=1000ms
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=5
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.timeout=6000ms

spring.redis.host=192.168.8.161
spring.redis.port=6379



2、相关代码

LimitAnno.java   注解类

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @description 限流注解
 * 
 * @Inherited:在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。这里一定要记住,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。
 * @Target:表示此注解可以放置的位置。常见的位置有:TYPE=枚举或注解上,FIELD=字段上,METHOD=方法上,PARAMETER=函数形参列表中,CONSTRUCTOR=构造函数上,LOCAL_VARIABLE=局部变量上                                                                                                        等等其他位置。
 * @Retention:此注解的生命周期。常见的有:SOURCE=源码时期;CLASS=字节码时期(已编译);RUNTIME=运行时期,通常是用这个的时候要多。 
 * @Documentd:生成注解文档。
 */
@Inherited
@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitAnno {
	
	int limit() default 5 ; // 限流数量
	
	int time() default 1 ; // 限流时间,单位秒
	
	Class<?> fallbackClass() default LimitAnno.class ; // 限流后降级的类,不指定就是该Controller
	
	String  fallbackMethod() default "" ; // 限流后降级的方法, 参数和返回值类型必须和原方法一致
	
}


注解切面:LimitAspect.java

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LimitAspect {

	private static Log log = LogFactory.getLog(LimitAspect.class);

	@Resource
	private RedisTemplate<String, Object> redisTemplate;

	@Pointcut("@annotation(com.meihaocloud.mongodb.redislock.LimitAnno)")
	public void limitAspect() {
	}

	@Around("limitAspect()")
	public Object limitAspectA(ProceedingJoinPoint pjp) throws Throwable {
		// 参数名
		Object[] args = pjp.getArgs();

		Signature signature = pjp.getSignature();
		if (!(signature instanceof MethodSignature)) {
			return pjp.proceed(args);
		}
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		LimitAnno limitAnno = method.getAnnotation(LimitAnno.class);
		if (limitAnno == null) {
			return pjp.proceed(args);
		}

		int limitCount = limitAnno.limit();
		int limitTime = limitAnno.time();

		Object bean = pjp.getTarget();
		// 类名
		String className = bean.getClass().getName();
		// 方法名
		String methodName = signature.getName();
		// 根据类名、方法名、参数 生成key

		String key = "limit_" + className + "_" + methodName;

		Object result = null;

		ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();

		Integer count = null;
		
		synchronized (log) { // 保证原子操作
			
			count = (Integer) opsForValue.get(key);
			log.info("count : " + count );
			
			if (count == null || count == 0 || count < 0) {
				opsForValue.set(key, 1, limitTime, TimeUnit.SECONDS);
				// 执行方法主体,获取结果值
				result = pjp.proceed(args);
			} else if (count < limitCount) {
				opsForValue.set(key, count + 1, limitTime, TimeUnit.SECONDS);

				// 执行方法主体,获取结果值
				result = pjp.proceed(args);

			} else {
				// 大于limitCount , 执行降级方法

				Class<?> backClass = limitAnno.fallbackClass();
				Object newInstance = backClass.newInstance();
				if (!(newInstance instanceof LimitAnno)) { // 没有配置Class,默认就是该类
					bean = newInstance;
				}

				String backMethod = limitAnno.fallbackMethod();
				if (StringUtils.isNotBlank(backMethod)) { // 没有配置方法,就直接返回null
					result = MethodUtils.invokeMethod(bean, backMethod, args);
				}
//				log.info(result + " ,拒绝访问: thread: " + Thread.currentThread().getId());
			}
		}

		return result;
	}

}





3、controller

import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisController {
	private static Log log = LogFactory.getLog(LimitAspect.class);
	
	
	@RequestMapping("/test")
	@LimitAnno(limit = 10, fallbackMethod = "redisTestBack", fallbackClass = LimitCallback.class)
	public String redisTest(HttpServletRequest request) {

		String id = "1001";
                // 业务处理 

		return "ok";
	}

	public String redisTestBack(HttpServletRequest request) {
//		System.out.println("===================限流");
		return "limit-error";
	}


}


LimitCallback.java

import javax.servlet.http.HttpServletRequest;

public class LimitCallback {

	public String redisTestBack(HttpServletRequest request) {
//		System.out.println("===================限流");
		return "limit-error";
	}
}




java 注解

2020.11.18 00:24

https://www.meihaocloud.com.com/308.html , 欢迎转载,请在文章页标出原文连接 !


Copyright © 2020 千夕网 联系站长

粤公网安备 44030302001408号 粤ICP备19099833号-1