spring

day2 class05 어노테이션 기반 AOP

일상다반ㅅㅏ 2019. 1. 6. 21:00

1. 어노테이션 기반 AOP 설정

- <aop:aspectj-autoproxy/> 엘리먼트만 선언하면 스프링 컨테이너는 AOP 관련 어노테이션들을 인식하고 용도에 맞게 처리해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- applicationContext.xml -->
 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
 
    <context:component-scan base-package="com.springbook.biz"></context:component-scan>
    
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
cs


2. Before 어드바이스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
@Aspect // Aspect = Pointcut + Advice
public class BeforeAdvice {
 
    // Pointcut
    @Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    public void allPointcut() {}
    
    // Advice
    @Before("allPointcut()")
    public void beforeLog(JoinPoint jp) {
        String method = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        
        System.out.println("[사전 처리] " + method + "() 메소드 ARGS 정보 : " + args[0].toString());
    }
}
cs


3. After Returning 어드바이스

- 메소드 수행 결과를 받아내기 위해 포인트컷을 참조하는 속성과 바인드 변수를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
@Aspect
public class AfterReturningAdvice {
 
    @Pointcut("execution(* com.springbook.biz..*Impl.get*(..))")
    public void getPointcut() {}
    
    @AfterReturning(pointcut="getPointcut()", returning="returnObj")
    public void afterLog(JoinPoint jp, Object returnObj) {
        String method = jp.getSignature().getName();
        if(returnObj instanceof UserVO) {
            UserVO user = (UserVO) returnObj;
            if(user.getRole().equals("Admin")) {
                System.out.println(user.getName() + " 로그인(Admin)");
            }
        }
        
        System.out.println("[사후 처리] " + method + "() 메소드 리턴값 : " + returnObj.toString());
    }
}
cs


4. After Trhowing 어드바이스

- 비즈니스 메소드에서 발생된 예외 객체를 받아낼 수 있도록 포인트컷을 참조하는 속성과 바인드 변수를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
@Aspect
public class AfterThrowingAdvice {
 
    @Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    public void allPointcut() {}
    
    @AfterThrowing(pointcut="allPointcut()", throwing="exceptObj")
    public void exceptionLog(JoinPoint jp, Exception exceptObj) {
        String method = jp.getSignature().getName();
        System.out.println(method + "() 비즈니스 로직 수행 중 예외 발생!");
        
        if(exceptObj instanceof IllegalArgumentException) {
            System.out.println("부적합한 값이 입력되었습니다.");
        } else if(exceptObj instanceof NumberFormatException) {
            System.out.println("숫자 형식의 값이 아닙니다.");
        } else if(exceptObj instanceof Exception) {
            System.out.println("문제가 발생했습니다.");
        } 
    }
}

cs



5. After 어드바이스

1
2
3
4
5
6
7
8
9
10
11
12
@Service
@Aspect
public class AfterAdvice {
 
    /*@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    public void allPointcut() {}*/
    
    @After("allPointcut()")
    public void finallyLog() {
        System.out.println("[사후 처리] 비즈니스 로직 수행 후 무조건 동작");
    }
}
cs


6. Around 어드바이스

- Around는 proceed를 사용하기 위해 ProceedingJoinPoint를 사용한다는 것을 다시 한번 강조한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Service
@Aspect
public class AroundAdvice {
 
    /*@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    public void allPointcut() {}*/
    
    @Around("allPointcut()")
    public Object aroundLog(ProceedingJoinPoint pjpthrows Throwable {
        String method = pjp.getSignature().getName();
        
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        Object returnObj = pjp.proceed();
        
        stopWatch.stop();
        System.out.println(method + "() 메소드 수행에 걸린 시간 : " + stopWatch.getTotalTimeMillis() + "(ms)초");
        
        return returnObj;
    }
}
cs


7. 외부 Pointcut 참조하기

- xml 설정으로 포인트컷을 관리했을 때는 포인트컷을 여러개 등록하여 재사용할 수 있었다.

- 어노테이션 설정을 하면 클래스 마다 반복 선언이 되었기 때문에 이를 해결하기 위해 공통 클래스로 등록한다.

- 어드바이스 클래스에서는 포인트컷 함수를 삭제하고 포인트컷 함수의 경로를 지정해준다.

1
2
3
4
5
6
7
8
9
@Aspect
public class PointcutCommon {
 
    @Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    public void allPointcut() {}
    
    @Pointcut("execution(* com.springbook.biz..*Impl.get*(..))")
    public void getPointcut() {}
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
@Aspect
public class BeforeAdvice {
 
    /*@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    public void allPointcut() {}*/
    
    @Before("PointcutCommon.allPointcut()")
    public void beforeLog(JoinPoint jp) {
        String method = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        
        System.out.println("[사전 처리] " + method + "() 메소드 ARGS 정보 : " + args[0].toString());
    }
}
cs