使用自定义注解,再用AOP切面,完成控制接口的开关。感觉挺有意思的。
直接上案例
Redis存储了这个接口的key,1表示放开,0表示关闭。
下图所示表示放开接口。
使用Postman调用接口,成功。
将Redis对应的value改为0(实际开发中是后台管理),在调用一次。
接口关闭。这样就可以轻松管理接口的开放与关闭了。
代码
首先看一下整体框架
annotation
package com.xmr.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时起作用
public @interface ServiceSwitch {
// 业务开关key
String switchKey();
// 开关:0表示关,1表示开,放行
String switchVal() default "0";
// 提示信息
String message() default "该接口关闭!";
}
constant
package com.xmr.constant;
public class Constant {
public static class ConfigCode{
// AI服务
public static final String AI_Service_SWITCH = "ai_service_switch";
// 其他业务配置……
}
}
aop
package com.xmr.aop;
import com.xmr.annotation.ServiceSwitch;
import com.xmr.constant.Constant;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
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.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class ServiceSwitchAOP{
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 定义切点,使用了@ServiceSwitch注解的类或方法都拦截
*/
@Pointcut("@annotation(com.xmr.annotation.ServiceSwitch)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point){
// 获取被代理的方法的参数
Object[] args = point.getArgs();
// 获取被代理的对象
Object target = point.getTarget();
// 获取通知签名
MethodSignature signature = (MethodSignature) point.getSignature();
try {
// 获取被代理的方法
Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
// 获取方法上的注解
ServiceSwitch annotation = method.getAnnotation(ServiceSwitch.class);
// 核心业务逻辑
if (annotation != null) {
// 获取注解里面的元素
String switchKey = annotation.switchKey();
String switchVal = annotation.switchVal();
String message = annotation.message();
// 方法1:存在redis,下面的做法
// 方法2:放在数据库
String configVal = redisTemplate.opsForValue().get(Constant.ConfigCode.AI_Service_SWITCH);
if (switchVal.equals(configVal)) {
// 开关打开,则返回提示。
// 表示开关关闭,为0
return message;
}
}
// 放行
return point.proceed(args);
} catch (Throwable e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
这里有两种方式:
- 配置加在Redis,查询时从Redis获取;
- 配置加在数据库,查询时从表获取。
- 优化方法:直接放到数据库,但是获取配置项的方法用SpringCache缓存,变更时要清理缓存。
Controller
package com.xmr.Controller;
import com.xmr.service.AiService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 调用Ai接口
*/
@RestController
@RequestMapping("/api")
@AllArgsConstructor
public class AiController {
private final AiService aiService;
@PostMapping("/Ai")
public String createOrder() {
return aiService.createOrder();
}
}
Service
package com.xmr.service;
import com.xmr.annotation.ServiceSwitch;
import com.xmr.constant.Constant;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@Service
public class AiService {
/**
* 调用AI服务
*/
@ServiceSwitch(switchKey = Constant.ConfigCode.AI_Service_SWITCH)
public String createOrder() {
// 具体业务逻辑省略....
return "调用AI服务成功";
}
}
yml配置
server:
port: 8080
spring:
data:
redis:
database: 0
host: localhost
port: 6379