学过 Spring 的小伙伴相信都知道 AOP,AOP 学的好的小伙伴相信对 AOP 的概念也是轻车熟路:面向切面编程、切点、切面、通知,Aspect、Pointcut、Advice 等如数家珍。
AOP 之所以这么重要,是因为它在项目中有着非常广泛的应用,今天这篇文章,松哥就来和大家总结一下,我们在日常开发中,都有哪些典型场景需要用到 AOP。
先来一句话总结下,AOP 的使用,基本上都会涉及到自定义注解,一个非常常见的组合,就是自定义注解+AOP。
在日常的开发中,有很多重复的代码,我们总是希望将之简化,AOP 就是一个非常常用的简化手段。简化的思路一般是这样:
- 首先,自定义一个注解。
- 定义 AOP 切面,在切面中,定义切点和通知,切点,也就是方法的拦截规则,我们可以按照注解来拦截,也就是某一个带有自定义注解的方法,将被我拦截下来。
- 拦截下来之后,前置通知、后置通知、异常通知、返回通知还是环绕通知,就可以随便写了。
所以,这些涉及到自定义注解的地方,基本上都可以算是 AOP 的使用场景了,因为自定义注解,需要用 AOP 来解析。
接下来我们来看几个比较典型的例子。
1. 幂等性处理
接口幂等性的处理,其实有很多种不同的方案,例如:
- Token 机制
- 去重表
- 利用 Redis 的 setnx
- 设置状态字段
- 上锁
无论是哪种方案处理幂等性,每个方法里边都去写一遍幂等性的处理显然是不现实的,因此,一般都是将幂等性的处理通过自定义注解+AOP给封装起来,大致的思路如下:
首先自定义一个注解。
自定义切点,拦截所有加了自定义注解的方法。
定义环绕通知,在环绕通知中,先通过上述五种思路中的任意一种,对方法执行的幂等性进行判断,判断通过了,再执行目标方法,判断不通过,则直接抛出异常,不执行目标方法。
这就是自定义注解+AOP 的一个典型应用场景。
2. 接口限流
对于接口限流,目前来说,一个比较成熟的方案是使用 Alibaba 的 Sentienl,简单配置一下就可以实现接口限流了。
但是如果没有用这个工具呢?如果是我们自己写呢?毫无疑问,还是自定义注解+AOP,思路大致如下:
- 自定义注解。
- 在需要进行限流的接口方法上添加自定义注解,同时还可以设置一些限流的参数,例如时间窗口值、流量大小等。
- 自定义切点,拦截规则就是所有添加了自定义注解的方法,拦截到方法之后,在环绕通知中,可以通过 Redis 插件 redis-cell、通过漏斗算法去处理限流,这个我这里就不罗嗦了,之前的文章中都写过了。限流计算没问题的话,就执行目标方法,否则将操作拦截下来。
大致思路如上,说白了就是自定义注解+ AOP,道理虽然简单,但是真正做起来,还是有很多细节。
3. 日志处理
说到 AOP,所有人都能想到的使用场景了,这个我就不罗嗦了,松哥之前也有过专门的文章介绍,没看过的小伙伴们戳这里:记录项目日志,一个注解搞定。
4. 多数据源处理
有时候我们项目中存在多个不同的数据源,在实际使用中需要进行切换,网上也有一些开源的解决方案,不过这个东西其实并不难,我们也可以自己写。
自定义多数据源的处理,大致上思路如下:
从 Spring2.0.1 中引入了 AbstractRoutingDataSource 类,(注意是 Spring2.0.1 不是 Spring Boot2.0.1,所以这其实也算是 Spring 一个非常古老的特性了), 该类充当了 DataSource 的路由中介,它能够在运行时, 根据某种 key 值来动态切换到真正的 DataSource 上。
大致的用法就是你提前准备好各种数据源,存入到一个 Map 中,Map 的 key 就是这个数据源的名字,Map 的 value 就是这个具体的数据源,然后再把这个 Map 配置到 AbstractRoutingDataSource 中,最后,每次执行数据库查询的时候,拿一个 key 出来,AbstractRoutingDataSource 会找到具体的数据源去执行这次数据库操作。
基于以上知识,我们可以自定义一个注解,在需要切换数据源的方法上,添加这个注解,然后通过 AOP 去解析这个自定义注解,当目标方法被拦截下来的时候,我们跟进注解中的配置,重新设置要执行的数据源,这样将来 service 中的方法在执行的过程中,就会使用到切换之后的数据源了。
5. 方法权限处理
这个其实也跟前面的差不多。
方法级别的权限处理,一般来说也是基于注解来完成的。如果你使用了 Spring Security 之类的权限框架,就不用自己解析权限注解了,按照框架的要求直接来使用就行了。
有的时候,我们可能没有使用 Spring Security,想自己处理权限注解,也是可以的。用户自定义权限注解,为注解添加属性,然后将注解添加到目标方法上,再通过 AOP 去解析这个注解,AOP 将目标方法的执行拦截下来,然后判断用户是否具备所需要的权限,如果具备,就执行目标方法,否则就不执行。
前两天松哥刚刚分享的在微服务中,服务内部的权限校验,就是自定义一个注解,将从其他微服务上来的请求给拦截下来,然后判断请求的来源,如果是从其他微服务上来的,就执行目标方法,如果不是从其他微服务上来的,而是从外部来的请求,那么就将之拦截下来抛出异常,不执行目标方法。
6. 事务处理
这个倒是不需要自定义注解,对于声明式事务,直接用现成的注解就行了,但是本质上也是 AOP,如果有小伙伴在 Spring 的 XML 中配置过事务的话,就知道这个东西底层也是 AOP。
好啦,梳理了几个简单的案例,希望小伙伴们了解到 AOP 并不是屠龙术,而是在日常开发中有着广泛应用的技术。