[Spring] AOP기반 SQL Logging
안녕하세요. yeTi입니다.
오늘은 Spring에서 AOP를 활용하여 SQL Log를 확인해보려고 합니다.
설치 환경
- Spring Boot : 1.5.4.RELEASE
- Mybatis : 1.3.2
1. Aspect에 다음과 같이 구현합니다.
- @Around("execution(* org.apache.ibatis.session.SqlSession.*(String, ..))")
public Object logBefore(ProceedingJoinPoint pjp) throws Throwable {
Object[] methodArgs = pjp.getArgs(), sqlArgs = null;
String statement = null;
// SQL ID
String sqlId = methodArgs[0].toString();
// find the SQL arguments (parameters)
for (int i = 1, n = methodArgs.length; i < n; i++) {
Object arg = methodArgs[i];
if (arg instanceof HashMap) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) arg;
// ? 가 포함된 SQL문
statement = ((SqlSessionTemplate)pjp.getTarget()).getConfiguration().getMappedStatement(sqlId)
.getBoundSql(map).getSql();
sqlArgs = new Object[map.size()];
Iterator<String> itr = map.keySet().iterator();
int j = 0;
while(itr.hasNext()) {
sqlArgs[j++] = map.get(itr.next());
}
}
break;
}
// '?' 대신 파라미터로 대체
String completedStatement = (sqlArgs == null ?
statement : fillParameters(statement, sqlArgs));
Object result = pjp.proceed();
log.info(completedStatement);
return result;
}
private String fillParameters(String statement, Object[] sqlArgs) {
// initialize a StringBuilder with a guesstimated final length
StringBuilder completedSqlBuilder = new StringBuilder(Math.round(statement.length() * 1.2f));
int index, // will hold the index of the next ?
prevIndex = 0; // will hold the index of the previous ? + 1
// loop through each SQL argument
for (Object arg : sqlArgs) {
index = statement.indexOf("?", prevIndex);
if (index == -1)
break; // bail out if there's a mismatch in # of args vs. ?'s
// append the chunk of SQL coming before this ?
completedSqlBuilder.append(statement.substring(prevIndex, index));
// append the replacement for the ?
if (arg == null)
completedSqlBuilder.append("NULL");
else
completedSqlBuilder.append(":"+arg.toString());
prevIndex = index + 1;
}
// add the rest of the SQL if any
if (prevIndex != statement.length())
completedSqlBuilder.append(statement.substring(prevIndex));
return completedSqlBuilder.toString();
}
2. Spring의 CGLIB 프록시를 사용하도록 설정합니다.
- application.yml에 다음을 추가합니다.
- spring.aop.proxy-target-class: true