环境:SpringBoot2.7.12
1. 概述本文将介绍如何为API接口动态添加开关功能 。通过这个功能,我们可以控制API接口的正常访问或显示提示信息 。这有助于在开发和维护过程中更好地管理和控制API接口的行为 。通过使用开关功能 , 我们可以轻松地在不同情况下调整接口的行为 , 提高系统的灵活性和可维护性 。
为什么要给API接口添加开关功能呢?从以下几方面考虑:
- 灵活性和可扩展性:我们可以根据需要动态地控制API接口的行为 。这使得在面对不同场景和需求时,可以更加灵活地调整接口的行为,而无需对代码进行修改 。
- 安全性和控制:有时,我们可能不希望在特定情况下API接口被正常访问,例如在测试、维护或敏感数据访问等场景下 。通过关闭开关并显示提示信息,我们可以对用户进行必要的通知和引导,确保接口不被未经授权的访问 。
- 错误处理和日志记录:当API接口出现错误或异常时,关闭开关并显示提示信息可以帮助我们更好地追踪和记录问题 。这对于后续的问题排查和系统优化非常有帮助 。
- 系统监控和管理:通过监控开关状态的变化,我们可以了解系统的运行状况,以及用户对API接口的使用情况 。这有助于进行系统性能优化和资源管理 。
- 用户体验:在某些情况下,当API接口不可用或需要维护时,向用户显示友好的提示信息可以避免用户感到困惑或带来不必要的困扰 。同时,提前通知用户也体现了对用户体验的关注 。
- 合规性和隐私保护:在涉及敏感数据或受限制的API接口中,通过关闭开关并显示提示信息,可以确保遵守相关法规和隐私政策 , 对数据进行适当的管理和保护 。
在切面中 , 我们可以使用Spring AOP的切入点(Pointcut)来指定需要拦截的方法 。一旦方法被拦截,我们可以在切面的通知(Advice)中定义相应的默认行为 。接下来我们将一步一步的实现接口开关功能 。
- 自定义注解
@Target({ElementType.TYPE, ElementType.METHOD})public @interface ApiSwitch {/**接口对应的key , 通过可以该key查询接口是否关闭*/String key() default "" ;/**解析器beanName , 通过具体的实现获取key对应的值*/String resolver() default "" ;/**开启后降级方法名*/String fallback() default "" ;}
- 定义解析器接口
public interface SwitchResolver {boolean resolver(String key) ;public void config(String key, Integer onoff) ;}
- 接口默认实现
@Componentpublic class ConcurrentMapResolver implements SwitchResolver {private Map<String, Integer> keys = new ConcurrentHashMap<>() ;@Overridepublic boolean resolver(String key) {Integer value = https://www.isolves.com/it/cxkf/bk/2023-11-28/keys.get(key) ;return value == null ? false : (value == 1) ;}public void config(String key, Integer onoff) {keys.put(key, onoff) ;}}
- 基于redis实现
@Componentpublic class RedisResolver implements SwitchResolver {private final StringRedisTemplate stringRedisTemplate ;public RedisResolver(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate ;}@Overridepublic boolean resolver(String key) {String value = https://www.isolves.com/it/cxkf/bk/2023-11-28/this.stringRedisTemplate.opsForValue().get(key) ;return !(value == null || "0".equals(value)) ;}@Overridepublic void config(String key, Integer onoff) {this.stringRedisTemplate.opsForValue().set(key, String.valueOf(onoff)) ;}}
这里就提供两种默认的实现 。- 定义切面
@Component@Aspectpublic class ApiSwitchAspect implements ApplicationContextAware {private ApplicationContext context ;private final SwitchProperties switchProperties ;public static final Map<String, Class<? extends SwitchResolver>> MAPPINGS;static {// 初始化所有的解析器Map<String, Class<? extends SwitchResolver>> mappings = new HashMap<>() ;mappings.put("map", ConcurrentMapResolver.class) ;mappings.put("redis", RedisResolver.class) ;MAPPINGS = Collections.unmodifiableMap(mappings) ;}public ApiSwitchAspect(SwitchProperties switchProperties) {this.switchProperties = switchProperties ;}@Pointcut("@annotation(apiSwitch)")private void onoff(ApiSwitch apiSwitch) {}@Around("onoff(apiSwitch)")public Object ctl(ProceedingJoinPoint pjp, ApiSwitch apiSwitch) throws Throwable {// 对应接口开关的keyString key = apiSwitch.key() ;// 解析器bean的名称String resolverName = apiSwitch.resolver() ;// 降级方法名String fallback = apiSwitch.fallback() ;SwitchResolver resolver = null ;// 根据指定的beanName获取具体的解析器;以下都不考虑不存在的情况if (StringUtils.hasLength(resolverName)) {resolver = this.context.getBean(resolverName, SwitchResolver.class) ;} else {resolver = this.context.getBean(MAPPINGS.get(this.switchProperties.getResolver())) ;}// 解析器不存在则直接调用目标接口if (resolver == null || !resolver.resolver(key)) {return pjp.proceed() ;}// 调用降级的方法;关于降级的方法简单点,都必须在当前接口类中 , 同时还不考虑方法参数的情况if (!StringUtils.hasLength(fallback)) {// 未配置的情况return "接口不可用" ;}Class<?> clazz = pjp.getSignature().getDeclaringType() ;Method fallbackMethod = clazz.getDeclaredMethod(fallback) ;return fallbackMethod.invoke(pjp.getTarget()) ;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context = applicationContext ;}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 利用Linux事件驱动编程实现嵌入式系统
- Spring Boot中实现订单30分钟自动取消的策略思路及源代码
- 数字孪生如何实现成功的工程和转型?
- Spring Cloud 实现分布式实时日志分析采集的三种方案
- 真丝睡衣美女,魅力与优雅的完美结合
- 俞飞鸿亲自示范:穿羽绒服不配打底裤,色不过三,52岁也优雅温婉
- 刘诗诗海外封神,穿礼服亮相微博之夜,尽显优雅
- 到60岁都没“大妈感”的女人:不烫发、不穿老年装,美得优雅高级
- Python的range函数内部是怎么实现的?
- 深入Linux内核:探秘进程实现的神秘世界