Spring AOP日志记录

时间:2022-11-01 11:58:04


分享知识 传递快乐


如有提议或更好的方法请留言--分享知识 传递快乐。


本项目主要是对Controller层和Service层出现异常时进行拦截,从而记录在数据库中。

关键代码如下:


package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 自定义注解
*
* @author Dingdong
* @date 2017年5月24日
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogger {

String description() default "";

}



package com.aop;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Enumeration;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.annotation.OperationLogger;
import com.model.SysLog;
import com.service.ISysLogService;

/**
* AOP切点类
*
* @author Dingdong
* @date 2017年5月23日
*/
public class LogAspect {

private static final Logger LOGGER = LogManager.getLogger(LogAspect.class);
private static final String LOG_CONTENT = "[类名]:%s,[方法]:%s,[参数]:%s";
private static final String[] METHOD_CONTENT = { "insert", "delete", "update", "save" };

@Resource
private ISysLogService logService;

/**
* 前置通知
*
* @author Dingdong
* @date 2017年5月22日
*
* @param joinPoint
*/
public void before(JoinPoint joinPoint) {
LOGGER.info("前置通知");
saveLog(joinPoint, null);
}

/**
* 异常操作并带有返回值
*
* @author Dingdong
* @date 2017年5月24日
*
* @param joinPoint
* @param argObj
*/
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
LOGGER.info("异常操作");
saveLog(joinPoint, exception);
}

/**
* 保存日志到数据库
*
* @author Dingdong
* @date 2017年5月24日
*
* @param joinPoint
* @param exception
*/
private void saveLog(JoinPoint joinPoint, Exception exception) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String methodName = joinPoint.getSignature().getName();// 方法名
Method method = currentMethod(joinPoint, methodName);
OperationLogger log = method.getAnnotation(OperationLogger.class);

SysLog logs = new SysLog();
logs.setUserId(1);
logs.setDateTime(new Date());
logs.setContent(operateContent(joinPoint, methodName, request));
if (exception != null) {
LOGGER.info("Service出现异常");
logs.setOperation("异常");
logs.setAbnormity(exception.toString());
logService.insert(logs);
} else {
LOGGER.info("方法名:" + methodName);
if (isWriteLog(methodName)) {
logs.setOperation((log != null) ? log.description() : null);
logService.insert(logs);
}
}
}

/**
* 判断是哪些方法可以写入LOG
*
* @author Dingdong
* @date 2017年5月24日
*
* @param method
* @return
*/
private boolean isWriteLog(String method) {
boolean falg = false;
for (String s : METHOD_CONTENT) {
if (method.indexOf(s) > -1) {
falg = true;
break;
}
}
return falg;
}

/**
* 获取当前执行的方法并判断
*
* @param joinPoint
* 连接点
* @param methodName
* 方法名称
* @return 方法
*/
private Method currentMethod(JoinPoint joinPoint, String methodName) {
Method[] methods = joinPoint.getTarget().getClass().getMethods();
Method resultMethod = null;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
resultMethod = method;
break;
}
}
return resultMethod;
}

/**
* 获取当前传递的参数
*
* @param joinPoint
* 连接点
* @param methodName
* 方法名称
* @return 操作内容
*/
private String operateContent(JoinPoint joinPoint, String methodName, HttpServletRequest request) {
String className = joinPoint.getTarget().getClass().getName();
Object[] params = joinPoint.getArgs();
StringBuffer bf = new StringBuffer();
if (params != null && params.length > 0) {
Enumeration<String> paraNames = request.getParameterNames();
while (paraNames.hasMoreElements()) {
String key = paraNames.nextElement();
bf.append(key).append("=");
bf.append(request.getParameter(key)).append("&");
}
if (StringUtils.isBlank(bf.toString())) {
bf.append(request.getQueryString());
}
}
return String.format(LOG_CONTENT, className, methodName, bf.toString());
}
}




package com.controller;

import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.fastjson.JSON;
import com.annotation.OperationLogger;
import com.model.User;
import com.service.IUserService;

