打印请求参数 HttpAspect.java
package com.school.oauth.aop;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;
@Aspect
@Component
public class HttpAspect {
public static ThreadLocal<Map<String,Object>> requsetLogKey = new ThreadLocal();
/**
* 这样写是将重复的代码提取出来方便处理
*/
@Pointcut("execution(* com.school.oauth.controller..*.*(..))")
public void log() {}
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Date d=new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date=simpleDateFormat.format(d);
StringBuffer url=request.getRequestURL();
String method=request.getMethod();
String ip=request.getRemoteAddr();
String class_method=joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
Object[] args=joinPoint.getArgs();
String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();//参数名
//记录请求耗时
//生成一个随机字符串
String key= RandomStringUtils.randomAlphanumeric(10);
//存入ThreadLocal,当controller方法执行完,或者报错的时候,可以查看到是哪个请求
long start = System.currentTimeMillis();
Map map=new HashMap();
map.put("requestKey",key);
map.put("currentTime",start);
map.put("class_method",class_method);
requsetLogKey.set(map);
StringBuffer s=new StringBuffer();
s.append(date+": ");
s.append("url="+url+" ");
s.append("method="+method+" ");
s.append("ip="+ip+" ");
s.append("class_method="+class_method+" ");
List a=new ArrayList<>();
for (int i =0;i<argNames.length;i++){
String n= argNames[i];
String v="";
if(args[i]!=null){
v=(args[i]).toString();
}else{
v="null";
}
a.add(n+"="+v);
}
s.append("args="+ ArrayUtils.toString(a)+" ");
System.out.println("[requestKey::"+key+"]--"+s.toString() );
//System.out.printf(", url = %f\n , method = %f\n , ip = %f\n , class_method = %f\n ,class_method = %f\n ", date, url.toString(),method,ip,class_method, ArrayUtils.toString(args));
}
/*@After("log()")
public void doAfter() {
System.out.println("方法正常完成!");
}*/
@AfterReturning(returning = "obj",pointcut = "log()")
public void doAfterReturning(Object obj) {
String resultJson = JSONObject.toJSONString(obj);
//计算方法请求耗时
Map map=requsetLogKey.get();
requsetLogKey.remove();
String key=(String)map.get("requestKey");
Long startTime=(Long)map.get("currentTime");
long elapseTime = System.currentTimeMillis() - startTime;
String class_method=(String)map.get("class_method");//执行的方法路径
System.out.println("[requestKey::"+key+"]--requestSucceeded:"+resultJson );
System.out.println("[requestKey::"+key+"]--method"+class_method+"--耗时:"+elapseTime);
}
@AfterThrowing(throwing="ex",pointcut="log()")
public void doRecoveryActions(Throwable ex) {
//计算方法请求耗时
Map map=requsetLogKey.get();
requsetLogKey.remove();
String key=(String)map.get("requestKey");
Long startTime=(Long)map.get("currentTime");
long elapseTime = System.currentTimeMillis() - startTime;
String class_method=(String)map.get("class_method");//执行的方法路径
System.out.println("[requestKey::"+key+"]--error"+ex );
System.out.println("[requestKey::"+key+"]--requestErrorMethod"+class_method+"--耗时:"+elapseTime );
}
}
打印请求sql
MyBatisInterceptor.java
package com.school.oauth.common.spring.mybatis;
import com.school.oauth.aop.HttpAspect;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import java.text.DateFormat;
import java.util.*;
import java.util.regex.Matcher;
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,
Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
Object.class, RowBounds.class, ResultHandler.class})})
@SuppressWarnings({"unchecked", "rawtypes"})
public class MyBatisInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
try
{
// 获取xml中的一个select/update/insert/delete节点,是一条SQL语句
MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
Object parameter = null;
// 获取参数,if语句成立,表示sql语句有参数,参数格式是map形式
if (invocation.getArgs().length > 1)
{
parameter = invocation.getArgs()[1];
}
String sqlId = mappedStatement.getId(); // 获取到节点的id,即sql语句的id
Map map= HttpAspect.requsetLogKey.get();
String key=(String)map.get("requestKey");
System.out.println("[requestKey::"+key+"]--sqlId = " + sqlId);
BoundSql boundSql = mappedStatement.getBoundSql(parameter); // BoundSql就是封装myBatis最终产生的sql类
Configuration configuration = mappedStatement.getConfiguration(); // 获取节点的配置
String sql = getSql(configuration, boundSql, sqlId); // 获取到最终的sql语句
System.out.println("[requestKey::"+key+"]--sql = " + sql);
}
catch (Exception e)
{
e.printStackTrace();
}
// 执行完上面的任务后,不改变原有的sql执行过程
return invocation.proceed();
}
// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句
public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId)
{
String sql = showSql(configuration, boundSql);
// StringBuilder str = new StringBuilder(100);
// str.append(sqlId);
// str.append(":");
// str.append(sql);
return sql;
}
// 如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理
private static String getParameterValue(Object obj)
{
String value = null;
if (obj instanceof String)
{
value = "'" + obj.toString() + "'";
}
else if (obj instanceof Date)
{
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(new Date()) + "'";
}
else
{
if (obj != null)
{
value = obj.toString();
}
else
{
value = "";
}
}
return value;
}
// 进行?的替换
public static String showSql(Configuration configuration, BoundSql boundSql)
{
// 获取参数
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// sql语句中多个空格都用一个空格代替
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (CollectionUtils.isNotEmpty(parameterMappings) && parameterObject != null)
{
// 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 如果根据parameterObject.getClass()可以找到对应的类型,则替换
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))
{
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(parameterObject)));
}
else
{
// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping parameterMapping : parameterMappings)
{
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName))
{
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(obj)));
}
else if (boundSql.hasAdditionalParameter(propertyName))
{
// 该分支是动态sql
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?",
Matcher.quoteReplacement(getParameterValue(obj)));
}
else
{
// 打印出缺失,提醒该参数缺失并防止错位
sql = sql.replaceFirst("\\?", "缺失");
}
}
}
}
return sql;
}
@Override
public Object plugin(Object o) { return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
如果遇到使用pagehelper-spring-boot-starter 后,自定义拦截器不生效,不能打印sql后,请看另一篇文章