最近在做接口,需要将接口调用做个记录,记录接口调用的入参、出参、执行之间等详细信息,因此就想到了切面,自定义一个注解,将含有欧该注解的方法的调用都记录下来。(最一开始想用的是webservice的拦截器,但是webservice拦截器不能将入参和出参报文对应上,因此只能用spring aop,用拦截器来做。)
具体使用方法:
1.依赖jar包导入:项目用的spring版本比较低2.5.6,所以jar包版本也比较低
aspectjweaver.jar和aspectjrt.jar可以通过这个链接下载:http://www.eclipse.org/aspectj/
下载步骤:
2.配置拦截器代理扫描:
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
3.自定义注解:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CalledRecord {
public String desc() default "";
}
4.定义拦截器:
package com.dcits.base.aspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.stereotype.Component;
import com.dcits.base.bean.CalledRecordBean;
import com.dcits.base.ws.CalledRecordAspectWS;
import com.webservices.org.annotation.CalledRecord;
/**
* 记录带有CalledRecord注解的接口调用信息
* @Description
*/
@Aspect
@Component
public class CalledRecordAspect {
private static final Logger logger = Logger.getLogger(CalledRecordAspect.class);
@Autowired
private CalledRecordAspectWS CalledRecordAspectWSImpl;
/**
* 拦截含有CalledRecord注解的方法,并记录相关信息
*/
@Pointcut("@annotation(com.webservices.org.annotation.CalledRecord)")
public void calledRecord() {
}
@Around("calledRecord()")
public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("进入了CalledRecordAspect.handle方法");
CalledRecordBean crd;
Object retVal = null;;
try {
crd = preHandle(joinPoint);
long startTime = System.currentTimeMillis();
retVal = joinPoint.proceed();
long endTime = System.currentTimeMillis();
crd.setStartTime(formatTime(startTime));
crd.setEndTime(formatTime(endTime));
crd.setDuration("" + (endTime - startTime));
String respParam = postHandle(retVal);
crd.setOutArgs(respParam);
int num = CalledRecordAspectWSImpl.saveCalledRecord(crd);
logger.info("保存了"+num+"条数据");
}catch(Exception e) {
logger.info("保存接口调用详情异常");
e.printStackTrace();
}
return retVal;
}
/**
* 格式化时间格式 long-->yyyy-MM-dd HH:mm:ss
*/
public String formatTime(long time) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(time);
return sdf.format(date);
}
/**
* 处理入参
*/
private CalledRecordBean preHandle(ProceedingJoinPoint joinPoint) throws SecurityException, NoSuchFieldException {
CalledRecordBean crd = new CalledRecordBean();
StringBuffer inArgs = new StringBuffer();
Object[] args1 = joinPoint.getArgs();
for (Object arg : args1) {
inArgs.append(arg.toString());
}
crd.setInArgs(inArgs.toString());
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
crd.setMethodName(methodName);
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
Annotation[] annotations = targetMethod.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].annotationType().equals(CalledRecord.class)) {
CalledRecord call = (CalledRecord) annotations[i];
crd.setMethodDesc(call.desc());
break;
}
}
return crd;
}
/**
* 处理出参
*/
private String postHandle(Object retVal) {
return retVal.toString();
}
}
spring aop分两层,外层是spring框架层面的,内层就是开发人员自定义的了,我们写的就是内层的拦截器。
按照上面的步骤就能定义一个简单的拦截器了。
遇到的问题:
1.漏掉@Component注解,导致拦截器一直未被调用 **********
2.handle方法未写返回值,导致方法被调用后调用者接收到的返回值为null *************。