spring的IOC和AOP是说的最烂的东西,尤其是后者,给编码带来很多很多的方便,网上不上代码都说了AOP主要用来做日志记录,异常处理,记录程序执行时间,缓存这样的事情,但是不少只是简单的做一个说明,没有代码,这里我把项目中实际使用的抽出来,本文主要是关于最简单的日志的记录。
前提:需要对spring aop概念有所了解,以及有spring开发经验,了解自定义注解。如果不明白,看下面的文章:
深入理解Java:注解(Annotation)自定义注解入门
-----------------------------------------------------------------------------------------------------------
下面进入正题:最终使用的效果如下:
@Service
public class TallyTypeService extends CrudService<TallyTypeDao, TallyType> {
.....
@LoggerPoint(pointKey=PointerKey.MONEY_TALLYTYPE)
public Page<TallyType> findPage(Page<TallyType> page, TallyType entity) {
return super.findPage(page,entity);
}
......
}
使用了自定义注解,
LoggerPoint标明要记录当前方法的执行参数,执行时间,执行类别等信息。pointKey是一个业务的分类。
/**
* 对于日志注入点的功能说明枚举.
* @author Administrator
*
*/
public enum PointerKey {
ALL("全部"), SINGLE("单接口"), UNKNOW("未知接口"), Test("测试"), MONEY_TALLYTYPE(
"金额类型");
private String name;
private PointerKey(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对于LoggerPoint注解,很简单:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface LoggerPoint {
public PointerKey pointKey();
}
关键的是,使用解析上面注解的切面类:
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.exception.ExceptionUtils;
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.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.renjie120.common.enums.PointerKey;
import com.renjie120.common.utils.JsonUtils;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LoggerAspect {
public static Logger logger = LoggerFactory.getLogger(LoggerAspect.class);
private Method getMethod(ProceedingJoinPoint pjp){
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
return method;
}
@Around("@annotation(com.renjie120.common.aspect.LoggerPoint)")
public Object trafficInterface(ProceedingJoinPoint pjp) throws Throwable {
Method method = this.getMethod(pjp);
LoggerPoint loggerPoint = method.getAnnotation(LoggerPoint.class);
PointerKey pointerKey = loggerPoint.pointKey();
Object[] args = pjp.getArgs();
Map<String,Object> paramMap = new HashMap<String,Object>();
for(Object arg:args){
paramMap.put(arg.getClass().getSimpleName(), arg);
}
String requestJson = JsonUtils.toJsonStr(paramMap);
String gid= UUID.randomUUID().toString();
System.out.println("请求参数:"+requestJson);
Object returnObj = null;
String errorMsg = null;
try {
System.out.println("当前执行:"+method.getName()+"---"+pointerKey.name()+",gid="+gid);returnObj = pjp.proceed();
return returnObj;
} catch (Exception e) {
errorMsg = ExceptionUtils.getStackTrace(e);
logger.error(e.getMessage(),e);
throw e;
} finally {
if (returnObj == null) {
returnObj = errorMsg;
}
System.out.println("响应:"+JsonUtils.toJsonStr(returnObj));
}
}
}
1、上面的注解@Component,@Aspect为spring 注解,需要放在扫描路径中:
<context:component-scan base-package="com.renjie120" >
</context:component-scan>
2、上图的@Around注解,标明使用的是环绕切面。
3、打印日志这里是直接system.out出来,实际项目中可以保存到数据库日志表中,对于大量数据为不影响操作,可以先保存到队列中,再从队列中取出异步保存到数据库中。
4、在实际使用中,也可以使用配置xml方式声明上面的切面处理类,这种情况就不需要使用上面的
@Component,
@Aspect,
@Around注解
:
在spring-context.xml中:
<bean id="loggerAspect" class="com.renjie120.common.annotation.log.LoggerAspect" /> <aop:config> <aop:pointcut id="loginPointer" expression="@annotation(com.renjie120.common.annotation.log.LoggerPoint)" /> <aop:aspect id="aspect" ref="loggerAspect"> <aop:around method="trafficInterface" pointcut-ref="loginPointer" /> </aop:aspect> </aop:config>