■ PointcutCommon.java
package com.example.biz.common;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class PointcutCommon { //그냥 설정이어서 new 해주지 않아도 된다
//결합도 낮추고 응집도를 높임
//>>코드 반복 줄이기
//>>관련 있는 코드들끼리 묶어놓기
//좋은 설계
@Pointcut("execution(* com.example.biz..*Impl.get*(..))") //get으로 시작하는 메서드만 적용(getall,getone)
// 반환값 확인 (After-Returning), 예외 처리 (After-Throwing), 실행시간 측정 (Around) 등에 주로 사용
public void aPointcut() {}
@Pointcut("execution(* com.example.biz..*Impl.*(..))")//Impl로 끝나는 클래스의 모든 메서드에 적용
public void bPointcut() {}
//실행 전,후 로깅 (Before, After) 등에 주로 사용
}
■ applicationContext.xml 은 이제 엄청 가벼워짐
■ LogAdvice.java
package com.example.biz.common;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import com.example.biz.board.BoardVO;
import com.example.biz.member.MemberVO;
@Service // 스프링 빈으로 인식
@Aspect // AOP 기능을 담고 있는 클래스임을 명시
public class LogAdvice {
// ===== BEFORE =====
@Before("PointcutCommon.bPointcut()") // 모든 메서드에 대해 실행 전 수행
public void before(JoinPoint jp) {
System.out.println("BEFORE 공통 로그");
// 메서드 이름 출력
String methodName = jp.getSignature().getName();
System.out.println("\\t메서드 시그니쳐 출력 [" + methodName + "]");
}
@After("PointcutCommon.bPointcut()") // 모든 메서드에 대해 실행 후 수행
public void after(JoinPoint jp) {
System.out.println("AFTER 공통 로그");
Object[] args = jp.getArgs(); // 인자 목록 추출
System.out.println("\\t인자 개수 >> " + args.length + "개");
// 첫 번째 인자가 MemberVO일 경우 회원 이름 출력
if (args.length > 0 && args[0] instanceof MemberVO) {
MemberVO vo = (MemberVO) args[0];
String name = vo.getName();
System.out.println("\\t가입한 회원의 이름 [" + name + "]");
}
}
@AfterReturning(pointcut = "PointcutCommon.aPointcut()", returning = "returnObj")
public void returning(JoinPoint jp, Object returnObj) throws Exception {
System.out.println("RETURNING 공통 로그");
System.out.print("\\t");
if (returnObj instanceof BoardVO) {
System.out.println("게시판 관련 서비스");
} else if (returnObj instanceof MemberVO) {
System.out.println("회원 관련 서비스");
MemberVO vo = (MemberVO) returnObj;
if ("ADMIN".equals(vo.getMrole())) {
System.out.println("\\t관리자가 로그인했습니다.");
}
} else {
System.out.println("확인되지않은 서비스");
}
}
@AfterThrowing(pointcut = "PointcutCommon.aPointcut()", throwing = "exceptObj")
public void throwing(JoinPoint jp, Exception exceptObj) {
System.out.println("THROWING 공통 로그");
System.out.println("\\t에러 리포트 [" + exceptObj.getMessage() + "]");
System.out.print("\\t[");
if (exceptObj instanceof IllegalArgumentException) {
System.out.print("일부러 발생시킨 예외");
} else {
System.out.print("확인되지않은 예외");
}
System.out.println("]");
}
@Around("PointcutCommon.aPointcut()") // get* 메서드를 전후로 감싸서 실행 시간 측정
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AROUND 공통 로그 시작");
StopWatch sw = new StopWatch(); // 실행 시간 측정용 객체
sw.start();
Object obj = pjp.proceed(); // 실제 비즈니스 로직 실행
sw.stop();
String methodName = pjp.getSignature().getName();
System.out.println("\\t수행한 서비스 메서드명 [" + methodName + "]");
System.out.println("\\t걸린시간 [" + sw.getTotalTimeMillis() + "]ms");
System.out.println("AROUND 공통 로그 끝");
// getMember 메서드에서 일반회원이면 예외 발생 (테스트 목적)
if ("getMember".equals(methodName) && obj instanceof MemberVO) {
MemberVO vo = (MemberVO) obj;
if ("USER".equals(vo.getMrole())) {
throw new Exception("일반회원 로그인");
}
}
return obj;
}
}
내가 한 코드를 @으로 바꿔보자 - 실습을 위한 코드
package com.example.biz.common;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import com.example.biz.member.MemberVO;
@Component // 스프링 빈으로 등록
@Aspect // AOP 적용 클래스임을 명시
public class Logadvice {
// BEFORE: 메서드 실행 전 동작
@Before("PointcutCommon.bPointcut()")
public void before(JoinPoint jp) {
System.out.println("BEFORE 공통 로그");
String methodName = jp.getSignature().getName();
System.out.println("메서드 시그니쳐 출력 >> " + methodName);
Object[] args = jp.getArgs();
System.out.println("인자 개수 >> " + args.length);
for (Object arg : args) {
System.out.println(arg);
}
}
// AFTER: 메서드 실행 후 동작
@After("PointcutCommon.bPointcut()")
public void after(JoinPoint jp) {
System.out.println("AFTER 공통 로그");
String methodName = jp.getSignature().getName();
System.out.println("메서드 시그니쳐 출력 >> " + methodName);
Object[] args = jp.getArgs();
System.out.println("인자 개수 >> " + args.length);
for (Object arg : args) {
System.out.println(arg);
if (arg instanceof MemberVO) {
String name = ((MemberVO) arg).getName();
System.out.println("가입자 이름 >> " + name);
}
}
}
// AROUND: 메서드 실행 전후 감싸기 (예외 발생 로직 포함 가능)
@Around("PointcutCommon.aPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AROUND 공통 로그 시작");
String methodName = pjp.getSignature().getName();
System.out.println("실행 메서드명 >> " + methodName);
Object[] args = pjp.getArgs();
System.out.println("인자 개수 >> " + args.length);
for (Object arg : args) {
System.out.println(arg);
}
StopWatch sw = new StopWatch();
sw.start();
Object obj = pjp.proceed(); // 실제 메서드 실행
sw.stop();
System.out.println("AROUND 공통 로그 끝 >> " + sw.getTotalTimeMillis() + "ms");
return obj;
}
// AFTER-RETURNING: 메서드 정상 반환 후
@AfterReturning(pointcut = "PointcutCommon.aPointcut()", returning = "returnObj")
public void returning(JoinPoint jp, Object returnObj) throws Exception {
System.out.println("[RETURNING] 공통 로그 실행");
String methodName = jp.getSignature().getName();
System.out.println("실행된 메서드명 >> " + methodName);
if (returnObj instanceof MemberVO) {
MemberVO vo = (MemberVO) returnObj;
if ("ADMIN".equals(vo.getMrole())) {
System.out.println("관리자가 로그인했습니다.");
} else {
System.out.println("일반회원입니다. 예외를 발생시킵니다.");
throw new IllegalArgumentException("일반회원 로그인 시도 예외 발생");
}
} else {
System.out.println("Member가 아닙니다.");
}
}
// AFTER-THROWING: 예외 발생 시
@AfterThrowing(pointcut = "PointcutCommon.aPointcut()", throwing = "exceptObj")
public void throwing(JoinPoint jp, Throwable exceptObj) {
System.out.println("[THROWING] 예외 발생 로그 실행");
String methodName = jp.getSignature().getName();
System.out.println("예외가 발생한 메서드명 >> " + methodName);
if (exceptObj instanceof IllegalArgumentException) {
System.out.println("일부러 발생시킨 IllegalArgumentException 예외");
} else if (exceptObj instanceof RuntimeException) {
System.out.println("일반회원 로그인 예외 // 메시지: " + exceptObj.getMessage());
} else {
System.out.println("확인되지 않은 예외 발생");
}
}
}
logger사용해라
xml 에서 @ 으로 AOP 변경시, advice 끼리의 catch는 지원안됨!(logger로 지원하기때문)