问题
最近工作中碰到了一个需求,需要实现后台对接口的重复调用,具体需求是:
- 后台需要缓存接口调用的结果;
- 每隔一段时间就自动重复调用一次,刷新缓存中的结果数据;
- 前端调用接口的时候,会直接返回缓存数据,而不会真实地调用接口,获取数据。
想到的具体实现方案是:
- 缓存结果数据很简单,直接用AOP做接口增强就行了;
- 保存接口调用的状态,以便后面能自动重复调用接口;
- 使用定时任务或者延迟队列来实现自动调用。
实现
AOP做接口增强
因为这个不是本文重点,所以就不贴具体实现了,大致思路就是:创建一个切点,然后在方法运行前后增强从缓存中取结果和将结果存入缓存的操作就行了。
保存接口调用状态
这个是本文的重点,我们知道一个接口的定义要素有:类、方法名和参数,所以我们要存储的调用信息就是这些信息了,具体实现步骤如下:
- 创建一个接口调用信息的类
public class RequestObject implements Serializable {
// 类名
private String clazzName;
// 方法名
private String methodName;
// 参数对象
private Object[] params;
// 参数类型
private String[] paramTypeNames;
public String getClazzName() {
return clazzName;
}
public void setClazzName(String clazzName) {
this.clazzName = clazzName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public String[] getParamTypeNames() {
return paramTypeNames;
}
public void setParamTypeNames(String[] paramTypeNames) {
this.paramTypeNames = paramTypeNames;
}
}
- 在AOP增强代码中保存调用接口的调用信息,至于这个对象存放在哪里就任君选择了。
// 用反射获取接口的各种信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RequestObject requestObject = new RequestObject();
// 类名
Object target = joinPoint.getTarget();
requestObject.setClazzName(target.getClass().getName());
// 方法名
requestObject.setMethodName(method.getName());
// 参数值
Object[] args = joinPoint.getArgs();
requestObject.setParams(args);
// 参数类型
if (args != null && args.length > 0){
String[] typeNames = new String[args.length];
for (int i = 0; i < args.length; i++){
typeNames[i] = args[i].getClass().getName();
}
requestObject.setParamTypeNames(typeNames);
}
- 用定时任务、mq或者延迟队列来实现对接口的自动调用:取出调用信息对象,然后执行接口调用
public String test() throws Throwable{
RequestObject requestObject = (RequestObject) RedisUtils.get("key");
// 加载类对象
Class c = Class.forName(requestObject.getClazzName());
// 参数类型
String[] paramTypeNames = requestObject.getParamTypeNames();
// 生成方法对象
Method method;
if (paramTypeNames == null || paramTypeNames.length <= 0){
method = c.getMethod(requestObject.getMethodName());
}else {
Class[] paramTypeClass = new Class[paramTypeNames.length];
for (int i = 0; i < paramTypeNames.length; i++){
paramTypeClass[i] = Class.forName(paramTypeNames[i]);
}
method = c.getMethod(requestObject.getMethodName(), paramTypeClass);
}
// 方法的调用,参数是类的一个对象和参数值;
// 这里对象是从spring中获取的,你也可以用其他方式来生成对象,
// 毕竟类对象都已经有了,用类生成对象应该不是什么难事
Object result = method.invoke(ApplicationContextHolder.getBean(c), requestObject.getParams());
return result.toString();
}
到这里,这个功能就已经大致实现了,虽然不是很难,但是用反射实现接口的重复调用还是很有意思的,记录一下,开心~