개인적인 사정으로 글이 많이 늦었습니다. 이전 글을 올린 지 한달 가까이가 지났네요. ;;
이번에는 AOP 글은 실제 AOP 사용 및 결과에 대한 예제입니다.
AOP 표현법만 참고 하시려면, 해당 AOP Pointcut 표현법 링크를 클릭해주세요.
SpringBoot 에서 AOP 를 활용하려면 다음과 같은 조건이 필요합니다.
아래는 build.gradle 파일에 선언된 AOP 관련 lib 입니다.
(버전정보는 본인의 SpringBoot 버전에 맞추어 사용하면 됩니다. )
implementation 'org.springframework.boot:spring-boot-starter-api:2.4.5' |
그 다음은 @SpringBootApplication 이 선언된 메소드에 AOP 를 사용한다고 선언해야 합니다.
내부적으로 많은 원리가 있을거라고 생각하지만.. ㅋ 깊게 공부하지 않았으니 SKIP..
일단 AOP 사용을 위해 @EnableAspectJAutoProxy 를 선언해줍니다.
( 아래 샘플의 CONF_FILE= "...." 은 그냥 SpringBoot 수행 시 설정파일이라 무시하셔도 무관합니다. )
@EnableAspectJAutoProxy @SpringBootApplication public class SpringBootApplication { private static final String CONF_FILE = "spring.config.location=classpath:application.yml" public static void main( String[] args ) { new SpringApplicationBuilder( SpringBootApplication.class ) .properties( CONF_FILE ) .run ( args ); } } |
서두가 길었습니다. 이제 실제 AOP 사용 예제와 그 결과를 확인해보겠습니다.
샘플은 Before , Around, After 순으로 해보겠습니다.
( Before, Around .... 부분이 궁금하시면 앞서 설명한 AOP 는 무엇인가? 페이지를 참고 해주세요.
AOP 호출 테스트를 위한 Control 함수 입니다.
@RestController public class AopTestPartCTRL { // slf4j Logger 사용 private static final Logger loclog = LoggerFactory.getLogger( AopTestPartCTRL.class ); @RequestMapping( value = "aopTestPart1" ) public ResponseEntity aopTestCtrl_1( HttpServletRequest request, @RequestParam(required=false) String testParam ) { // 단순히 controller 메소드명을 로깅 loclog.info( "CTRL Method ---> {}", Thread.currentThread().getStackTrace()[1].getMethodName() ); // URL ( ~~/aopTestPart1 ) 호출 시 200 성공 및 'AOP-TEST_PART_1' 이 리턴 return new ResponseEntity( "AOP-TEST_PART_1", HttpStatus.OK ); } } |
[ @Before Aop ]
@Component @Aspect public class TestAspectOrder_1 { // slf4j Logger 사용 private static final Logger loclog = LoggerFactory.getLogger( TestAspectOrder_1.class ); @Before( "execution(* com.foxy.wany..*Test.Wany*(..) )" ) public void beforeAopTest_1( JoinPoint jp ) { Object args[] = jp.getArgs(); // beforeAopTest_1 호출 대상 메소드 loclog.info( "Before ---> Target Method : {}", jp.getSignature().getName() ); // beforeAopTest_1 호출 대상 메소드 인자 갯수 loclog.info( "Before ---> Target Method Param len : {}", args.length ); } } |
[ RESULT ]
Before ---> Target Method : aopTestCtrl_1 Before ---> Target Method param len : 2 CTRL Method ---> aopTestCtrl_1 |
위 결과에서 보듯이 Before AOP 가 먼저 호출 후에 controller 메소드 aopTestPart1 가 호출 되었습니다.
[ @After , @AfterReturning Aop ]
@Component @Aspect public class TestAspectOrder_1 { // slf4j Logger 사용 private static final Logger loclog = LoggerFactory.getLogger( TestAspectOrder_1.class ); @After( "execution(* com.foxy.wany..*Test.Wany*(..) )" ) public void afterAopTest_1( JoinPoint jp ) { Object args[] = jp.getArgs(); // afterAopTest_1 호출 대상 메소드 loclog.info( "After---> Target Method : {}", jp.getSignature().getName() ); // afterAopTest_1 호출 대상 메소드 인자 갯수 loclog.info( "After ---> Target Method Param len : {}", args.length ); } @AfterReturning( value="execution(* com.foxy.wany..*Test.Wany*(..) )" , returning="returnData" ) public void afterAopTest_1( JoinPoint jp , ResponseEntity returnData ) { Object args[] = jp.getArgs(); // afterAopTest_1 호출 대상 메소드 loclog.info( "AfterReturn---> Target Method : {}", jp.getSignature().getName() ); // afterAopTest_1 호출 대상 메소드 인자 갯수 loclog.info( "AfterReturn ---> Target Method Param len : {}", args.length ); // afterAopTest_1 호출 대상 메소드의 리턴 데이터 loclog.info( "AfterReturn returning ---> {}", returnData ); } } |
[ RESULT ]
CTRL Method ---> aopTestCtrl_1 AfterReturn ---> Target Method : aopTestCtrl_1 AfterReturn ---> Target Method param len : 2 AfterReturn returning ---> <200 OK OK, AOP-TEST_PART_1, []> After ---> Target Method : aopTestCtrl_1 After ---> Target Method param len : 2 |
위와 같은 결과가 나옵니다.
@After 특성상 대상 메소드 호출 완료 후 AOP 가 호출되는 것을 확인할 수 있으며, @After 과 @AfterReturning 의 차이
점은 @AfterReturning 은 대상 메소드의 리턴값을 받을 수 있는 것입니다.
단 주의할 이 2가지 정도 있습니다.
1. @AfterReturning 내 returning 에 선언된 'returnData' 명칭이 afterAopTest_1 메소드 내 parameter 명이
'returnData'과 동일 해야 합니다.
2. 대상 메소드 리턴 타입과 afterAopTest_1 메소드 내 parameter 타입이 동일해야 합니다.
만약 위 1,2 가 수락되지 않을 경우, compile 오류 또는 @AfterReturning 이 호출 되지 않습니다.
[ @Around ]
@Component @Aspect public class TestAspectOrder_1 { // slf4j Logger 사용 private static final Logger loclog = LoggerFactory.getLogger( TestAspectOrder_1.class ); @Around( "execution(* com.foxy.wany..*Test.Wany*(..) )" ) public Object aroundAopTest_1( ProceedingJoinPoint pjp ) { Object args[] = jp.getArgs(); // afterAopTest_1 호출 전 loclog.info( "Around before ---> Target Method : {}", pjp.getSignature().getName() ); // afterAopTest_1 호출 전 loclog.info( "Around before ---> Target Method Param len : {}", args.length ); // afterAopTest_1 수행 Object rtnObj = pjp.proceed(); // afterAopTest_1 호출 후 리턴값 loclog.info( "Around after ---> rtnObj : {}", rtnObj ); return rtnObj; } } |
[ RESULT ]
Around before ---> Target Method : aopTestCtrl_1 Around before ---> Target Method param len : 2 CTRL Method ---> aopTestCtrl_1 Around after ---> <200 OK OK, AOP-TEST_PART_1, []> |
위와 같은 결과가 나옵니다.
@Around 의 경우는 pjp.proceed(); 호출 전/후 로 나눌 수 있습니다.
pjp.proceed(); 호출 전에 @Around 내 기능을 수행하고, proceed(); 호출 시점에서 대상 메소드 로직이 수행됩니다.
pjp.proceed(); 호출 후 다시 @Around 내 기능이 수행되는 것입니다.
즉, @Around 에서 proceed() 를 호출 하지 않을 경우는 대상 메소드를 호출하지 않습니다.
때문에 반드시 proceed() 를 선언해 주어야 합니다.
또한 @Around 선언된 메소드는 리턴 타입을 가져야 할 수도 있습니다.
즉, @Around 대상 메소드가 리턴 타입이 존재한다면, @Around 내 에서도 반드시 리턴 타입을 가져야 합니다.
Object rtnObj = pjp.proceed(); . . . . 중략 . . . . return rtnObj; |
위 처럼 return rtnObj; 을 선언해 주어야, @Around 대상 메소드의 리턴값이 정상적으로 반환됩니다.
만약 return 해주지 않는다면, 대상 메소드의 리턴값은 처리될 수 없습니다.
'Framework > SpringBoot' 카테고리의 다른 글
[ 2 ] AOP Pointcut 표현방법 (0) | 2021.04.30 |
---|---|
[ 1 ] AOP 는 무엇인가? (0) | 2021.04.22 |