上文讲解了sping Aop的基础概念与实现逻辑,本文从实践出发介绍sping进行aop配置并实现日志打印过程。
环境介绍:spring4.0 release版本,前端使用spring mvc框架。
1. 定义注解拦截器
/**
* 自定义注解 拦截service
*/
@Component
public class LogInterceptor {
@Resource
private SystemLogService systemLogService;
private final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
/**
* @Description: 前置通知
* @param @param joinPoint
* @return void
* @author whp
*/
public void before(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
String methodName = joinPoint.getSignature().getName();
try {
logger.info("=====Entering " + methodName + "()=====");
logger.info("请求方法:"
+ (joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName() + "()"));
} catch (Exception e) {
// 记录本地异常日志
logger.error("==Entering " + methodName + "()异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
/**
* @Description: 后置通知
* @param @param joinPoint
* @return void
* @author whp
*/
public void after(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
// 请求的IP
String methodName = joinPoint.getSignature().getName();
try {
logger.info("=====leaving " + methodName + "()=====");
// contentToTxt("=====leaving " + methodName + "()=====");
} catch (Exception e) {
// 记录本地异常日志
logger.error("==leaving " + methodName + "()异常==");
logger.error("异常信息:{}", e.getMessage());
}
}
/**
* @Description: 通用异常通知
* @param @param joinPoint
* @param @param e
* @param @throws NoSuchFieldException
* @param @throws SecurityException
* @return void
* @author whp
*/
public void afterThrowing(JoinPoint joinPoint, Throwable e)
throws NoSuchFieldException, SecurityException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
// 请求的IP
String params = "";
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
for (Object obj : args) {
if (null != obj) {
Field[] fields = obj.getClass().getDeclaredFields();
if (null != fields && fields.length > 0) {
params += obj.getClass().getSimpleName() + "[";
for (Field field : fields) {
String name = field.getName();
Object value = getFieldValueByName(name, obj);
if (null != value && (!"".equals(value))) {
params += name + ":" + value + ";";
}
}
params += "],";
} else {
params += obj.getClass().getSimpleName() + ":"
+ obj.toString() + ";";
}
}
}
}
String methodName = joinPoint.getSignature().getName();
try {
/* ========控制台输出========= */
logger.info("=====Entering " + methodName + "()异常=====");
logger.info("异常代码:" + e.getClass().getName());
logger.info("异常信息:" + e.getMessage());
logger.info("异常方法:"
+ (joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName() + "()"));
logger.info("请求参数:" + params);
// ==========数据库日志=========
logger.info("=====leaving " + methodName + "()异常=====");
} catch (Exception ex) {
// 记录本地异常日志
logger.error("==" + methodName + "()异常通知异常==");
logger.error("异常信息:{}", ex.getMessage());
logger.error("==" + methodName + "()异常结束==");
}
// ==========记录本地异常日志==========
logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget()
.getClass().getName()
+ joinPoint.getSignature().getName(), e.getClass().getName(),
e.getMessage(), params);
}
/**
* @Description: 根据属性名获取属性值
* @param @param fieldName
* @param @param o
* @param @return
* @return Object
* @author whp
*/
private Object getFieldValueByName(String fieldName, Object o) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
if (o.getClass().getName().startsWith("com.cxg")) {
Method method = o.getClass().getMethod(getter, new Class[] {});
Object value = method.invoke(o, new Object[] {});
return value;
} else {
return null;
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
}
}
}
2. 将拦截器注入到springmvc.xml中
<bean id="logInterceptor" class="com.cxg.interactiveweb.logs.util.LogInterceptor" />
3. 配置aop容器
<aop:config proxy-target-class="true">
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:pointcut
expression="execution(* com.cxg.interactiveweb.*.controller..*.*(..))"
id="logControllerCut" />
<aop:pointcut
expression="execution(* com.cxg.interactiveweb.*.service..*.*(..))"
id="logServiceCut" />
<!-- <aop:around method="around" pointcut-ref="logPointCut" /> -->
<aop:before method="before" pointcut-ref="logControllerCut" />
<aop:after method="after" pointcut-ref="logControllerCut" />
<aop:after-throwing method="afterThrowing"
pointcut-ref="logControllerCut" throwing="e" />
<aop:before method="before" pointcut-ref="logServiceCut" />
<aop:after method="after" pointcut-ref="logServiceCut" />
</aop:aspect>
</aop:config>
上面创建过程非常简单,首先定义一个拦截器类,该类实现了,before,after,afterThrowing三个方法,添加@Component注解,将拦截器注入到spring容器。
配置AOP,定义aspect切面,定义pointcut切入点,使用expression将所有的controller和service方法定义切入点,通过before,after,after-throwing定义advice,指定切入方法。