AOP技术限制后端接口调用次数

本文探讨了如何使用AOP技术在后端限制接口调用次数,同时介绍了与前端配合的解决方案,以及遇到的挑战和实践技巧。作者强调了与前端团队协作的重要性,特别是在处理前端显示和错误处理方面的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章目录

第一章 AOP后端控制接口调用次数
第二章 前后端配合控制接口调用次数


@[TOC](AOP技术限制后端接口调用次数)

背景

最近工作上在做安全管控的需求,需要限制一些接口的调用次数,一般可以采用两种方案,一种是将在后端使用AOP进行限制,另外一种是在后端开一个http接口,前端每次调用的时候,先调用安全管控的接口,接口返回成功的情况下,才进行实际业务操作。本文主要总结使用AOP限制次数踩的一些坑


一、整体方案

plan
整体方案如上图所示,使用AOP技术,在原有的基础上,增加规则判断。

二、AOP

1.反射解析属性

getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
同样类似的还有getConstructors()和getDeclaredConstructors()、getMethods()和getDeclaredMethods(),这两者分别表示获取某个类的方法、构造函数。

private Object parseFieldValue(JoinPoint jp, String filed){
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        Object[] args = jp.getArgs();
        String[] parameterNames = methodSignature.getParameterNames();
        Class[] parameterTypes = method.getParameterTypes();
        String[] filedNames = filed.split("\\.");
        for (int i = 0; i < parameterNames.length; i++){
            if (!parameterNames[i].equals(filedNames[0])){
                continue;
            }

            if (filedNames.length == 1){
                return args[i];
            }

            try{
                return parseFiled(parameterTypes[i], args[i], filedNames, 1);
            } catch (IllegalAccessException e1){
                e1.printStackTrace();
            }
        }

        throw new IllegalArgumentException("解析field失败");
    }

    private Object parseFiled(Class<?> parameterType, Object targetObject, String[] filedNames, int index) throws IllegalAccessException{
        try{
            Field field = parameterType.getDeclaredField(filedNames[index]);
            field.setAccessible(true);
            Object result = field.get(targetObject);
            if (filedNames.length == index){
                return result;
            } else {
                return parseFiled(result.getClass(), result, filedNames, ++index);
            }
        } catch (NoSuchFieldException e) {
            return parseFiled(parameterType.getSuperclass(), targetObject, filedNames, index);
        }
    }

2.SpEL解析属性

private <T> T parseSpel(ProceedingJoinPoint jp, String spel, Class<T> clazz, T defaultResult) {
        ExpressionParser parser = new SpelExpressionParser();
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = null;
        if (signature instanceof MethodSignature){
            methodSignature = (MethodSignature) signature;
        }
        if (methodSignature == null){
            return defaultResult;
        }

        Method method = methodSignature.getMethod();
        String[] params = methodSignature.getParameterNames();
        Object[] arguments = jp.getArgs();
        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < params.length; len++) {
            context.setVariable(params[len], arguments[len]);
        }
        try {
            Expression expression = parser.parseExpression(spel);
            return expression.getValue(context, clazz);
        } catch (Exception e) {
            return defaultResult;
        }
    }

3.下载接口

附件下载的时候,由于下载直接是浏览器控制,所以返回特定的错误,或者返回null,对于用户的提示效果都不太好。可以通过页面跳转到固定的错误页面,或者直接html的方式进行解决。

HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Content-Type", "text/html;charset=utf-8");
String content = "<!DOCTYPE html>\n" +
                "<html>\n" +
                "    <head>\n" +
                "        <meta charset=\"UTF-8\">\n" +
                "        <title>xxx失败</title>\n" +
                "    </head>\n" +
                "\n" +
                "    <body>\n" +
                "        <h2>xxx,请联系管理员进行配置!</h2>\n" +
                "    </body>\n" +
                "</html>";
byte[] data = content.getBytes("UTF-8");
ResponseEntity.status(HttpStatus.OK)
                .headers(headers)
                .contentLength(data.length)
                .body(data);

4.预览接口

项目上面预览使用的viewer.js来做的,直接url绑定到img上面,所以这个根本上就没有好的方法给用户进行提示,只能在前端进行改造。

总结

涉及前端到类似的方案评审的时候,需要拉上前端的同事,自己本来是前端小白,经验有限,临时改方案,增加自己的工作量,吃力不讨好。

### Java 后端开发者常见面试问题及解答 #### 1. Java 基础知识 Java 是一种广泛使用的面向对象编程语言,具有平台无关性和强大的库支持。对于多线程的支持是其一大特色之一[^1]。 ```java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world!"); } } ``` #### 2. 多线程与并发控制 在 Java实现多线程可以通过继承 `Thread` 类或者实现 `Runnable` 接口来完成。为了确保共享资源的安全访问,可以采用锁机制如 synchronized 关键字或 ReentrantLock 来管理同步代码块[^2]。 ```java synchronized (lockObject) { // critical section here } ``` #### 3. 数据结构与算法设计 掌握常见的数据结构(数组、链表、栈队列等)及其操作方法非常重要;另外还需要了解基本的排序查找算法以及复杂度分析能力。例如,在处理缓存时可能会涉及到 LRU 缓存淘汰策略的设计。 #### 4. Spring 框架的理解程度 Spring Framework 提供了一套完整的解决方案用于简化企业级应用程序开发过程中的诸多方面的工作量。其中包括依赖注入(Dependency Injection),AOP(Aspect Oriented Programming), MVC(Model View Controller)模式等方面的知识点。 #### 5. Redis 使用经验 Redis 不仅是一个高性能 key-value 存储系统,还可以作为消息中间件使用。熟悉如何利用 Redis 解决实际业务需求比如限流/防刷接口调用次数限制等问题是非常必要的。此外还有像 Redission 这样的工具可以帮助更好地集成到项目当中去[^3]。 #### 6. JVM 调优技巧 理解垃圾回收(Garbage Collection)的过程和参数配置能够帮助优化程序性能表现。同时也要知道类加载器(ClassLoader)的作用范围以及自定义 ClassLoader 的应用场景等等。 #### 7. 设计模式的应用实践 熟练运用 Singleton 单例模式保证全局只有一个实例存在;Factory 工厂方法模式创建不同类型的对象而无需暴露具体构造逻辑给客户端;Observer 观察者模式建立一对多的关系使得当一个对象状态改变时所有依赖它的其他对象都会得到通知并自动更新自己的状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值