接口日志表

id int
request_url varchar 请求url
method varchar 请求方式
request_time varchar 接口响应时间
params text 请求参数
ip varchar 客户端IP
operation_desc varchar 操作描述
create_date datetime 创建时间

环绕通知在实际开发中应用非常广泛,所以下面用环绕通知去实现。

创建注解
/anotation/Log.java

// 这个注解用来对controller方法进行描述
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
	// 来一个属性,描述这个方法是用来干嘛的
	String desc();
}

在接口上加注解
controller/StudentController.java

@GetMapping
@Log(desc = "这是学生列表接口")
public List<Student> list() {
	...
}

切面类
/aop/LogAspect.java

// @Pointcut用来标识切点
// RestfulStudentController.*(),指RestfulStudentController里面的所有方法
// @Pointcut("execution(* com.ca.controller.StudentController.*())") 
@Pointcut("execution(* com.ca.controller.StudentController.*(..))") // 所有方法的所有参数.*(..)
public void pointcut() {
		
}

// 添加常量
private static final String title="SpringMvc action report -----";

// 用ThreadLocal来获取时间,new一个ThreadLocal后在里面初始化SimpleDateFormat对象
/*
ThreadLocal操作值的时候是取得当前线程的ThreadLocalMap对象,然后把值设置到了这个对象中,这样对于不同的线程得到的就是不同
的ThreadLocalMap,那么向其中保存值或者修改值都只是会影响到当前线程,这样就保证了线程安全。
*/
private static final ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal() {
	@Override
	protected Object initialValue() {
		return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	}
};


@Around("pointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) {
	
	// 拼接标题(考虑到线程安全,可以用StringBuffer)
	StringBuffer sb = new StringBuffer(title);

	// 然后在标题后需要拼接当前时间
	// 如果使用SimpleDateFormat的话,他是非线程安全类,所以在这里需要去使用ThreadLocal。
	sb.append(sdf.get().format(new Date())).append("\n");

	String url = RequestUtils.getRequestUrl();// 通过工具类获取当前请求的url路径
	
	// 通过request获取当前的请求方式
	HttpServletRequest request = RequestUtils.getRequest();
	String method = request.getMethod();

	sb.append("Url:		").append(method).append("	").append(url).append("\n");

	// 获取controller
	Class<?> controllerClass = proceedingJoinPoint.getTarget().getClass();// 获取到controller类

	sb.append("Controller:		").append(controllerClass).append("\n");

	// 获取方法
	Object[] args = proceedingJoinPoint.getArgs();
	MethodSignature methodSignature = (MethodSignature)proceedingJoinPoint.getSignature();
	Method targetMethod = methodSignature.getMethod();
	String methodName = targetMethod.getName();

	sb.append("Method:		").append(methodName).append("\n");

	sb.append("Params:		").append(JSONUtil.toJsonStr(args)).append("\n");

	// 获取接口描述
	// 要先获取注解
	Log log = targetMethod.getAnnotation(Log.class);
	if (log != null) {
		// 如果注解不为空,获取注解上面的描述
		Striing desc = log.desc();

		sb.append("接口描述:		").append(desc).append("\n");
	}
	
	Object result;
	try {

		// 这个接口的执行时间
		long startTime = System.currentTimeMillis();// 获取当前时间
		
		result = proceedingJoinPoint.proceed();// 执行目标方法

		// 接口的执行时间(当前时间 - startTime),由于是毫秒值,所以加上" ms"
		long executionTime = System.currentTimeMillis() - startTime;

		sb.append("接口响应时间:		").append(executionTime).append(" ms").append("\n");

		// 把上面的信息保存到数据库
		// 由于保存日志不会影响到系统的正常使用,为了系统效率更高,建议这里new线程后使用异步保存。
		// 这样做的话,可以提升接口的响应时间。
		// TODO:new Thread()...
		
		// 打印
		logger.info(sb.toString());
	}catch(Throwable e) {
		// 还可以捕获异常
		logger.error(e.getMessage(), e);
		throw e;
	}
	
	// 执行目标方法后执行后置通知
	System.out.println("执行后置通知");

	return result;
}

工具类
RequestUtils.java

public class RequestUtil {
	// 获取当前线程的request对象
	public static HttpServletRequest getRequest() {
		try {
			return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
		}catch(Exception e) {
			return null;
		}
	}

	public static String getRequestUrl() {
		return getRequestUrl(getRequest());
	}

	// 获取接口请求路径(获取当前的接口地址)
	public static String getRequestUrl(HttpServletRequest request) {
		String contentPath = request.getServletContext().getContextPath();
		int contentPathLength = contentPath.length();
		String target = request.getRequestURI();
		if(contentPathLength != 0) {
			target = target.substring(contentPathLength);
		}
		return target;
	}
}

启动服务后,访问列表接口,在控制台就能看到刚才拼接的信息。

Url:		GET	/student
Controller:	class com.ca.controller.StudentController
Method:		list
Params:		[]【只有在浏览器传递参数后才会显示,跟方法里面的参数无关】
接口描述:	这是学生列表接口
接口响应时间:	125 ms

以上内容源于网络。


版权声明:本文为PurineKing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/PurineKing/article/details/128769514