应用系统开发时,与第三方系统对接是常见的需求,当我们兴冲冲按照接口文档开发好代码的时候,第三方系统却未准备好,而我们编写的业务代码散落与业务模块的各个地方。为了不影响我们系统的运行,必须暂时屏蔽这部分代码调用。
怎么办?
-
方法一: 注释掉这些代码不就行了吗,确实,大部分情况下我们可能就是这样做的。
-
方法二: 配置开关项,接口未准备好的时候设为0或false,启用时设置为1或true,这好像是我们能想到的比方法一更好的方法,不过针对调用前频繁的 if 判断和对代码结构的整体破坏让人十分不爽。
有没有办法摆脱方法二的弊端呢!答案是肯定的,祭出我们的spring AOP。
假定我们的接口返回对象为InvokeResult,全部的接口方法均封装好放在包com.fly.demo.api下面。
/**
*
* 接口返回对象
*
* @author 00fly
* @version [版本号, 2018年12月10日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class InvokeResult
{
private Object data;
private boolean success = false;
private String errorMessage;
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public boolean isSuccess()
{
return success;
}
public void setSuccess(boolean success)
{
this.success = success;
}
public String getErrorMessage()
{
return errorMessage;
}
public void setErrorMessage(String errorMessage)
{
this.errorMessage = errorMessage;
}
}
aop核心代码
import java.lang.reflect.Method;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.RequestMapping;
import com.fly.core.entity.InvokeResult;
/**
* aop 处理接口未启用返回的接口调用结果
*
* @author 00fly
* @version [版本号, 2018年12月10日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Aspect
@Component
public class MockCalledAspect
{
static final Logger LOGGER = LoggerFactory.getLogger(MockCalledAspect.class);
/**
* 配置文件配置接口是否启用
*/
@Value("${interface.enable:false}")
private boolean enable;
/**
* 接口是否启用
*/
public boolean isEnable()
{
return enable;
}
@Around("within(com.fly.demo.api.*Interface)")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable
{
String className = joinPoint.getTarget().getClass().getName();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
String methodName = new StringBuffer(className).append(".").append(method.getName()).toString();
boolean isController = method.isAnnotationPresent(RequestMapping.class);
Object result = null;
StopWatch clock = new StopWatch();
clock.start(methodName);
// 接口未启用的话,构造返回失败信息
if (className.startsWith("com.fly.demo.api") && !isEnable())
{
String returnClassName = method.getReturnType().getSimpleName();
if ("InvokeResult".equals(returnClassName))
{
InvokeResult stringInvokeResult = new InvokeResult();
stringInvokeResult.setSuccess(false);
stringInvokeResult.setErrorMessage("接口配置未启用");
result = stringInvokeResult;
}
if (result != null)
{
LOGGER.info("执行{} 返回结果:{}", methodName, ToStringBuilder.reflectionToString(result));
}
if (result == null)
{
result = joinPoint.proceed();
}
}
clock.stop();
if (isController)
{
LOGGER.info("end in {} ms, method = {}\n", clock.getTotalTimeMillis(), clock.getLastTaskName());
}
else
{
LOGGER.info("running {} ms, method = {}", clock.getTotalTimeMillis(), clock.getLastTaskName());
}
return result;
}
}
我们的模块接口代码什么都不用动,就实现了模拟接口的返回是不是很棒!
而接口启用时我们可以删除上面这段代码或者isEnable返回值设置为true或者在配置文件将interface.enable设为true。
—over—