自定义AOP切面类接口限流

说明:

spring的两个主要特性就是IOC和AOP

IOC: 控制反转,简单来说就是自己不创建对象,把对象的创建和管理交给spring容器,我们直接使用,Spring 的核心就是一个大的工厂容器

AOP: Spring 提供了面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等切面功能。

那么实现AOP一般都是通过 @Aspect 注解去自定义切面类

● 切面(Aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
● 连接点(Joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器,根据连接点可以获取请求的参数等信息
● 切点(Pointcut):对连接点进行拦截的定位
● 通知(Advice):所谓通知指的就是指拦截到连接点之后要执行的代码,也可以称作增强
● 目标对象 (Target):代理的目标对象

AOP 一般有 5 种环绕方式:
● 前置通知 (@Before)
● 返回通知 (@AfterReturning)
● 异常通知 (@AfterThrowing)
● 后置通知 (@After)
● 环绕通知 (@Around)

实操

1、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--Redis依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、自定义接口访问注解

/**
* 接口访问限制枚举
* @author ppp
* @date 2023/2/14
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
    /**
     * @return 访问最大次数
     */
    int accessMaxTimes() default 60;

    /**
     * @return 时间
     */
    long timeOut() default 1;

    /**
     * @return 时间单位
     */
    TimeUnit timeUnit() default TimeUnit.MINUTES;
}

3、自定义切面类

/**
* 接口访问限制Aop配置
* @author ppp
* @date 2023/2/14
*/
@Aspect
@Component
public class AccessLimitAopConfig {

    private final static String KEY = "Redis_accessLimit_key";

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 定义切点为注解
     */
    @Pointcut("@annotation(com.nw.test.configure.annotation.AccessLimit)")
    public void AccessLimitPointcut(){
    }

    @Before(value = "AccessLimitPointcut()")
    public void handleAccessLimit(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AccessLimit annotation = AnnotationUtils.findAnnotation(method, AccessLimit.class);
        int accessMaxTimes = annotation.accessMaxTimes();
        long timeOut = annotation.timeOut();
        TimeUnit timeUnit = annotation.timeUnit();

        // 如果redis不存在或已经过期
        Long expire =redisTemplate.opsForValue().getOperations().getExpire(KEY);
        if (!redisTemplate.hasKey(KEY) ||  Objects.requireNonNull(expire).intValue() < 0) {
            redisTemplate.opsForValue().set(KEY, 1, timeOut, timeUnit);
        } else {
            long increment = redisTemplate.opsForValue().increment(KEY).intValue();
            if (increment > accessMaxTimes) {
               throw new RuntimeException("访问已经超过最大值: " + accessMaxTimes);
            }
        }
    }
    
}

4、配置redis

/**
 * Redis管理配置
 */
@Configuration
public class RedisConfigurer {

    /**
     * 设置 redisTemplate 的序列化设置
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
        template.setValueSerializer(genericToStringSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer(Charset.forName("UTF-8")));
        template.setHashValueSerializer(new StringRedisSerializer(Charset.forName("UTF-8")));
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(24 * 60 * 60), // 默认策略,未配置的 key 会使用这个
                this.getRedisCacheConfigurationMap() // 指定 key 策略
        );
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(seconds));
        return redisCacheConfiguration;
    }
}

5、使用

/**
 * 一分钟内最多请求10次
 * @param content
 */
@AccessLimit(accessMaxTimes = 10, timeOut = 1, timeUnit = TimeUnit.MINUTES)
@GetMapping("/sendEvent")
public void sendEvent(@RequestParam String content) {
    testService.send(content);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值