https://hwanii96.tistory.com/334
위의 글에서, @ 어노테이션 방식으로,
Xxx 포인트컷과 로그 설정 어드바이스를 어스펙트 하는 설정을 했었다.
만약, 포인트컷과 어드바이스를 어스펙트 했는데, 위빙이 되지 않는다 ?
>> 오타일 가능성이 매우 높다.
>> 필요한 객체들의 객체화가 안됬을 수도 있다.
1.
package com.spring.biz.common;
import org.aspectj.lang.annotation.Pointcut;
public class PointcutCommon { // PointCut 통합 클래스. (@ 어노테이션을 사용)
@Pointcut("execution(* com.spring.biz..*Impl.*(..))")
public void aPointcut() {}
@Pointcut("execution(* com.spring.biz..*Impl.select*(..))")
public void bPointcut() {}
@Pointcut("execution(* com.spring.biz..*Impl.*(..))")
public void cPointcut() {}
@Pointcut("execution(* com.spring.biz..*Impl.*(..))")
public void dPointcut() {}
@Pointcut("execution(* com.spring.biz..*Impl.insert*(..))")
public void ePointcut() {}
} // PointCutCommon
위는, XxxPointcut 에 대해 모듈화 해놓은 PointCutCommon 클래스 이다.
포인트컷을 어떻게 설정할것인지를 생각해서,
@Pointcut() 어노테이션의 소괄호 내부를 작성해주면 된다.
모든 Pointcut을 이 클래스에서 관리하므로,
유지보수가 용이하고, 가독성이 올라간다.
2.
package com.spring.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.spring.biz.member.MemberVO;
@Service
@Aspect
public class LogAdvice { // LogAdvice 통합 클래스. (@ 어노테이션을 사용)
@Before("PointcutCommon.aPointcut()")
public void beforeLog() {
System.out.println("log : LogAdvice : beforeLog() : ");
System.out.println("모든 비즈니스 메서드를 대상으로, 비즈니스 메서드 '수행 전'에 로그 호출.");
System.out.println();
}
@After("PointcutCommon.bPointcut()")
public void afterLog() {
System.out.println("log : LogAdvice : afterLog() : ");
System.out.println("R 비즈니스 메서드를 대상으로, 비즈니스 메서드 '수행 후'에 로그 호출.");
System.out.println();
}
@AfterReturning(pointcut = "PointcutCommon.cPointcut()", returning = "returnObj")
public void afterReturningLog(JoinPoint jp, Object returnObj) { // CRUD 메서드가 결과값을 반환 후에 호출 되는 어드바이스 (횡단 관심).
String methodName = jp.getSignature().getName();
String getSignature = jp.getSignature().toString(); // MemberVO com.spring.biz.member.MemberService.selectOne(MemberVO)
int index = getSignature.indexOf("biz.") + 4; // "biz." 다음 인덱스
if(index != -1) {
String log = getSignature.substring(index);
System.out.println("log : " + log);
System.out.println();
}
System.out.println("log : LogAdvice : afterReturningLog() : ");
System.out.println("methodName : " + methodName + " 의 반환 이후의 로그 호출.");
System.out.println();
if(returnObj instanceof MemberVO) {
MemberVO mVO = (MemberVO)returnObj;
if(mVO.getRole().equals("ADMIN")) {
System.out.println("[관리자 입장]");
System.out.println();
}
else {
System.out.println("[사용자 입장]");
System.out.println();
}
}
}
@AfterThrowing(pointcut = "PointcutCommon.dPointcut()", throwing = "exceptObj")
public void afterThrowingLog(JoinPoint jp, Exception exceptObj) { // 예외 발생 시 뜨는 로그.
String methodName = jp.getSignature().getName();
System.out.println("log : LogAdvice : afterThrowingLog() : ");
System.out.println("methodName : " + methodName + " 의 반환 이후의 로그 호출.");
System.out.println("exceptObj.getMessage() : " + exceptObj.getMessage());
}
@Around("PointcutCommon.ePointcut()")
public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
System.out.println("log : LogAdvice : aroundLog() : ");
System.out.println("C 비즈니스 메서드를 대상으로, 비즈니스 메서드 '수행 전' 에 로그 호출.");
System.out.println();
StopWatch sw = new StopWatch(); // 스프링에서 제공 해주는 클래스 이다.
sw.start();
// 외부의 비즈니스 메서드를 호출 한다.
Object obj = pjp.proceed();
sw.stop();
System.out.println("log : LogAdvice : aroundLog() : ");
System.out.println("methodName : " + methodName + " 의 반환 이후의 로그 호출.");
System.out.println(sw.getTotalTimeMillis() + " 밀리초의 시간이 걸렸습니다.");
System.out.println(sw.getTotalTimeSeconds() + " 초의 시간이 걸렸습니다.");
System.out.println();
return obj;
}
} // LogAdvice
모든, 로그 설정에 관련된 로그 어드바이스들을 한곳에 모듈화한 LogAdvice 클래스 이다.
위와 같이,
LogAdvice 라는 클래스 안에 모든 어드바이스 메서드들이 있기 때문에,
LogAdvice 객체화에 필요한 @Service 어노테이션은 딱 한번만 명시 해 주면 된다.
@Aspect 어노테이션도 마찬 가지 이다.
만약, 로그 설정 어드바이스를 사용 하고 싶지 않다면 ?
위와 같이 주석 처리를 하면 될듯 하다.
3.
[ 정리 ]
@Aspect 어노테이션은 스프링 AOP 에서 사용되는 어드바이스 클래스를 정의 할 때 사용 된다.
해당 클래스가 AOP의 관점 (aspect) 역할을 수행함을 나타낸다.
@Aspect 어노테이션을 사용하면,
LogAdvice 클래스가,
AOP의 관점 역할을 한다는 것을 명시적으로 나타내고,
스프링 컨테이너가 LogAdvice 클래스를 AOP의 일부로 인식 하게 된다.
@Aspect 어노테이션을 사용한 이유와 장점은 다음과 같다.
1) AOP 관심사 분리
LogAdvice 클래스는 다양한 로그 설정 메서드를 포함 한다.
하지만, 이 로그 설정 메서드 들은 비즈니스 로직과는 별개이다.
(== 별개의 관심사 이다.)
@Aspect 어노테이션을 사용하면, 해당 클래스를 AOP 관점으로 정의 하게 되고,
그렇게 되면,
비즈니스 로직과, 어드바이스 로직을 분리하게 된다.
이는 유지 보수가 용이해짐을 의미 한다.
2) 모듈화 및 재사용
@Aspect 어노테이션을 사용해서, AOP 관점을 정의하면,
이 어드바이스 클래스는 다른곳에서도 재사용이 가능해진다.
그냥 LogAdvice 클래스를 그대로 재사용 하면 된다.
3) 명시성
@Aspect 어노테이션은 스프링 AOP와 함께 사용되는 개념이다.
스프링은 @Aspect 어노테이션을 인식하고,
자동으로 AOP 설정을 생성하고 실행한다.
이는 코드의 가독성을 높이고,
개발자들도 코드를 이해하기 쉽게 되는 장점을 가진다.
4) 스프링 AOP 통합
@Aspect 어노테이션은 스프링 AOP와 함께 사용되므로,
어드바이스 메서드가 적용될 지점과 관련된 부분을
XML 설정이 아닌, 자바에서 정의 할 수 있다는 장점을 가지게 된다.
'Spring 프레임워크 > 이론' 카테고리의 다른 글
Spring 프레임워크 MultipartFile 인터페이스 (0) | 2023.08.17 |
---|---|
Spring JDBC (1) | 2023.08.16 |
AOP (관점 지향 프로그래밍) 관심분리 (@ 어노테이션 설정) (0) | 2023.08.11 |
AOP (관점 지향 프로그래밍) 관심분리 (.xml 파일 설정) (0) | 2023.08.11 |
AOP (Aspect Oriented Programming) (관점 지향 프로그래밍) (0) | 2023.08.10 |