Maven 의존성 설정
<!-- 스프링 AOP 핵심 라이브러리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
<!-- AspectJ 어노테이션 방식 지원 라이브러리 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
주의사항: 버전은 사용 중인 JDK와 반드시 호환되어야 합니다. 호환되지 않으면 error at ::0 can't find referenced pointcut pointCutName 오류가 발생할 수 있습니다.
Spring MVC XML 설정
<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:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- AspectJ 자동 프록시 활성화 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
필수 설정: <aop:aspectj-autoproxy> 태그는 반드시 포함되어야 합니다.
AOP 어노테이션 기반 Aspect 클래스
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAopAction {
/**
* execution: 메서드 실행 조인 포인트 매칭
* - 예: @Pointcut("execution(* com.example.service..*.*(..))")
* 첫 번째 *: 모든 반환 타입, ..: 0개 이상, service..: 서비스 패키지 및 하위 패키지
* - @Pointcut("within(com.example.service.*)")
* within: 특정 패키지 내 조인 포인트 제한
* - @Pointcut("this(com.example.service.UserService)")
* this: AOP 프록시가 특정 타입의 인스턴스인 경우
* - @Pointcut("bean(userService)")
* bean: IoC 컨테이너 내 특정 빈 이름 지정
* - @within(org.springframework.stereotype.Service): @Service 어노테이션이 있는 클래스의 모든 메서드
* - @annotation(org.springframework.web.bind.annotation.RequestMapping): @RequestMapping 어노테이션이 있는 메서드
*/
@Pointcut("execution(* com.cn.xxx..*.*(..))") // 지정된 패키지의 모든 메서드 매칭
private void pointCut() {}
/**
* 메서드 실행 전
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {
Class<?> clazz = joinPoint.getTarget().getClass();
String className = clazz.getSimpleName();
String methodName = joinPoint.getSignature().getName();
Logger logger = Logger.getLogger(clazz);
Object[] params = joinPoint.getArgs();
StringBuilder logMessage = new StringBuilder(
"메서드 시작: " + clazz.getPackage().getName() + "." + className + "." + methodName + ", 인자: ");
for (Object param : params) {
logMessage.append(param).append(", ");
}
logger.info(logMessage.toString());
}
/**
* 메서드 정상 종료 후
*/
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void doAfter(JoinPoint joinPoint, Object result) {
Class<?> clazz = joinPoint.getTarget().getClass();
String className = clazz.getSimpleName();
String methodName = joinPoint.getSignature().getName();
Logger logger = Logger.getLogger(clazz);
logger.info("메서드 종료: " + className + "." + methodName + ", 결과: " + result);
}
/**
* 메서드 예외 발생 시
*/
@AfterThrowing(pointcut = "pointCut()", throwing = "ex")
public void doAfterThrow(JoinPoint joinPoint, Throwable ex) {
Class<?> clazz = joinPoint.getTarget().getClass();
String className = clazz.getSimpleName();
String methodName = joinPoint.getSignature().getName();
Logger logger = Logger.getLogger(clazz);
logger.error("메서드 예외: " + className + "." + methodName + ", 예외 메시지: ", ex);
}
/**
* 메서드 전후 처리 (Around)
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
return pjp.proceed();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
주의사항: 어노테이션 내 returning과 throwing 속성 값은 메서드 파라미터 이름과 반드시 일치해야 합니다. 일치하지 않으면 IllegalArgumentException: error at ::0 formal unbound in pointcut 오류가 발생합니다. 예를 들어, 위 코드에서 returning = "result"는 파라미터 Object result와, throwing = "ex"는 파라미터 Throwable ex와 일치해야 합니다.