最近项目中需要记录服务端接口访问日志,所以在开发过程中回顾了一下AOP相关的内容,特此记录,便于日后查阅。
1、引入依赖
<!-- 引入aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、系统日志拦截器
@Aspect
@Component
public class SystemLogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SystemLogDAO systemLogDAO;
@Pointcut("execution(public * com.cy.ops.api.*.controller..*(..))")
public void systemLog() {}
@Around(value = "systemLog()")
public ResponseResult doAround(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
//1、从当前Session中获取登录用户信息
UserInfoBO userInfo = WebSession.getUserInfo(request, response);
if(userInfo == null) {
return new ResponseResult().setSuccess(false).setMessage(ResultCode.RECORD_LOGIN_EXPIRE.getMessage()).setCode(ResultCode.RECORD_LOGIN_EXPIRE.getCOde());
}
//2、记录执行时间
long startTime = System.currentTimeMillis();
ResponseResult result = (ResponseResult) joinPoint.proceed(joinPoint.getArgs());
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
//3、入系统日志表
SystemLogDO systemLogDO = new SystemLogDO();
if(joinPoint.getArgs().length > 0){
systemLogDO.setPara(JsonToBeanUtil.beanToJSON(joinPoint.getArgs()[0]));
}
systemLogDO.setClientIp(IpUtil.getClientIp(request));
systemLogDO.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
systemLogDO.setOperator(userInfo.getUserName());
systemLogDO.setReqUri(request.getRequestURI());
systemLogDO.setReturnData(JsonToBeanUtil.beanToJSON(result));
systemLogDO.setStartTime(String.valueOf(startTime));
systemLogDO.setEndTime(String.valueOf(endTime));
systemLogDO.setTotalTime(String.valueOf(totalTime));
systemLogDO.setGmtCreateUser(userInfo.getUserId());
systemLogDO.setGmtModifiedUser(userInfo.getUserId());
systemLogDO.setIsDelete(0);
systemLogDAO.insert(systemLogDO);
return result;
}
@Before("systemLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
logger.info("***********************进入前置切面***********************");
}
@After("systemLog()")
public void doAfter(JoinPoint joinPoint) throws Throwable {
logger.info("***********************进入后置切面***********************");
}
@AfterReturning((pointcut="systemLog()" returning="object")
public void doAfterReturning(JoinPoint joinPoint, Object object) throws Throwable {
logger.info("***********************进入返回切面***********************");
}
@AfterThrowing(pointcut = "systemLog()", throwing = "e")
public void doAfterThrowing(Exception e) throws Exception{
logger.info("***********************进入异常切面***********************");
}
}
3、Mapper层异常拦截器
@Aspect
@Component
public class MapperLogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut("execution(public * com.cy.ops.*.dal..*(..))")
public void mapperLog() {}
@AfterThrowing(pointcut = "mapperLog()", throwing = "e")
public void doAfterThrowing(Exception e) throws Exception{
throw new SqlException(e);
}
}
4、自定义SQL异常
package com.czgo.exception;
/**
* 自定义异常类(继承运行时异常)
* @author zhangzhixiang
* @version 2018/11/09
*/
public class SqlException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误编码
*/
private String errorCode;
/**
* 消息是否为属性文件中的Key
*/
private boolean propertiesKey = true;
/**
* 构造一个基本异常.
*
* @param cause 异常信息
*
*/
public SqlException(Throwable cause)
{
super(cause);
}
/**
* 构造一个基本异常.
*
* @param message 信息描述
*
*/
public SqlException(String message)
{
super(message);
}
/**
* 构造一个基本异常.
*
* @param errorCode 错误编码
* @param message 信息描述
*
*/
public SqlException(String errorCode, String message)
{
this(errorCode, message, true);
}
/**
* 构造一个基本异常.
*
* @param errorCode 错误编码
* @param message
*
*/
public SqlException(String errorCode, String message, Throwable cause)
{
this(errorCode, message, cause, true);
}
/**
* 构造一个基本异常.
*
* @param errorCode 错误编码
* @param message 信息描述
* @param propertiesKey 消息是否为属性文件中的Key
*
*/
public SqlException(String errorCode, String message, boolean propertiesKey)
{
super(message);
this.setErrorCode(errorCode);
this.setPropertiesKey(propertiesKey);
}
/**
* 构造一个基本异常.
*
* @param errorCode 错误编码
* @param message 信息描述
*
*/
public SqlException(String errorCode, String message, Throwable cause, boolean propertiesKey)
{
super(message, cause);
this.setErrorCode(errorCode);
this.setPropertiesKey(propertiesKey);
}
/**
* 构造一个基本异常.
*
* @param message 信息描述
* @param cause 根异常类(可以存入任何异常)
*
*/
public SqlException(String message, Throwable cause)
{
super(message, cause);
}
public String getErrorCode()
{
return errorCode;
}
public void setErrorCode(String errorCode)
{
this.errorCode = errorCode;
}
public boolean isPropertiesKey()
{
return propertiesKey;
}
public void setPropertiesKey(boolean propertiesKey)
{
this.propertiesKey = propertiesKey;
}
}
5、IPUtil工具类
package com.gcloud.common;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description: IP工具类
* @Author: zhangzhixiang
* @CreateDate: 2018/11/08 16:08:54
* @Version: 1.0
*/
public class IPUtil {
/**
* 检查IP是否合法
* @param ip
* @return
*/
public static boolean ipValid(String ip) {
String regex0 = "(2[0-4]\\d)" + "|(25[0-5])";
String regex1 = "1\\d{2}";
String regex2 = "[1-9]\\d";
String regex3 = "\\d";
String regex = "(" + regex0 + ")|(" + regex1 + ")|(" + regex2 + ")|(" + regex3 + ")";
regex = "(" + regex + ").(" + regex + ").(" + regex + ").(" + regex + ")";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(ip);
return m.matches();
}
/**
* 获取本地ip 适合windows与linux
*
* @return
*/
public static String getLocalIP() {
String localIP = "127.0.0.1";
try {
Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
InetAddress ip = ni.getInetAddresses().nextElement();
if (!ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {
localIP = ip.getHostAddress();
break;
}
}
} catch (Exception e) {
try {
localIP = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
}
return localIP;
}
/**
* 获取客户机的ip地址
* @param request
* @return
*/
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("http_client_ip");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
// 如果是多级代理,那么取第一个ip为客户ip
if (ip != null && ip.indexOf(",") != -1) {
ip = ip.substring(ip.lastIndexOf(",") + 1, ip.length()).trim();
}
return ip;
}
/**
* 把ip转化为整数
* @param ip
* @return
*/
public static long translateIP2Int(String ip){
String[] intArr = ip.split("\\.");
int[] ipInt = new int[intArr.length];
for (int i = 0; i <intArr.length ; i++) {
ipInt[i] = new Integer(intArr[i]).intValue();
}
return ipInt[0] * 256 * 256 * 256 + + ipInt[1] * 256 * 256 + ipInt[2] * 256 + ipInt[3];
}
public static void main(String[] args) {
System.out.println(getLocalIP());
}
}
6、结果返回实体类
public class ResponseResult implements Serializable {
private static final long serialVersionUID = 6054052582421291408L;
private String message;
private Object data;
private int code;
private boolean success;
private Long total;
public ResponseResult(){}
public ResponseResult(boolean success, Object data) {
this.success = success;
this.data = data;
}
public ResponseResult(boolean success, String message, Object data) {
this.success = success;
this.message = message;
this.data = data;
}
public String getMessage() {
return message;
}
public ResponseResult setMessage(String message) {
this.message = message;
return this;
}
public Object getData() {
return data;
}
public ResponseResult setData(Object data) {
this.data = data;
return this;
}
public boolean getSuccess() {
return success;
}
public ResponseResult setSuccess(boolean success) {
this.success = success;
return this;
}
public int getCode() {
return code;
}
public ResponseResult setCode(int code) {
this.code = code;
return this;
}
public Long getTotal() {
return success;
}
public ResponseResult setTotal(Long total) {
this.total = total;
return this;
}
}
7、日志表结构设计
字段名 | 注释 | 类型 | 长度 | 是否必填 | 是否主键 |
id | 自增ID | int | 11 | 是 | 是 |
client_ip | 客户端ip | varchar | 100 | 否 | 否 |
req_uri | 请求映射路径 | varchar | 100 | 否 | 否 |
method | 方法名 | varchar | 200 | 否 | 否 |
param | 参数 | text | 0 | 否 | 否 |
operator | 操作人姓名 | varchar | 100 | 否 | 否 |
start_time | 接口请求时间 | varchar | 50 | 否 | 否 |
ent_time | 接口返回时间 | varchar | 50 | 否 | 否 |
total_time | 总消耗时间 | varchar | 50 | 否 | 否 |
return_data | 接口返回数据 | text | 0 | 否 | 否 |
gmt_create | 创建时间 | datatime | 50 | 是 | 否 |
gmt_create_user | 创建人 | int | 11 | 是 | 否 |
gmt_modified | 修改时间 | datatime | 0 | 是 | 否 |
gmt_modified_user | 修改人 | int | 11 | 是 | 否 |
is_delete | 是否删除(0:否 1:是) | tinyint | 2 | 是 | 否 |
全篇文章完全纯手打,如果觉得对您有帮助,记得加关注给好评哟~~