[Spring] AOP

배세훈·2021년 11월 30일
0

Spring

목록 보기
26/38

1. @Aspect

@Aspect
public class LogAop{
	...
}

@Aspect Annotation의 경우 해당 Class가 횡단관심사(부가기능) Class임을 알려주는 Annotation입니다.
@Aspect Annotation이 부여되었다고 해서 자동으로 Bean으로 등록되는것이 아니므로 따로 Bean으로 등록을 해주는 작업이 필요합니다.

2. @Around("Pointcut")

  • 핵심 메서드와 공통 메서드의 실행 시점을 자유롭게 설정할 수 있는 Around 어노테이션은 단독적으로 지시자(메서드)의 매개 변수를 JoinPoint가 아닌 ProceedingJoinPoint를 사용합니다. 해당 API의 .proceed() 메서드는 핵심 메서드를 호출하는 기능이 있습니다.
    따라서 try-catch로 핵심 메서드를 선언해주고 원하는 위치에 공통 메서드를 놓아주면 됩니다.
@Aspect
public class LogAop{
	
    @Around("execution(* com.example.Car.accelerate(..))")
    public Object loggin(ProceedingJoinPoint joinPoint) throws Throwable {
    	String methodName = joinPoint.getSignature().toShortString();
        
        try{
        	System.out.println(methodName + "is Start");
            Object obj = joinPoint.proceed();
            return obj;
        }finally{
        	System.out.println(methodName + "is Finish");
		}
    }
}

@Around Annotation은 핵심관심사의 실패여부와 상관없이 전 후로 실행되도록 하는 Advice입니다. @Around Annotation의 속성값으로 PointCut을 전달해주어야 합니다.

3. @PointCut

@Pointcut은 횡단관심사(부가기능)이 적용될 JoinPoint들을 정의한 것입니다.
@Pointcut Annotation의 경우 조금 복잡한 표현식을 가지고 있습니다.

execution(접근제어자, 반환형 패키지를 포함한 클래스 경로.메소드 파라미터)

@Pointcut("execution(public void get*(())")
퍼블릭형의 반환형이 없는 (public void)형태의 메소드 중 get으로 시작하는 (get*) 모든 메소드중 파라미터가 존재하지 않는 (()) 메소드들에게 적용

@Pointcut("execution(* *(..))")
첫번째 기호는 접근제어자와 반환형 모두를 상관하지 않고 적용.
두번째
기호는 어떤한 경로에 존재하는 클래스도 상관하지 않고 적용
마지막으로 (..)은 메소드의 파라미터가 몇개가 존재하던지 상관없이 실행하는 경우

@Pointcut("execution(* com.java.example.Car.accelerate())")
첫번째 * 기호는 접근제어자, 반환형을 상관하지 않는다는 의미
com.java.example.Car.accelerate()는 해당 Car 클래스의 accelerate() 파라미터가 없는 메소드가 호출될 때 실행하는 경우를 의미

@Pointcut("execution(* com.java..*.*(())")

com.java 패키지 및 하위 패키지의 모든 클래스의 파라미터가 없는 모든 메서드에 적용한다는 의미

예제

@Aspect
public class LogAop{
	
    @Around("execution(* com.java.car..*.*(())")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
    	String methodName = joinPoint.getSignature().toShortString();
        
        try{
        	System.out.println(methodName + "is Start");
            Object obj = joinPoint.proceed();
            return obj;
        }finally{
        	System.out.println(methodName + "is Finish");
        }
    }
}

within(Class의 경로)

패키지의 모든 메소드에 적용할 때 사용합니다.

@Pointcut("within(com.java.example.*)")

  • com.java.example 내의 모든 클래스의 모든 메소드에 적용하겠다는 의미

@Pointcut("within(com.java.example..*)")

  • com.java.example 패키지내 및 하위 패키지를 포함한 모든 클래스의 모든 메소드에 적용하겠다는 의미

@Pointcut("within(com.java.example.Car)")

  • com.java.example.Car 클래스안의 모든 메소드에 적용하겠다는 의미

예제

@Aspect
public class LogAop{
	
    @Around("within(com.java.example.*)")
    public Object loggin(ProceedingJoinPoint joinPoint) throws Throwable{
    	String methodName = joinPoint.getSignature().toShortString();
        
        try{
        	Systme.out.println(methodName + "is Start");
            Object obj = joinPoint.proceed();
            return obj;
        }finally{
        	System.out.println(methodName + "is Finish");
        }
    }
}

Bean(bean id)

  • 해당 bean id를 가지고있는 bean의 모든 메소드에 적용한다는 의미

@Pointcut(bean(car))

car라는 bean id를 가지고 있는 bean에게 적용한다는 의미

예제

@Around("bean(car)")
public Object loggin(ProceedingJoinPoint joinPoint) throws Throwable{
	
    String methodName = joinPoint.getSignature().toShortString();
    
    try{
    	System.out.println(methodName + "is Start");
        Object obj = joinPoint.proceed();
        return obj;
    }finally{
    	System.out.println(methodName + "is Finish");
    }
}

Annotation

사용자가 직접 만든 Annotation에 기반해 Aspect를 적용할 수 있습니다.

Annotation 생성

@Documented
@Target(ElementType.METHOD)
public @interface PerfLogging{}

예제

@Aspect
public class LogAop{
	
    @Around("@annotation(PerfLogging)")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable{
    	
        String methodName = joinPoint.getSignature().toShortString();
        
        try{
        	System.out.println(methodName + "is Start");
            Object obj = joinPoint.proceed();
            return obj;
        }finally{
        	System.out.println(methodName + "is Finish");
        }
    }
}

그 후 어드바이스 어노테이션안에 @annotation("어노테이션 class명")을 입력해주면 됩니다.

pointcut 지정 어노테이션 @Pointcut

  • 해당 어노테이션을 사용하면 핵심 메서드의 경로를 공통 메서드에 일일이 쳐서 넣을 필요가 없습니다. @Pointcut을 선언한 메서드를 담아주기만 하면 됩니다.

ex) @Before() 괄호 내에 "execution(* runSomething())" 핵심 메서드 경로를 @Pointcut에 한 번만 설정해주면 생성하는 공통 메서드 매개변수론 지시자(메서드)명만 써주면 됩니다.

예제

@Aspect
public class MyAspect{
	
    // 중복을 해결해주는 어노테이션 - 포인트컷 지시자를 미리 설정해두는 것
    @Pointcut("execution(* runSomething())")
    private void pointcut() {}
    
    // 핵심 메서드 실행 전 실행
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
    	System.out.println("메서드 before");
    }
    
    // 핵심 메서드 실행 후 실행
    @After("pointcut()")
    public void after(JoinPoint joinPoint){
    	System.out.println("메서드 after");
    }
    
}

@AroundReturning

  • 핵심 메서드가 종료됬을 때 조건없이 공통 메서드를 실행하는 기능이 @After라면 반드시 핵심 메서드 호출이 성공했을 때 반환하는 메서드일 때 @AfterReturning을 사용합니다. 만약 에러(Exception)이 발생했다면 실행되지 않습니다. 따라서 실행시점은 핵심 메서드가 호출된 후 입니다.

@AfterThrowing

  • 핵심 메서드가 호출되어 에외가 발생했을 때만 실행되는 공통 메서드
profile
성장형 인간

0개의 댓글