/**
*
* @author Dingdong
* @date 2017年5月22日
*/
@Controller
public class UserController {

@Resource
private IUserService userService;

/**
* 查询
*
* @author Dingdong
* @date 2017年5月22日
*
* @param model
* @return
*/
@RequestMapping("forwordIndex")
@OperationLogger(description = "查询")
public String forwordIndex(Model model) {

List<User> list = userService.selectAll();
model.addAttribute("users", JSON.toJSONString(list));
model.addAttribute("user", list);

return "index";
}

/**
* 添加
*
* @author Dingdong
* @date 2017年5月22日
*
* @param model
* @return
*/
@RequestMapping("saveUser")
@OperationLogger(description = "添加")
public String saveUser(User user) {

System.out.println(user.toString());
int no = userService.insertUser(user);

return no > 0 ? "success" : "error";
}

}




package com.service.impl;

import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.dao.IUserDao;
import com.model.User;
import com.service.IUserService;

/**
*
* @author Dingdong
* @date 2017年5月3日
*/
@Service
public class UserServiceImpl implements IUserService {

@Resource
private IUserDao dao;

public String selectJson() {
List<User> list = dao.selectAll();
return JSON.toJSONString(list);
}

public List<User> selectAll() {

return dao.selectAll();
}

public List<Map<String, String>> selectMap() {

return dao.selectMap();
}

public int insertUser(User user) {

// user = null;

return dao.insert(user);
}
}


XML配置文件1 spring-config.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">


<!-- 引入属性文件 -->
<context:property-placeholder location="classpath:config/db.properties" />

<!-- Service包(自动注入) -->
<context:component-scan base-package="com.service" />

<import resource="classpath:spring/spring-mybatis.xml" />

</beans>



XML配置文件2 spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 配置数据源省略... -->

<!--将日志类注入到bean中。 -->
<bean class="com.aop.LogAspect"></bean>

<!-- 启用@AspectJ支持 -->
<aop:aspectj-autoproxy/>

<!-- 配置切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.service..*.*(..))" />
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
<!--调用日志类 -->
<aop:aspect ref="logAspect" order="1">
<!-- <aop:after-returning pointcut-ref="transactionPointcut" returning="argObj" arg-names="argObj" method="afterReturning"/> -->
<aop:after-throwing pointcut-ref="transactionPointcut" throwing="exception" arg-names="exception" method="afterThrowing"/>
</aop:aspect>
</aop:config>

</beans>



XML配置文件3 spring-mvc.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<mvc:annotation-driven />
<!-- 配置哪些是静态资源,缺省Servlet 直接返回 -->
<mvc:resources mapping="/static/**" location="/static/" cache-period="31556926" />

<context:component-scan base-package="com.controller" />

<!-- 配置结果页面 前缀和后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>


<bean class="com.aop.LogAspect" />

<!-- proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。 如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。 如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK基于接口的代理 -->
<aop:aspectj-autoproxy proxy-target-class="true" />

<aop:config>
<aop:aspect ref="logAspect" order="2">
<aop:pointcut expression="execution(* com.controller.*.*(..))" />
<aop:before pointcut-ref="transactionPointcut" method="before" />
<!-- <aop:after-throwing pointcut-ref="transactionPointcut" throwing="exception" arg-names="exception" method="afterThrowing"/> -->
</aop:aspect>
</aop:config>

</beans>



个人总结
1.拦截Controller注意事项

想要拦截Controller时要把AOP放到spring-mvc.xml中,否则在拦截不了,本人就在此花了好些时间


2.aop:aspectj-autoproxy
<aop:aspectj-autoproxy proxy-target-class="true" />
proxy-target-class:属性值决定是基于接口的还是基于类的代理被创建。 如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。 如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK基于接口的代理


3.order
<aop:aspect order="2" />
spring AOP事务与Afterthrowing冲突是要设置"order"参数,当在执行事务时遇到错误(exception)就会将插入的数据进行回滚,如果想在事务执行遇到exception回滚以后再去控制它去进入afterthowing就要用到"order"参数。 "order"参数,这个参数是用来控制aop通知的优先级,值越小,优先级越高。